← Back to Feed CACHED · 2026-08-24 10:48:21 · CACHE_KEY CVE-2026-76172
CVE-2026-76172 · CWE-177 · Disclosed 2026-08-23

fast-uri vulnerable to host confusion via percent-encoded scheme normalization

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

Like putting a trap door in a hallway that 99% of tenants never walk down

CVE-2026-76172 affects fast-uri, a dependency-free RFC 3986 URI toolbox for Node.js maintained under the Fastify project. The bug is in the normalize() function: it decodes percent-encoded characters in the *scheme* component using the legacy global unescape() and serializes the result back as raw characters without re-escaping or validating. A crafted input like %2f%2fevil.example:/pwn initially parses without an authority component, but after normalization becomes //evil.example:/pwn, which reparses with evil.example as the host. Affected version ranges span v2.x ≥ 2.3.1 < 2.4.5, v3.x ≥ 3.0.0 < 3.1.6, and v4.x ≥ 4.0.0 < 4.1.3. Fixed in 2.4.5, 3.1.6, and 4.1.3 respectively. Published August 23, 2026.

The vendor rates this HIGH 7.5 — and the CVSS vector (AV:N/AC:L/PR:N/UI:N) is technically accurate *for the subset of apps that call normalize() on attacker-controlled URLs and use the output for security-relevant host checks*. But that vector dramatically overstates real-world exposure. fast-uri pulls ~83 million weekly npm downloads, almost entirely as a transitive dependency of ajv (the JSON Schema validator), where it resolves developer-authored $id and $ref URIs — not user-supplied URLs. The population of applications that (a) call fast-uri.normalize() directly on untrusted input, (b) use the result for allowlist enforcement, and (c) then pass it to a *different* URL parser for actual fetching is a narrow slice of that install base. The vendor severity assumes a worst-case integration pattern that most consumers don't exhibit.

"Library-level URL confusion rarely hits the SSRF-critical code path most installs use"
02 · The Attack Path

4 steps from start to impact.

STEP 01

Attacker identifies target using fast-uri for URL validation

The attacker discovers a Node.js service that accepts user-supplied URLs (e.g., webhook registration, OAuth callback, open-redirect endpoint) and validates host allowlists using fast-uri's normalize() before dispatching the URL via Node's built-in URL or fetch(). This is the critical prerequisite — without this specific integration pattern, the chain is dead.
Conditions required:
  • Target application accepts user-controlled URLs
  • Application calls fast-uri normalize() on those URLs
  • Normalized output feeds into a host allowlist check
Where this breaks in practice:
  • Most fast-uri consumers inherit it via ajv and never call normalize() directly
  • Applications using the WHATWG URL API for both validation and fetching are immune because the parser is consistent
STEP 02

Craft percent-encoded scheme payload

The attacker constructs a URI where the scheme component contains percent-encoded forward slashes: %2f%2fevil.example:/pwn. Before normalization, this parses as a relative-path reference or an opaque scheme with no authority. The PoC is trivial — no tooling beyond a text editor is required.
Conditions required:
  • Knowledge of the allowlisted domain(s) to bypass
Where this breaks in practice:
  • Attacker must know or guess the specific allowlist entries in the target application
Detection/coverage: WAF rules inspecting for %2f%2f in scheme position can flag this pattern.
STEP 03

Normalization flips the URI structure

fast-uri's normalize() calls unescape() on the scheme, converting %2f%2f to //. The serialized output //evil.example:/pwn now has an authority component pointing to evil.example. The allowlist check that ran *before* this transformation saw the original benign-looking input and passed it.
Conditions required:
  • Application validates host BEFORE normalize(), then uses post-normalize output for fetching
Where this breaks in practice:
  • If the application validates AFTER normalize(), the confusion is visible and blocked
  • If the application uses the same parser end-to-end, no desync occurs
STEP 04

SSRF or open redirect to attacker-controlled host

The post-normalization URL is passed to Node's http.request(), fetch(), or a redirect response. The request lands on evil.example instead of the intended allowlisted host. Impact ranges from open redirect (integrity) to SSRF against internal infrastructure if the allowlist was guarding outbound server-side requests to cloud metadata endpoints or internal APIs.
Conditions required:
  • Application fetches or redirects using the normalized URL
Where this breaks in practice:
  • Cloud metadata SSRF requires the Node.js process to run in a cloud environment with an accessible metadata endpoint (169.254.169.254)
  • Most modern cloud providers (AWS IMDSv2, GCP) require hop-limited tokens that resist blind SSRF
Detection/coverage: Outbound request monitoring and cloud metadata access logs can detect unexpected calls to 169.254.169.254.
03 · Intelligence Metadata

The supporting signals.

