Unverified Commit 786bfc4f authored by Hong Minhee's avatar Hong Minhee
Browse files

Support for cross-origin WebFinger resources in getActorHandle()

The getActorHandle() function now supports cross-origin WebFinger resources. This allows for retrieving actor handles from different domains. The function has been updated to verify the hostname of WebFinger aliases that do not match the hostname of the actor ID before returning the actor handle. This ensures the security and integrity of the actor handle.
parent 908475fd
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -79,6 +79,9 @@ To be released.
        applicable to `format: "compact"`.  Otherwise, it throws
        a `TypeError`.

 -  The `getActorHandle()` function now supports cross-origin WebFinger
    resources.

 -  The `lookupWebFinger()` and `getActorHandle()` functions no more throw
    an error when they fail to reach the WebFinger resource.

+18 −0
Original line number Diff line number Diff line
@@ -152,6 +152,24 @@ test("getActorHandle()", async (t) => {
    );
  });

  mf.mock(
    "GET@/.well-known/webfinger",
    (_) =>
      new Response(
        JSON.stringify({
          subject: "acct:john@bar.example.com",
          aliases: [
            "https://foo.example.com/@john",
          ],
        }),
        { headers: { "Content-Type": "application/jrd+json" } },
      ),
  );

  await t.step("cross-origin WebFinger resources", async () => {
    assertEquals(await getActorHandle(actor), "@john@bar.example.com");
  });

  mf.mock(
    "GET@/.well-known/webfinger",
    (_) => new Response(null, { status: 404 }),
+18 −1
Original line number Diff line number Diff line
@@ -115,7 +115,12 @@ export async function getActorHandle(
        const match = alias.match(/^acct:([^@]+)@([^@]+)$/);
        if (match != null) {
          const hostname = new URL(`https://${match[2]}/`).hostname;
          if (hostname !== actorId.hostname) continue;
          if (
            hostname !== actorId.hostname &&
            !await verifyCrossOriginActorHandle(actorId.href, alias)
          ) {
            continue;
          }
          return normalizeActorHandle(`@${match[1]}@${match[2]}`, options);
        }
      }
@@ -135,6 +140,18 @@ export async function getActorHandle(
  );
}

async function verifyCrossOriginActorHandle(
  actorId: string,
  alias: string,
): Promise<boolean> {
  const response = await lookupWebFinger(alias);
  if (response == null) return false;
  for (const alias of response.aliases ?? []) {
    if (new URL(alias).href === actorId) return true;
  }
  return false;
}

/**
 * Options for {@link normalizeActorHandle}.
 * @since 0.9.0