CVE-2026-49857
The vulnerability in `isPrivateV6()` stems from an unhandled intersection between IPv4 and IPv6 address categories, not a logical inversion. The function uses `net.isIPv4(host)` to decide whether to run IPv4 checks, treating any non-IPv4-form as automatically safe. But IPv4-mapped IPv6 addresses (`::ffff:127.0.0.1`) are syntactically IPv6 while semantically IPv4 — they satisfy neither category individually while belonging to both semantically. The WHATWG URL parser compounds this by silently normalizing IPv4-mapped addresses to hex form (`[::ffff:7f00:1]`), producing a representation that obscures the loopback address and bypasses checks looking for decimal notation or `::1`. This is a parser-to-checker semantic gap: two systems operating on different address representations, wired together without a contract. The blast radius here is significant. Auth-fetch-mcp fetches authenticated content from web pages — this bypass doesn't just expose a debug endpoint, it lets an attacker reach internal services through the MCP server's session. In containerized environments, `::ffff:127.0.0.1` can be reachable even when IPv6 is 'disabled' at the OS level, because the kernel accepts IPv4-mapped addresses on IPv6 sockets by default. The fix must normalize all IPv4-mapped addresses to their pure-IPv4 form before applying checks, or explicitly handle hex-notation IPv4-mapped addresses in `isPrivateV6()`. A third case is needed, not an operator flip. Additionally, audit other address-type checks in your codebase for the same fossilized IPv4-or-IPv6 assumption — this pattern has recurred since the early 2000s, surfacing in new implementations as IPv6 adoption and containerization expand the default attack surface.
Reviewed through automated stages and approved by a human before publication.