high · 7.5CVE-2026-75975Sep 2, 2026

CVE-2026-75975: fast-uri SSRF via Malformed IPv6 Normalization

Shubham Kandhare
Security Engagement Manager, SecureLayer7

A malformed IPv6 address in a URL fed to fast-uri's normalize() function is silently rewritten into a valid private or loopback address, letting an attacker bypass host-policy checks and trigger…

Packagefast-uri
Ecosystemnpm
Affected>= 2.3.1, < 2.4.5
Fixed in2.4.5
CVE-2026-75975: fast-uri SSRF via Malformed IPv6 Normalization

The problem

fast-uri's bracketed IPv6 literal parser does not enforce the full RFC 3986 grammar. Invalid trailing text after the address is silently dropped during normalization.

For example, normalize('http://[::not-valid]/private') returns http://[::]/private, and [fc00::not-hex] collapses to [fc00::]. Critically, parse().error is never set, so any guard that checks that field offers no protection.

Proof of concept

A working proof-of-concept for CVE-2026-75975 in fast-uri, with the exact payload below.

javascript
const fastURI = require('fast-uri')

// Collapses to http://[::]/private  (loopback-adjacent)
console.log(fastURI.normalize('http://[::not-valid]/private'))

// Collapses to http://[fc00::]/internal  (unique-local)
console.log(fastURI.normalize('http://[fc00::not-hex]/internal'))

// Collapses to http://[fe80::]/internal  (link-local)
console.log(fastURI.normalize('http://[fe80::not-hex]/internal'))

// parse().error is empty for all of the above — no error signal
console.log(fastURI.parse('http://[::not-valid]/private').error) // undefined

The root cause is in normalizeIPv6() inside lib/utils.js. It parsed only the recognizable prefix of a bracketed literal and discarded the rest, so [fc00::not-hex] became [fc00::] with no error. The patch (commits 3728465, 607bfbe, 9161eded) adds strict grammar validation: any bracketed host that does not conform fully to RFC 3986 now sets parsed.error and causes normalize() to return the input unchanged instead of rewriting it to a different, valid address.

This is CWE-20 (Improper Input Validation) enabling CWE-918 (SSRF). An application that normalizes an attacker-controlled URL and then makes an outbound request based on the normalized host can be routed to loopback, unique-local (fc00::/7), or link-local (fe80::/10) addresses.

The fix

Upgrade fast-uri to **2.4.5**, **3.1.6**, or **4.1.3**. All three lines now reject malformed IPv6 literals with a host error rather than silently normalizing them. As a temporary workaround, reject any untrusted URL whose host is a bracketed IPv6 literal before passing it to fast-uri, or validate outbound request destinations against an explicit allowlist.

Reporter not attributed.

References: [1][2][3][4][5][6]

Related research