← Back to Feed CACHED · 2026-07-31 16:52:07 · CACHE_KEY CVE-2026-18446
CVE-2026-18446 · CWE-436 · Disclosed 2026-07-19

fast-uri vulnerable to host confusion via backslash authority introducer

ASSESSED — NOISGATE V0.5
Vendor
Reassessed
Verdict:
Do you agree?
01 · The Real Story

Handing two bouncers different guest lists for the same party

fast-uri versions 2.3.1–2.4.2, 3.0.0–3.1.3, and 4.0.0–4.1.0 fail to treat the literal backslash character (U+005C) as an authority delimiter in URLs with special schemes (http, https, ws, wss, ftp, file). Node's built-in WHATWG URL parser — used by fetch(), undici, and the core http/https clients — normalizes \ to /, so the two parsers extract different hosts from the same input string. The canonical example is http://evil.com\@allowed.com: fast-uri sees host allowed.com with userinfo evil.com\, while Node resolves it to host evil.com with path /@allowed.com. An application that checks the URL against an allowlist using fast-uri and then hands the same string to fetch() can be steered to an attacker-controlled destination, enabling SSRF to cloud metadata endpoints, loopback, or internal services. Note: The user-supplied identifier CVE-2026-18446 does not appear in any public database; this vulnerability is tracked as CVE-2026-16221 (GHSA-v2hh-gcrm-f6hx). The assessment below covers the described flaw regardless of identifier.

The vendor rates this HIGH at CVSS 7.5 (Snyk scores it 8.7), and the vector AV:N/AC:L/PR:N/UI:N looks alarming on paper — network-reachable, no auth, no interaction. But that vector describes the *library's parsing behavior*, not the *application's exposure surface*. The vuln only fires when a Node.js application (a) uses fast-uri specifically for security-critical URL validation such as allowlisting or SSRF filtering, and (b) then passes the identical raw string to Node's URL consumer. The overwhelming majority of fast-uri's ~260M transitive weekly downloads arrive via ajv (JSON Schema validation), where fast-uri resolves $ref pointers in schemas — a context where host confusion has zero security relevance. The vendor severity overstates real-world risk for the vast majority of installations.

"Library-level URL parser confusion; exploitable only when fast-uri gates SSRF policy ahead of Node fetch"
02 · The Attack Path

4 steps from start to impact.

STEP 01

Identify target app using fast-uri for URL gating

The attacker must find a Node.js service that accepts user-supplied URLs, validates them against a host allowlist or denylist using fast-uri (directly or via a wrapper), and then issues an outbound request with Node's fetch(), undici, or http.request(). This is a narrow intersection: the app must specifically rely on fast-uri for security, not just have it in node_modules via ajv.
Conditions required:
  • Target app accepts user-supplied URLs
  • App uses fast-uri (not Node URL) for host-based policy enforcement
  • App passes the raw URL string to a Node consumer after validation
Where this breaks in practice:
  • Most fast-uri installs are transitive via ajv for JSON Schema $ref resolution — no URL gating involved
  • Modern SSRF protection libraries (e.g., ssrf-req-filter, ssrf-agent) use Node's own URL parser, not fast-uri
  • Developers writing allowlist logic typically use new URL() directly
STEP 02

Craft backslash-smuggled URL

The attacker constructs a URL like http://evil.com\@allowed.com where allowed.com is on the target's allowlist. fast-uri parses the host as allowed.com; Node's WHATWG parser resolves the host as evil.com. No tooling beyond a text editor is required; no public exploit repo exists because the payload is trivial.
Conditions required:
  • Knowledge of at least one allowed host in the target's policy
Where this breaks in practice:
  • WAFs and reverse proxies may normalize or reject URLs containing literal backslashes before they reach the Node app
  • Some frameworks (Express, Fastify) canonicalize request URLs upstream
Detection/coverage: WAF rules scanning for \ in URL authority position; custom Semgrep rule matching fast-uri parse calls followed by fetch/http.request
STEP 03

Trigger outbound request to attacker-controlled host

