Skip to content

Commit

Permalink
Merge tag '1.3.4'
Browse files Browse the repository at this point in the history
Fedify 1.3.4
  • Loading branch information
dahlia committed Jan 20, 2025
2 parents 72b4d6d + 91bd1d7 commit b9f34f4
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 3 deletions.
94 changes: 94 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,29 @@ To be released.
[#195]: https://github.com/dahlia/fedify/issues/195


Version 1.3.4
-------------

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.3.3
-------------

Expand Down Expand Up @@ -192,6 +215,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
--------------

Expand Down Expand Up @@ -416,6 +462,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
--------------

Expand Down Expand Up @@ -681,6 +750,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
--------------

Expand Down
8 changes: 7 additions & 1 deletion src/runtime/url.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { LookupAddress } from "node:dns";
import { lookup } from "node:dns/promises";
import { isIP } from "node:net";

Expand Down Expand Up @@ -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) ||
Expand Down
50 changes: 49 additions & 1 deletion src/webfinger/lookup.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
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";
Expand Down Expand Up @@ -91,6 +93,52 @@ test("lookupWebFinger()", async (t) => {
assertEquals(await lookupWebFinger("acct:[email protected]"), 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:[email protected]"),
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:[email protected]"), 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:[email protected]"),
UrlError,
);
});

mf.uninstall();
});

Expand Down
29 changes: 28 additions & 1 deletion src/webfinger/lookup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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: {
Expand All @@ -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) {
Expand Down

0 comments on commit b9f34f4

Please sign in to comment.