In-the-wild exploitationNot observed. No KEV listing, no vendor or threat-intel reports of active exploitation as of 2026-08-24.
Proof of conceptTrivial PoC in the advisory itself: %2f%2fevil.example:/pwn → normalize → //evil.example:/pwn. No weaponized exploit tooling published. Reporter: YashvantHange.
EPSSNot yet scored (published 2026-08-23, <48 hours old). Expect low-to-moderate EPSS given library-level bug with conditional exploitability.
KEV statusNot listed. CISA KEV catalog does not include this CVE.
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N — Network-accessible, no privileges, no interaction, integrity-only impact. No confidentiality or availability impact scored.
Affected versionsv2.x: ≥ 2.3.1, < 2.4.5 · v3.x: ≥ 3.0.0, < 3.1.6 · v4.x: ≥ 4.0.0, < 4.1.3
Fixed versions2.4.5, 3.1.6, 4.1.3 (all released 2026-08-23)
Install base / exposure~83 million weekly npm downloads. 292 direct dependents. Overwhelmingly pulled transitively via ajv@fastify/ajv-compilerfastify. Shodan/Censys not applicable (library, not a network service).
Disclosure date2026-08-23 (coordinated via GitHub Security Advisory GHSA-jqff-g426-hqxp)
Reporter / orgYashvantHange (reporter), mcollina (remediation), UlisesGascon (reviewer). Part of a batch of 4 fast-uri advisories published the same day.
04 · The Call

noisgate verdict.

Final Verdict
DOWNGRADED to MEDIUM (5.3/10)

The single most decisive downgrade factor is conditional exploitability as a library dependency: the vulnerable normalize() code path is only security-relevant when the consuming application explicitly calls it on attacker-controlled URLs for host-policy decisions, a pattern that excludes the vast majority of the ~83M weekly download base (which inherits fast-uri via ajv for developer-authored schema URIs). Without that specific integration, the bug has zero security impact regardless of network exposure.

HIGH Vulnerability mechanics and affected versions
MEDIUM Real-world exploitability assessment (dependent on unknown consumer code patterns)
HIGH No active exploitation or KEV listing

Why this verdict

  • Conditional code-path reachability: fast-uri is overwhelmingly consumed via ajv for JSON Schema $ref resolution, where URIs are developer-controlled constants. The vulnerable normalize() function must be called on *attacker-supplied* input to be exploitable — a pattern that applies to a small fraction of the install base.
  • Validate-then-use desync required: Exploitation demands a two-parser desync: the application validates the host using one representation and then fetches using the post-normalization representation. Applications using a single consistent URL parser (e.g., WHATWG URL throughout) are immune.
  • Role multiplier: fast-uri runs in Node.js application tiers. (a) *Low-value role* — dev tooling, build scripts using ajv for config validation: chain does not succeed (no attacker-controlled URLs). (b) *Typical role* — API server using Fastify with ajv schema validation: chain does not succeed (ajv does not pass user input to fast-uri normalize). (c) *High-value role* — API gateway or SSO proxy performing URL allowlist validation via fast-uri before redirect/fetch: chain succeeds, blast radius is application-level SSRF/open-redirect (tenant-scoped, not fleet/domain). This does NOT reach the domain-takeover / fleet-compromise / supply-chain threshold. Verdict floor remains MEDIUM.
  • No exploitation evidence: Zero KEV, zero in-the-wild reports, zero weaponized PoC tooling. The advisory is <48 hours old but the PoC is trivially simple — if mass exploitation were viable, we would expect rapid uptake, which has not materialized.
  • Integrity-only impact: CVSS confirms C:N/I:H/A:N. Even in the worst case (SSRF), the attacker redirects a request — they do not directly exfiltrate data from the vulnerable process or crash it.

Why not higher?

Upgrading to HIGH would require evidence that a meaningful fraction of fast-uri consumers call normalize() on untrusted URLs for security-critical decisions. The dominant consumption path (ajv schema resolution) is not attacker-reachable. There is no KEV listing, no active exploitation, and the blast radius even in the worst-case high-value role (API gateway SSRF) is tenant-scoped, not fleet-scale. The role-multiplier floor check does not trigger because the affected component (a URI parsing library) is not canonically a high-value-role component — it is infrastructure plumbing whose impact is entirely application-dependent.

Why not lower?

Dropping to LOW would understate the risk for the subset of applications that *do* use fast-uri for URL validation on untrusted input. The PoC is trivial, exploitation requires no authentication or interaction, and SSRF against cloud metadata or internal services carries real consequences. The 83M weekly download footprint means even a small percentage of vulnerable integration patterns represents thousands of applications. MEDIUM correctly captures this conditional-but-real risk.

05 · Compensating Control