The validated-but-smuggled URL is passed to fetch() or http.request(). Node resolves the host as evil.com, sending the request to the attacker's server. The attacker can harvest credentials, tokens, or API keys included in the request headers or body.
Conditions required:
  • Outbound network access from the target server to the attacker's host
Where this breaks in practice:
  • Egress firewalls restricting outbound HTTP to known destinations block this
  • Network-level allowlisting (proxy with domain allowlist) catches the mismatch
Detection/coverage: Outbound connection to unexpected IP in network flow logs; DNS resolution for attacker domain in internal DNS logs
STEP 04

Pivot to SSRF target (cloud metadata, internal API)

Alternatively, the attacker points the smuggled URL at http://169.254.169.254 or an internal service. If the allowlist check only blocks external hosts and allows internal ones (or vice versa), the confusion lets the attacker reach cloud instance metadata (AWS IMDSv1) or internal APIs. This yields IAM credentials, secrets, or internal data.
Conditions required:
  • Target runs in a cloud environment with IMDSv1 enabled, OR has reachable internal services
  • fast-uri allowlist logic doesn't also validate against internal ranges
Where this breaks in practice:
  • AWS IMDSv2 (token-required) blocks casual SSRF to metadata
  • GCP and Azure metadata endpoints require custom headers that may not be forwarded
  • Internal services behind mTLS reject unauthenticated requests
Detection/coverage: CloudTrail / GCP audit logs showing metadata endpoint access from application workloads; VPC flow logs showing traffic to 169.254.169.254
03 · Intelligence Metadata

The supporting signals.

