Sep 29, 2026·6 min read·4 visits
Malformed URLs with unbalanced brackets bypass fast-uri checks but resolve to internal hosts in WHATWG HTTP clients, enabling SSRF.
An interpretation conflict in the fast-uri library allows unauthenticated remote attackers to bypass Server-Side Request Forgery filters due to inconsistent handling of malformed bracket notation in hostnames.
The fast-uri library is a high-performance URI parsing and serialization package designed for Node.js environments. It serves as an alternative to the native Node.js WHATWG URL implementation, optimizing for processing speed and compliance with RFC 3986. The package is integrated into high-throughput web frameworks and reverse proxies where processing performance is a critical path.\n\nThis library contains an interpretation conflict vulnerability (CWE-436) when processing URL structures featuring malformed bracket notation in the host portion of the authority block. If an input URL contains unbalanced or misplaced brackets (such as [ or ]), the parser fails to identify the structure as invalid or to classify it correctly as an IP literal.\n\nBecause fast-uri does not reject the malformed input, it returns the entire bracket-laden host string to the calling application as a valid hostname. A parser differential arises when the application passes this same validated URL to standard HTTP clients that follow WHATWG parsing specifications. This discrepancy undermines Server-Side Request Forgery (SSRF) protections, allowing attackers to route requests to internal interfaces.
Under RFC 3986, square brackets in the authority segment are reserved for IP-literal formats, such as IPv6 or IPvFuture addresses. Standard domain names (registered names or reg-name) must not contain bracket characters. The vulnerable versions of fast-uri perform an incomplete check to determine if a host represents an IP literal, relying on a simple check of the start and end characters.\n\nSpecifically, the library implements bracketedIPLiteral by checking if the first character of the host is [ and the final character is ]. If a host string such as [@127.0.0.1 is evaluated, this check returns false because the closing bracket is absent at the absolute end. Since the check returns false, the parser skips strict IPv6 verification and fails to raise an error. The unbalanced bracket is not treated as an illegal character within the domain name, leading to the host being successfully parsed.\n\nWhen a WHATWG-compliant parser processes this same malformed URL, it processes the authority block according to different precedence rules. The WHATWG standard recognizes the @ character as a delimiter between credentials (userinfo) and the hostname. Consequently, a URL like http://[@127.0.0.1 splits the authority into a username of [ and a hostname of 127.0.0.1. This inconsistent parsing creates a security discrepancy between validation and execution pathways.\n\nmermaid\ngraph LR\n Input["URL: http://[@127.0.0.1"] --> FastURI["fast-uri parser"]\n Input --> WHATWG["WHATWG Parser (e.g., axios)"]\n FastURI --> FastResult["Host: '[@127.0.0.1' (Accepted)"]\n WHATWG --> WHATWGResult["Host: '127.0.0.1' (Internal Loopback)"]\n
The vulnerability is situated within the host normalization and status parsing logic of fast-uri. In the vulnerable code path, the lack of comprehensive bracket validation allows invalid IP-literal structures to pass into downstream canonicalization routines. This lack of initial validation prevents the parser from flagging the malformed structure as a failure state.\n\njavascript\n// Vulnerable logic in fast-uri\nconst bracketedIPLiteral = parsed.host[0] === '[' && parsed.host[parsed.host.length - 1] === ']'\nconst ipv6result = normalizeIPv6(parsed.host)\nisIP = ipv6result.isIPV6 || ipv6result.isIPVFuture === true\nmalformedIPLiteral = bracketedIPLiteral && ipv6result.error === true\n\n\nIn the patched versions, the maintainers isolated the bracket checking logic into an independent function called isIPLiteral and added validation via hasIPLiteralBracket to identify any bracket character anywhere in the host string. This ensures that any bracket occurrences trigger validation as an IP literal, forcing a check that will eventually fail and yield a malformed host error.\n\njavascript\n// Patched logic in fast-uri\nfunction isIPLiteral (host) {\n return host[0] === '[' && host[host.length - 1] === ']';\n}\n\n// Within parseWithStatus:\nconst bracketedIPLiteral = isIPLiteral(parsed.host);\nconst hasIPLiteralBracket = parsed.host.indexOf('[') !== -1 || parsed.host.indexOf(']') !== -1;\nconst ipv6result = normalizeIPv6(parsed.host);\nisIP = ipv6result.isIPV6 || ipv6result.isIPVFuture === true;\nmalformedIPLiteral = hasIPLiteralBracket && (!bracketedIPLiteral || ipv6result.error === true);\n\n\nAdditionally, the host canonicalization block is guarded. If malformedIPLiteral evaluates to true, the library blocks canonicalization and marks the parsing status as failed. This prevents unclosed bracket payloads from bypassing validation controls.
Exploiting this vulnerability requires an application that uses a vulnerable version of fast-uri to sanitize or validate URLs before passing them to a WHATWG-compliant HTTP client. The attacker must have control over the destination URL. No authentication or special privileges are required to submit the payload.\n\nTo perform the bypass, the attacker constructs a payload containing an unbalanced bracket immediately preceding the userinfo delimiter symbol. A common vector format is http://[@127.0.0.1/admin. When processed by fast-uri, the hostname is extracted as [@127.0.0.1. If the application's defense filter matches this string against a list of blocked hosts (such as 127.0.0.1 or localhost), the string fails to trigger the block, and the request is permitted.\n\njavascript\n// Proof of Concept Demonstration\nconst fastURI = require('fast-uri');\nconst payloadUrl = 'http://[@127.0.0.1:8080/admin';\n\n// fast-uri parses the malformed URL successfully\nconst parsedFast = fastURI.parse(payloadUrl);\nconsole.log(parsedFast.host); // Outputs: "[@127.0.0.1"\n\n// Downstream client interprets using WHATWG rules\nconst parsedNative = new URL(payloadUrl);\nconsole.log(parsedNative.hostname); // Outputs: "127.0.0.1"\n\n\nFollowing validation, the application executes the connection using a native client or library (such as axios or fetch). The downstream engine parses the same URL, identifies [ as userinfo, and resolves the request to 127.0.0.1. This allows the attacker to connect to local interfaces or cloud metadata endpoints.
The impact of CVE-2026-84394 is high because it facilitates arbitrary Server-Side Request Forgery (SSRF) on applications that rely on fast-uri for URL routing or validation filters. SSRF allows remote attackers to scan internal infrastructure, access local APIs, or interact with restricted cloud metadata services (e.g., the AWS Instance Metadata Service at 169.254.169.254).\n\nThe CVSS v3.1 base score of 7.5 reflects a high-severity rating. The attack vector is Network, requiring low complexity and no privileges or user interaction. The scope remains unchanged since the exploit operates within the same authorization boundaries of the host system. The impact is primarily categorized as High for Integrity, because the vulnerability allows attackers to bypass security filters designed to maintain network integrity.\n\nWhile there is no current evidence of weaponized exploitation in the wild, the availability of documented proof-of-concept vectors increases the likelihood of opportunistic attacks. Security teams must assume that any endpoint validating URLs with fast-uri and fetching them via standard Node.js engines is vulnerable.
The primary remediation path is to upgrade the fast-uri package to a patched release. For environments running the 2.x branch, upgrade to 2.4.6 or higher. For the 3.x branch, upgrade to 3.1.7 or higher. For the 4.x branch, upgrade to 4.1.4 or higher.\n\nIf the dependency is transitively pulled by third-party packages, use dependency overrides or resolutions within the package configuration. Adding an overrides block in npm package.json or a resolutions block in Yarn forces the package manager to use the secure version across all dependencies.\n\nTo prevent parser differentials structurally, developers should align the parsing engine used for security checks with the engine used for request execution. Using the native WHATWG URL implementation for both validation and execution removes interpretation discrepancies. Furthermore, applications should resolve the hostname to an IP address programmatically and perform verification directly against the IP list before establishing a network socket.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
fast-uri fastify | 2.4.5 | 2.4.6 |
fast-uri fastify | 3.1.6 | 3.1.7 |
fast-uri fastify | 4.1.3 | 4.1.4 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-436 |
| Attack Vector | Network |
| CVSS Score | 7.5 (High) |
| EPSS Score | 0.0038 |
| Exploit Status | Proof of Concept (PoC) available |
| KEV Status | Not listed |
The product of two or more separate components fails to interpret input consistently, creating a security weakness.
A high-severity Cross-Site Scripting (XSS) vulnerability in Angular server-side rendering (SSR) component allows unauthenticated attackers to execute arbitrary client-side JavaScript. The flaw is caused by a parsing discrepancy between the server-side DOM emulator, Domino, and standard client-side browser HTML5 parsers. When serializing ProcessingInstruction nodes inside raw-content fallback elements, Domino fails to escape matching ancestor closing tags, causing the client-side parser to transition out of raw-text mode prematurely and execute subsequent sibling elements as active HTML.
A validation bypass vulnerability exists in the npm package `ip-address` from version 10.2.0 to 10.5.1. The library's `Address6.isPrivate()` classifier fails to recognize the NAT64 local-use prefix range 64:ff9b:1::/48 as a restricted, private subnet. In networks implementing NAT64 routing configurations, an attacker can exploit this flaw to execute Server-Side Request Forgery (SSRF) and bypass local trust-boundary validations.
A validation bypass exists in the ip-address library prior to version 10.5.1. The Address6.isLinkLocal() method inaccurately restricted link-local classifications to the fe80::/64 subnet, failing to cover the complete RFC 4291 fe80::/10 allocation. This allows attackers to bypass SSRF filters relying on this library to safeguard local network boundaries.
An authority injection vulnerability exists in the serialization components of fast-uri (versions before 2.4.6, 3.1.7, and 4.1.4) where unvalidated port components can contain authority delimiters (such as '@'). This results in host demotion to userinfo, redirection of traffic to an arbitrary attacker-controlled host, and downstream Server-Side Request Forgery (SSRF) without causing parser errors in standard clients.
CVE-2026-87859 is a medium-severity log injection vulnerability in the Node.js morgan HTTP request logger middleware. Prior to version 1.12.1, the internal sanitization utility fails to escape double-quote characters within logged fields, enabling unauthenticated remote attackers to inject arbitrary text, close log fields early, and spoof critical metadata in downstream log parsers.
An uncontrolled resource consumption vulnerability exists in the multer middleware for Node.js (versions 2.2.0 through 2.3.0) when handling aborted multipart uploads using disk storage. Due to an asynchronous race condition in path resolution, files can become permanently orphaned on disk, leading to storage exhaustion and denial of service.