What to do — in priority order.

  1. Pin and upgrade fast-uri across your lockfiles — Run npm audit fix or explicitly bump to 2.4.5 / 3.1.6 / 4.1.3. This is a pure-JavaScript dependency update with no native bindings — risk of regression is low. Target completion within the noisgate 365-day remediation window for MEDIUM.
  2. Audit direct fast-uri usage in application code — Search your codebase for require('fast-uri') or import.*fast-uri and check whether normalize(), resolve(), or equal() are called on user-supplied URL strings. If not found, the vulnerability is not reachable in your deployment and remediation priority drops further.
  3. Use WHATWG URL consistently for security decisions — If your application validates URLs before fetching or redirecting, use Node's built-in new URL() for both validation and consumption. This eliminates the two-parser desync that the exploit relies on.
  4. Block percent-encoded slashes in scheme position at the WAF — A WAF rule rejecting %2f or %2F in the URI scheme component (before the first colon) catches this and related normalization confusion payloads.
What doesn't work
  • Upgrading Fastify alone does not help if your lockfile still resolves a vulnerable fast-uri version — check the transitive dependency, not just the framework.
  • CSP headers do not mitigate server-side SSRF; they only constrain browser-side resource loading.
  • Network segmentation around the Node.js process reduces SSRF blast radius but does not fix the URL confusion itself — open-redirect remains possible.
06 · Verification

Crowdsourced verification payload.

Run this on any host with Node.js installed. Invoke: bash check-cve-2026-76172.sh /path/to/project. No special privileges required — it reads node_modules and package-lock.json.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# check-cve-2026-76172.sh — detect vulnerable fast-uri versions
# Usage: bash check-cve-2026-76172.sh /path/to/node/project
# Exit codes: 0 = PATCHED, 1 = VULNERABLE, 2 = UNKNOWN

set -euo pipefail

PROJECT_DIR="${1:-.}"
VULN=0

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

# Find all installed fast-uri versions
while IFS= read -r pkg_json; do
  version=$(node -e "console.log(require('$pkg_json').version)" 2>/dev/null || echo "unknown")
  if [ "$version" = "unknown" ]; then
    continue
  fi

  major=$(echo "$version" | cut -d. -f1)
  minor=$(echo "$version" | cut -d. -f2)
  patch=$(echo "$version" | cut -d. -f3)

  vulnerable=false
  case "$major" in
    2)
      if [ "$minor" -eq 3 ] && [ "$patch" -ge 1 ]; then vulnerable=true; fi
      if [ "$minor" -eq 4 ] && [ "$patch" -lt 5 ]; then vulnerable=true; fi
      if [ "$minor" -eq 4 ] && [ "$patch" -ge 5 ]; then vulnerable=false; fi
      ;;
    3)
      if [ "$minor" -eq 0 ]; then vulnerable=true; fi
      if [ "$minor" -eq 1 ] && [ "$patch" -lt 6 ]; then vulnerable=true; fi
      ;;
    4)
      if [ "$minor" -eq 0 ]; then vulnerable=true; fi
      if [ "$minor" -eq 1 ] && [ "$patch" -lt 3 ]; then vulnerable=true; fi
      ;;
  esac

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

if [ "$VULN" -eq 1 ]; then
  exit 1
elif [ "$VULN" -eq 0 ]; then
  # Check if any fast-uri was found at all
  count=$(find "$PROJECT_DIR/node_modules" -path '*/fast-uri/package.json' -not -path '*/node_modules/.cache/*' 2>/dev/null | wc -l)
  if [ "$count" -eq 0 ]; then
    echo "UNKNOWN — fast-uri not found in node_modules"
    exit 2
  fi
  echo "PATCHED — all fast-uri instances are on fixed versions"
  exit 0
fi
07 · Bottom Line

If you remember one thing.

TL;DR
CVE-2026-76172 is a URL normalization confusion bug in fast-uri that the vendor rates HIGH 7.5 but noisgate downgrades to MEDIUM 5.3 because exploitability is conditional on a specific application integration pattern that most of the 83M-download install base does not exhibit. First, audit your codebases for direct fast-uri imports that call normalize() on user-supplied URLs — if you find none, this is a backlog item only. Second, bump fast-uri to 2.4.5 / 3.1.6 / 4.1.3 via npm audit fix within the noisgate remediation SLA of 365 days for MEDIUM-severity findings. There is no mitigation SLA for MEDIUM — go straight to the 365-day remediation window. If you do find direct usage on untrusted input, treat it as a targeted HIGH and patch within 30 days while adding a WAF rule to block %2f in scheme position as an interim control.

Sources

  1. GHSA-jqff-g426-hqxp — GitHub Security Advisory
  2. fast-uri on npm — download stats and dependents
  3. CWE-177: Improper Handling of URL Encoding (MITRE)
  4. fast-uri security advisories — full list
  5. GHSA-v39h-62p7-jpjc — related host confusion via percent-encoded authority delimiters
  6. fast-uri on Snyk — vulnerability database
  7. CISA Known Exploited Vulnerabilities Catalog
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.