In-the-Wild ExploitationNone observed. Not listed on CISA KEV. No known campaigns or threat-actor usage as of 2026-08-01.
Proof-of-ConceptNo dedicated exploit repo. The payload is trivial (http://evil.com\@allowed.com) and documented in the advisory itself. No weaponized tooling exists.
EPSS Score0.0022 (0.22%) — 13th percentile. Indicates very low predicted exploitation probability.
KEV StatusNot listed. No CISA KEV entry.
CVSS VectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N (7.5 HIGH). Snyk independently scores 8.7. The vector reflects the library's parsing flaw in isolation; it does not account for the narrow application-level conditions required for exploitation.
Affected Versionsfast-uri 2.3.1–2.4.2 (v2 branch), 3.0.0–3.1.3 (v3 branch), 4.0.0–4.1.0 (v4 branch). Versions prior to 2.3.1 are not affected.
Fixed Versions2.4.3, 3.1.4, 4.1.1. All three branches received backported fixes.
Exposure / Install Basefast-uri has massive transitive reach via ajv (~260M weekly npm downloads). However, the security-relevant population — apps using fast-uri directly for URL allowlisting — is estimated at <1% of total installs. No Shodan/Censys/GreyNoise signatures apply (this is a library, not a network service).
Disclosure Date2026-07-19 (GitHub Security Advisory GHSA-v2hh-gcrm-f6hx published).
ReporterNot publicly attributed in the advisory. Maintained by the Fastify team under the OpenJS Foundation.
04 · The Call

noisgate verdict.

Final Verdict
DOWNGRADED to MEDIUM (5.3/10)

The single most decisive factor is the extremely narrow exploitability surface: fast-uri's 260M+ weekly downloads are overwhelmingly transitive via ajv for JSON Schema validation, where host confusion has zero security impact — the fraction of installs where fast-uri actually gates URL-based security policy is well below 1%. The library-level CVSS vector misleadingly scores the parsing flaw in isolation without reflecting this application-context dependency.

HIGH Vulnerability mechanics and affected version ranges
HIGH Exploitability conditions (requires specific app-level usage pattern)
MEDIUM Installed base estimate for security-critical usage (<1%)
LOW CVE identifier discrepancy (user supplied CVE-2026-18446; public databases track CVE-2026-16221)

Why this verdict

  • Narrow exploitability window: The vulnerability only fires when an application uses fast-uri for host-based security policy (allowlisting, SSRF filtering) AND then hands the same raw URL string to Node's WHATWG-based consumer. This is a specific architectural pattern, not a default behavior.
  • Transitive dependency dilution: ~99%+ of fast-uri installs arrive via ajv for JSON Schema $ref resolution, where the host confusion has no security relevance whatsoever. The population of apps where this bug matters is vanishingly small.
  • No exploitation evidence: EPSS 0.22% (13th percentile), no KEV listing, no known campaigns, no weaponized tooling. The advisory was published 2026-07-19 and the payload is trivial, yet no exploitation has materialized in two weeks.
  • Multiple friction layers in practice: WAFs may reject backslash-containing URLs, egress firewalls block unexpected outbound connections, AWS IMDSv2 blocks casual SSRF, and modern SSRF-protection libraries use Node's own new URL() rather than fast-uri.
  • Role multiplier: fast-uri is a utility library, not a component canonically deployed in high-value roles. It does not run on domain controllers, hypervisors, network edge appliances, or identity providers. In the rare case a Node.js API gateway uses fast-uri for URL gating, a successful chain yields SSRF — serious but scoped to that single application's network position, not fleet-scale compromise. The blast radius is host → tenant at worst, not domain or fleet. This does not trigger the HIGH floor because the affected component is not canonically a high-value-role component and <1% of installs occupy a security-gating role.

Why not higher?

To warrant HIGH, the vulnerability would need to be exploitable in a significant share of real deployments or the affected component would need to canonically occupy a high-value infrastructure role. Neither condition holds: fast-uri is a utility parsing library, the overwhelming majority of installs are non-security-relevant transitive dependencies via ajv, and no real-world exploitation has been observed despite the trivial payload.

Why not lower?

The parsing confusion is real and mechanically sound — it demonstrably produces different hosts from the same input. For the small population of apps that *do* use fast-uri for URL validation before Node fetch, the SSRF bypass is unauthenticated, requires no interaction, and could reach cloud metadata or internal services. Dismissing it entirely would leave those apps unprotected.

05 · Compensating Control

What to do — in priority order.

  1. Audit your codebase for direct fast-uri security usage — Run grep -r 'fast-uri\|require.*fast-uri\|from.*fast-uri' --include='*.js' --include='*.ts' across your repositories. If fast-uri only appears in node_modules/ajv/, you are not directly affected and can treat this as backlog hygiene during your next npm update cycle within the noisgate remediation SLA of 365 days.
  2. Replace fast-uri with Node's built-in URL for security checks — If you find direct fast-uri usage for allowlisting or SSRF filtering, refactor to use new URL() from Node's built-in url module. This eliminates the parser mismatch entirely and is the strongest long-term fix.
  3. Add WAF rule blocking backslash in URL authority — Deploy a WAF rule that rejects or normalizes HTTP requests containing \ in the authority portion of the URL. This blocks the specific attack vector at the network edge. Deploy within the 365-day noisgate remediation SLA window.
  4. Enforce IMDSv2 on all cloud instances — If running in AWS, ensure IMDSv2 (token-required) is enforced on all EC2 instances. This blocks the most damaging SSRF outcome (credential theft from metadata) even if the URL confusion succeeds.
What doesn't work
  • Upgrading ajv alone does not help — ajv pins its fast-uri dependency range; you must explicitly upgrade fast-uri in your lockfile (npm update fast-uri or override in package.json) to get the patched version.
  • Input validation on the full URL string is insufficient — the backslash is a legitimate ASCII character that may appear in other URL components; blanket rejection causes false positives. The fix must be at the parser level or by using a consistent parser for both validation and consumption.
  • CSP (Content-Security-Policy) headers do not help — this is a server-side URL parsing issue, not a browser-side origin policy problem.
06 · Verification

Crowdsourced verification payload.

Run this on any host with Node.js installed to check if the installed fast-uri version is vulnerable. Execute from the root of your Node.js project: bash check_fast_uri.sh /path/to/project. No special privileges required.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# check_fast_uri.sh — Detect vulnerable fast-uri versions (CVE-2026-16221)
# Usage: bash check_fast_uri.sh /path/to/node/project
# Exit codes: 0 = PATCHED, 1 = VULNERABLE, 2 = UNKNOWN

set -euo pipefail

PROJECT_DIR="${1:-.}"

if [ ! -d "$PROJECT_DIR/node_modules" ]; then
  echo "UNKNOWN — no node_modules found in $PROJECT_DIR"
  exit 2
fi

# Find all fast-uri installations (may be nested in node_modules)
FOUND=0
VULN=0

while IFS= read -r pkg_json; do
  dir=$(dirname "$pkg_json")
  name=$(node -e "try{console.log(require('$pkg_json').name)}catch(e){console.log('')}" 2>/dev/null)
  version=$(node -e "try{console.log(require('$pkg_json').version)}catch(e){console.log('')}" 2>/dev/null)

  if [ "$name" = "fast-uri" ] && [ -n "$version" ]; then
    FOUND=1
    # Parse major.minor.patch
    IFS='.' read -r major minor patch <<< "$version"
    major=${major:-0}; minor=${minor:-0}; patch=${patch:-0}

    IS_VULN=0
    # v2 branch: 2.3.1 – 2.4.2
    if [ "$major" -eq 2 ]; then
      if [ "$minor" -gt 4 ] || { [ "$minor" -eq 4 ] && [ "$patch" -ge 3 ]; }; then
        : # patched
      elif [ "$minor" -gt 3 ] || { [ "$minor" -eq 3 ] && [ "$patch" -ge 1 ]; }; then
        IS_VULN=1
      fi
    # v3 branch: 3.0.0 – 3.1.3
    elif [ "$major" -eq 3 ]; then
      if [ "$minor" -gt 1 ] || { [ "$minor" -eq 1 ] && [ "$patch" -ge 4 ]; }; then
        : # patched
      else
        IS_VULN=1
      fi
    # v4 branch: 4.0.0 – 4.1.0
    elif [ "$major" -eq 4 ]; then
      if [ "$minor" -gt 1 ] || { [ "$minor" -eq 1 ] && [ "$patch" -ge 1 ]; }; then
        : # patched
      else
        IS_VULN=1
      fi
    fi

    if [ "$IS_VULN" -eq 1 ]; then
      echo "VULNERABLE — fast-uri $version found at $dir"
      VULN=1
    else
      echo "PATCHED — fast-uri $version found at $dir"
    fi
  fi
done < <(find "$PROJECT_DIR/node_modules" -name package.json -path '*/fast-uri/package.json' 2>/dev/null)

if [ "$FOUND" -eq 0 ]; then
  echo "UNKNOWN — fast-uri not found in $PROJECT_DIR/node_modules"
  exit 2
fi

if [ "$VULN" -eq 1 ]; then
  exit 1
else
  exit 0
fi
07 · Bottom Line

If you remember one thing.

TL;DR
This is a downgraded MEDIUM (5.3) from the vendor's HIGH 7.5. For the vast majority of environments, fast-uri is a transitive dependency of ajv used for JSON Schema validation — not for URL security gating — and the vulnerability has no exploitable path. Monday morning action: run the verification script across your Node.js repositories to identify any direct security-critical usage of fast-uri. If you find none (the expected outcome for 99%+ of codebases), schedule npm update fast-uri as routine dependency hygiene within the noisgate remediation SLA of 365 days — no mitigation SLA applies at MEDIUM severity, so go straight to the remediation window. If you *do* find fast-uri used for URL allowlisting or SSRF filtering, treat those specific applications as HIGH: refactor to use Node's built-in new URL() for validation and deploy the fast-uri patch immediately. No KEV listing or active exploitation exists, so there is no override to accelerate the timeline.

Sources

  1. GitHub Security Advisory GHSA-v2hh-gcrm-f6hx
  2. GitLab Advisory Database — CVE-2026-16221
  3. Snyk Vulnerability DB — SNYK-JS-FASTURI-18021349
  4. CVEFeed — CVE-2026-16221
  5. CWE-436: Interpretation Conflict
  6. npm — fast-uri package
  7. GitHub Advisory Database — CVE-2026-16221
Peer Review

What defenders are saying.

Submit a review attribution: handle + country only
0 flags selected · stored anonymously
Validation Results

Crowdsourced verification outputs.

Results submitted by users who ran the verification payload against their environment.