CVE-2025-23221
Description
Fedify is a TypeScript library for building federated server apps powered by ActivityPub and other standards. This vulnerability allows a user to maneuver the Webfinger mechanism to perform a GET request to any internal resource on any Host, Port, URL combination regardless of present security mechanisms, and forcing the victim’s server into an infinite loop causing Denial of Service. Moreover, this issue can also be maneuvered into performing a Blind SSRF attack. This vulnerability is fixed in 1.0.14, 1.1.11, 1.2.11, and 1.3.4.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
@fedify/fedifynpm | >= 1.0.13, < 1.0.14 | 1.0.14 |
@fedify/fedifynpm | >= 1.1.10, < 1.1.11 | 1.1.11 |
@fedify/fedifynpm | >= 1.2.10, < 1.2.11 | 1.2.11 |
@fedify/fedifynpm | >= 1.3.3, < 1.3.4 | 1.3.4 |
Patches
10fbfe4e13d83ee06d8732d0f9d47268b1a1a291bd1d7e9a218be3c2038eebc505eb82fcd6e921134dd5098be3c2038eeb`lookupWebFinger()`: avoid SSRF attacks
3 files changed · +25 −1
CHANGES.md+5 −0 modified@@ -19,6 +19,11 @@ To be released. 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.0.13 --------------
src/webfinger/lookup.test.ts+18 −1 modified@@ -1,6 +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"; @@ -122,6 +123,22 @@ test("lookupWebFinger()", async (t) => { 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(); });
src/webfinger/lookup.ts+2 −0 modified@@ -1,4 +1,5 @@ import { getLogger } from "@logtape/logtape"; +import { validatePublicUrl } from "../runtime/url.ts"; import type { ResourceDescriptor } from "./jrd.ts"; const logger = getLogger(["fedify", "webfinger", "lookup"]); @@ -35,6 +36,7 @@ export async function lookupWebFinger( { url: url.href }, ); let response: Response; + await validatePublicUrl(url.href); try { response = await fetch(url, { headers: { Accept: "application/jrd+json" },
c505eb82fcd6Make `lookupWebFinger()` to enforce protocol consistency
3 files changed · +35 −3
CHANGES.md+10 −3 modified@@ -8,9 +8,16 @@ Version 1.0.14 To be released. - - 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 several security vulnerabilities of the `lookupWebFinger()` function. + + - 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. Version 1.0.13
src/webfinger/lookup.test.ts+13 −0 modified@@ -109,6 +109,19 @@ test("lookupWebFinger()", async (t) => { 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.uninstall(); });
src/webfinger/lookup.ts+12 −0 modified@@ -64,6 +64,18 @@ export async function lookupWebFinger( 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; }
e921134dd509`lookupWebFinger()`: avoid infinite redirection
3 files changed · +36 −1
CHANGES.md+4 −0 modified@@ -8,6 +8,10 @@ Version 1.0.14 To be released. + - 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. + Version 1.0.13 --------------
src/webfinger/lookup.test.ts+18 −0 modified@@ -1,4 +1,5 @@ import { assertEquals } from "@std/assert"; +import { deadline } from "@std/async/deadline"; import * as mf from "mock_fetch"; import { test } from "../testing/mod.ts"; import type { ResourceDescriptor } from "./jrd.ts"; @@ -91,6 +92,23 @@ 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.uninstall(); });
src/webfinger/lookup.ts+14 −1 modified@@ -3,6 +3,8 @@ import type { ResourceDescriptor } from "./jrd.ts"; const logger = getLogger(["fedify", "webfinger", "lookup"]); +const MAX_REDIRECTION = 5; // TODO: Make this configurable. + /** * Looks up a WebFinger resource. * @param resource The resource URL to look up. @@ -26,6 +28,7 @@ export async function lookupWebFinger( } 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}...", @@ -48,10 +51,20 @@ export async function lookupWebFinger( 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, ); + url = redirectedUrl; continue; } if (!response.ok) {
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
6- github.com/advisories/GHSA-c59p-wq67-24wxghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-23221ghsaADVISORY
- github.com/dahlia/fedify/commit/8be3c2038eebf4ae12481683a1e809b314be3151nvdWEB
- github.com/dahlia/fedify/commit/c505eb82fcd6b5b17174c6659c29721bc801ab9anvdWEB
- github.com/dahlia/fedify/commit/e921134dd5097586e4563ea80b9e8d1b5460a645nvdWEB
- github.com/dahlia/fedify/security/advisories/GHSA-c59p-wq67-24wxnvdWEB
News mentions
0No linked articles in our index yet.