Unverified Commit bb9c4047 authored by Hong Minhee's avatar Hong Minhee
Browse files

Merge tag '1.2.11' into 1.3-maintenance

Fedify 1.2.11
parents 49389051 d47268b1
Loading
Loading
Loading
Loading
+88 −0
Original line number Diff line number Diff line
@@ -8,6 +8,23 @@ Version 1.3.4

To be released.

 -  Fixed several security vulnerabilities of the `lookupWebFinger()` function.
    [[CVE-2025-23221]]

     -  Fixed a security vulnerability where the `lookupWebFinger()` function
        had followed the infinite number of redirects, which could lead to
        a denial of service attack.  Now it follows up to 5 redirects.

     -  Fixed a security vulnerability where the `lookupWebFinger()` function
        had followed the redirects to other than the HTTP/HTTPS schemes, which
        could lead to a security breach.  Now it follows only the same scheme
        as the original request.

     -  Fixed a security vulnerability where the `lookupWebFinger()` function
        had followed the redirects to the private network addresses, which
        could lead to a SSRF attack.  Now it follows only the public network
        addresses.


Version 1.3.3
-------------
@@ -153,6 +170,29 @@ Released on November 30, 2024.
[#193]: https://github.com/dahlia/fedify/issues/193


Version 1.2.11
--------------

Released on January 21, 2025.

 -  Fixed several security vulnerabilities of the `lookupWebFinger()` function.
    [[CVE-2025-23221]]

     -  Fixed a security vulnerability where the `lookupWebFinger()` function
        had followed the infinite number of redirects, which could lead to
        a denial of service attack.  Now it follows up to 5 redirects.

     -  Fixed a security vulnerability where the `lookupWebFinger()` function
        had followed the redirects to other than the HTTP/HTTPS schemes, which
        could lead to a security breach.  Now it follows only the same scheme
        as the original request.

     -  Fixed a security vulnerability where the `lookupWebFinger()` function
        had followed the redirects to the private network addresses, which
        could lead to a SSRF attack.  Now it follows only the public network
        addresses.


Version 1.2.10
--------------

@@ -377,6 +417,29 @@ Released on October 31, 2024.
[#118]: https://github.com/dahlia/fedify/issues/118


Version 1.1.11
--------------

Released on January 21, 2025.

 -  Fixed several security vulnerabilities of the `lookupWebFinger()` function.
    [[CVE-2025-23221]]

     -  Fixed a security vulnerability where the `lookupWebFinger()` function
        had followed the infinite number of redirects, which could lead to
        a denial of service attack.  Now it follows up to 5 redirects.

     -  Fixed a security vulnerability where the `lookupWebFinger()` function
        had followed the redirects to other than the HTTP/HTTPS schemes, which
        could lead to a security breach.  Now it follows only the same scheme
        as the original request.

     -  Fixed a security vulnerability where the `lookupWebFinger()` function
        had followed the redirects to the private network addresses, which
        could lead to a SSRF attack.  Now it follows only the public network
        addresses.


Version 1.1.10
--------------

@@ -642,6 +705,31 @@ Released on October 20, 2024.
[#150]: https://github.com/dahlia/fedify/issues/150


Version 1.0.14
--------------

Released on January 21, 2025.

 -  Fixed several security vulnerabilities of the `lookupWebFinger()` function.
    [[CVE-2025-23221]]

     -  Fixed a security vulnerability where the `lookupWebFinger()` function
        had followed the infinite number of redirects, which could lead to
        a denial of service attack.  Now it follows up to 5 redirects.

     -  Fixed a security vulnerability where the `lookupWebFinger()` function
        had followed the redirects to other than the HTTP/HTTPS schemes, which
        could lead to a security breach.  Now it follows only the same scheme
        as the original request.

     -  Fixed a security vulnerability where the `lookupWebFinger()` function
        had followed the redirects to the private network addresses, which
        could lead to a SSRF attack.  Now it follows only the public network
        addresses.

[CVE-2025-23221]: https://github.com/dahlia/fedify/security/advisories/GHSA-c59p-wq67-24wx


Version 1.0.13
--------------

+7 −1
Original line number Diff line number Diff line
import type { LookupAddress } from "node:dns";
import { lookup } from "node:dns/promises";
import { isIP } from "node:net";

@@ -38,7 +39,12 @@ export async function validatePublicUrl(url: string): Promise<void> {
  }
  // To prevent SSRF via DNS rebinding, we need to resolve all IP addresses
  // and ensure that they are all public:
  const addresses = await lookup(hostname, { all: true });
  let addresses: LookupAddress[];
  try {
    addresses = await lookup(hostname, { all: true });
  } catch {
    addresses = [];
  }
  for (const { address, family } of addresses) {
    if (
      family === 4 && !isValidPublicIPv4Address(address) ||
+49 −1
Original line number Diff line number Diff line
import { assertEquals } from "@std/assert";
import { assertEquals, assertRejects } from "@std/assert";
import { deadline } from "@std/async/deadline";
import * as mf from "mock_fetch";
import { UrlError } from "../runtime/url.ts";
import { test } from "../testing/mod.ts";
import type { ResourceDescriptor } from "./jrd.ts";
import { lookupWebFinger } from "./lookup.ts";
@@ -91,6 +93,52 @@ test("lookupWebFinger()", async (t) => {
    assertEquals(await lookupWebFinger("acct:johndoe@example.com"), expected);
  });

  mf.mock(
    "GET@/.well-known/webfinger",
    (_) =>
      new Response("", {
        status: 302,
        headers: { Location: "/.well-known/webfinger" },
      }),
  );

  await t.step("infinite redirection", async () => {
    const result = await deadline(
      lookupWebFinger("acct:johndoe@example.com"),
      2000,
    );
    assertEquals(result, null);
  });

  mf.mock(
    "GET@/.well-known/webfinger",
    (_) =>
      new Response("", {
        status: 302,
        headers: { Location: "ftp://example.com/" },
      }),
  );

  await t.step("redirection to different protocol", async () => {
    assertEquals(await lookupWebFinger("acct:johndoe@example.com"), null);
  });

  mf.mock(
    "GET@/.well-known/webfinger",
    (_) =>
      new Response("", {
        status: 302,
        headers: { Location: "https://localhost/" },
      }),
  );

  await t.step("redirection to private address", async () => {
    await assertRejects(
      () => lookupWebFinger("acct:johndoe@example.com"),
      UrlError,
    );
  });

  mf.uninstall();
});

+28 −1
Original line number Diff line number Diff line
@@ -10,10 +10,13 @@ import {
  getUserAgent,
  type GetUserAgentOptions,
} from "../runtime/docloader.ts";
import { validatePublicUrl } from "../runtime/url.ts";
import type { ResourceDescriptor } from "./jrd.ts";

const logger = getLogger(["fedify", "webfinger", "lookup"]);

const MAX_REDIRECTION = 5; // TODO: Make this configurable.

/**
 * Options for {@link lookupWebFinger}.
 * @since 1.3.0
@@ -99,12 +102,14 @@ async function lookupWebFingerInternal(
  }
  let url = new URL(`${protocol}//${server}/.well-known/webfinger`);
  url.searchParams.set("resource", resource.href);
  let redirected = 0;
  while (true) {
    logger.debug(
      "Fetching WebFinger resource descriptor from {url}...",
      { url: url.href },
    );
    let response: Response;
    await validatePublicUrl(url.href);
    try {
      response = await fetch(url, {
        headers: {
@@ -126,10 +131,32 @@ async function lookupWebFingerInternal(
      response.status >= 300 && response.status < 400 &&
      response.headers.has("Location")
    ) {
      url = new URL(
      redirected++;
      if (redirected >= MAX_REDIRECTION) {
        logger.error(
          "Too many redirections ({redirections}) while fetching WebFinger " +
            "resource descriptor.",
          { redirections: redirected },
        );
        return null;
      }
      const redirectedUrl = new URL(
        response.headers.get("Location")!,
        response.url == null || response.url === "" ? url : response.url,
      );
      if (redirectedUrl.protocol !== url.protocol) {
        logger.error(
          "Redirected to a different protocol ({protocol} to " +
            "{redirectedProtocol}) while fetching WebFinger resource " +
            "descriptor.",
          {
            protocol: url.protocol,
            redirectedProtocol: redirectedUrl.protocol,
          },
        );
        return null;
      }
      url = redirectedUrl;
      continue;
    }
    if (!response.ok) {