← Back to Feed CACHED · 2026-08-06 15:19:27 · CACHE_KEY CVE-2026-18427
CVE-2026-18427 · CWE-22 · Disclosed 2026-07-22

@fastify/static vulnerable to route guard bypass via non-canonical path segments

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

It's like a museum guard who checks your ticket before reading the room number — but you wrote the room number in mirror-script

@fastify/static versions prior to 10.1.2 evaluate the allowedPath security callback before normalizing dot segments (/../), current-directory references (/./), and duplicate slashes (//) in the request pathname. An attacker can craft a non-canonical URL such as /public/../private/secret.json or //admin/config.yaml that the allowedPath callback sees as benign but that path.resolve() later maps to a restricted file within the configured static root. The flaw is classified as CWE-22 (Path Traversal) by the reporter and more precisely as CWE-180 (Incorrect Behavior Order: Validate Before Canonicalize) by the maintainers. All versions of @fastify/static through 10.1.1 are affected; the fix landed in 10.1.2, published 2026-07-22. The package sees ~1.5 million weekly npm downloads.

The vendor-stated HIGH / 7.5 overstates real-world risk. The GitHub Security Advisory (GHSA-8pvw-jcv7-9cmj, mapped to the sibling CVE-2026-7120) rates the same class of bypass as Moderate / 5.3 with Confidentiality:Low, and noisgate agrees with that framing. The attack is unauthenticated and network-reachable, which sounds scary, but the blast radius has hard limits: (1) only applications that rely on allowedPath as a security boundary are affected — many @fastify/static deployments simply serve a public dist/ folder with no callback at all; (2) the attacker cannot escape the configured static root, so this is *not* arbitrary file read on the host; (3) impact is read-only — no integrity, no availability, no code execution. The 7.5 score with C:H assumes high confidentiality impact, but reading extra files inside a static directory rarely equates to database credentials or PII unless the developer chose to co-locate secrets alongside public assets, which is a deployment anti-pattern rather than the norm.

"Route guard bypass limited to static-root reads; no RCE, no write, and only apps using allowedPath are affected."
02 · The Attack Path

4 steps from start to impact.

STEP 01

Identify Fastify target serving static assets

The attacker discovers a web application responding with Fastify-characteristic headers or behavior. Fingerprinting can be done via the x-powered-by header (if not stripped) or by probing known Fastify error response shapes. Shodan/FOFA queries for Fastify are straightforward but the framework does not self-identify loudly by default.
Conditions required:
  • Target runs @fastify/static <= 10.1.1
  • Application is network-reachable
Where this breaks in practice:
  • Many Fastify deployments strip x-powered-by or sit behind a reverse proxy / CDN that masks the origin framework
Detection/coverage: Web application scanners (Nuclei, Nikto) with updated templates can fingerprint Fastify.
STEP 02

Confirm allowedPath is in use

The attacker probes for evidence that certain paths return 404 while their parent directories serve content — a signal that allowedPath filtering is active. Without allowedPath, there is nothing to bypass; the static files are already public. This step is inferential and noisy.
Conditions required:
  • Application uses allowedPath callback to restrict access to certain subtrees
Where this breaks in practice:
  • A large fraction of @fastify/static deployments do not use allowedPath at all — they serve a flat public directory. The vulnerable population is a subset of a subset.
  • Probing multiple paths generates log entries and may trigger WAF rate limiting
Detection/coverage: WAF or application-layer logging will show sequential probing of path variants.
STEP 03

Craft non-canonical URL to bypass allowedPath

The attacker sends a request with path segments like /public/../private/secret.json, /./admin/config.yaml, or //restricted/data.csv. The allowedPath callback evaluates the raw, un-normalized path and returns true (pass). The static file handler then normalizes the path and serves the file from the restricted subtree.
Conditions required:
  • Target file exists within the static root but in a path subtree that allowedPath would normally deny
Where this breaks in practice:
  • The attacker must know or guess the exact file names inside the restricted subtree — blind enumeration is slow
  • The file must reside within the configured static root; this is not arbitrary file read on the OS filesystem
  • A well-configured WAF (ModSecurity, Cloudflare WAF) with path-traversal rules will block ../ and // patterns in URLs
Detection/coverage: WAF path-traversal signatures (OWASP CRS rule 930100-930120) detect ../ and // in request paths. IDS/IPS with HTTP normalization will also flag these.
STEP 04

Exfiltrate restricted static content

The server responds with the contents of the restricted file. The attacker obtains data that was intended to be gated behind the allowedPath check. Impact is limited to whatever static files the developer placed behind the guard — typically HTML templates, internal documentation, or pre-rendered reports. Secrets, database credentials, and source code are not conventionally stored in static asset directories.
Conditions required:
  • Sensitive data is stored as static files within the restricted subtree
Where this breaks in practice:
  • High-value secrets are almost never served via static file plugins — they live in environment variables, vaults, or databases
  • The Node.js process UID's filesystem permissions further bound what can be read
Detection/coverage: Data-loss-prevention (DLP) or response-body inspection proxies can flag unexpected file types being served from the static handler.
03 · Intelligence Metadata

The supporting signals.

In-the-Wild ExploitationNo evidence. Not listed on CISA KEV. No GreyNoise tags or Shadowserver scans observed as of 2026-08-06. No known campaigns.
Proof-of-ConceptTrivial — a single curl command with /../ in the path is sufficient. The GHSA advisory (GHSA-8pvw-jcv7-9cmj) describes the technique. No weaponized tooling or Nuclei template observed yet.
EPSS ScoreNot yet scored (CVE published 2026-07-22). Sibling CVE-2026-7120 is also too recent for a reliable EPSS percentile.
KEV StatusNot listed. No CISA KEV entry as of 2026-08-06.
CVSS VectorUser-provided: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N (7.5). Maintainer-assessed (GHSA): CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N (5.3). noisgate aligns with C:L — the read is bounded to static-root files, not host-level secrets.
Affected Versions@fastify/static all versions through 10.1.1 (npm). Older major branches (8.x, 9.x) also affected per the advisory lineage (CVE-2026-6414, CVE-2026-6410, CVE-2026-15074).
Fixed Version10.1.2 (npm, published 2026-07-22). No distro backports — this is a pure npm dependency.
Exposure / Install Base~1.5 million weekly npm downloads (npmjs.com). However, only the subset using allowedPath as a security boundary is exploitable. Fastify apps are typically behind reverse proxies, not directly internet-exposed.
Disclosure Date2026-07-22 (GHSA publication) / 2026-07-24 (GitLab Advisory Database).
Reporteryuki-matsuhashi (finder). Fix by mcollina (Matteo Collina, Fastify lead maintainer). Reviewed by UlisesGascon.
04 · The Call

noisgate verdict.

Final Verdict
DOWNGRADED to MEDIUM (5.3/10)

The single most decisive factor is the bounded blast radius: the attacker can only read files already within the static-root directory, and only when allowedPath is actively used as a security gate — a minority deployment pattern. This is a confidentiality-only, read-only bypass of an application-level callback, not an OS-level file-read or code-execution primitive.

HIGH Vulnerability mechanics and affected versions
MEDIUM Fraction of deployments using allowedPath (estimated minority, no hard data)
HIGH No active exploitation or KEV listing

Why this verdict

  • Prerequisite narrows population: Only apps that implement the allowedPath callback are exploitable. Deployments serving a fully public dist/ folder have nothing to bypass — estimated <30% of @fastify/static installs use this feature.
  • Blast radius is capped at static root: Unlike a true path-traversal (e.g., ../../etc/passwd), the file resolution stays within the configured static directory. The attacker reads *different static files*, not arbitrary OS files. This justifies C:L, not C:H.
  • Read-only, no chaining to RCE: Impact is strictly confidentiality — no write, no availability, no code execution. There is no documented chain from this bypass to a higher-impact outcome.
  • Role multiplier: @fastify/static is an application-tier npm package. It is not a hypervisor, domain controller, identity provider, CI/CD server, PAM vault, or kernel-mode agent. The typical deployment role is a Node.js microservice or BFF behind a reverse proxy. Even in a worst case where an internal dashboard serves sensitive reports as static HTML behind allowedPath, the blast radius is *tenant-level read of specific files*, not domain/fleet/supply-chain compromise. No high-value role floor applies.
  • WAF coverage is strong: Standard OWASP CRS rules (930100–930120) detect ../ and // patterns in HTTP request paths, providing a readily available compensating control for internet-facing deployments.

Why not higher?

Upgrading to HIGH would require either (a) a credible chain to code execution or broader file-system read, (b) evidence of active exploitation, or (c) a deployment role where the static-root bypass yields fleet-scale or identity-scale impact. None of these conditions are met. The read is confined to the static directory, there is no KEV listing, and @fastify/static is an application-tier library, not infrastructure-defining software.

Why not lower?

The vulnerability is unauthenticated, network-reachable, and trivially exploitable with a single HTTP request — no tooling or special conditions beyond a deployed allowedPath callback. The ~1.5M weekly download count means a non-trivial absolute number of apps could be affected. Dropping below MEDIUM would understate the ease of exploitation for the affected subset.

05 · Compensating Control

What to do — in priority order.

  1. Deploy WAF rules blocking path-traversal sequences — Enable OWASP CRS rules 930100–930120 (or equivalent on Cloudflare, AWS WAF, Akamai) to reject requests containing ../, /./, and // in the URL path. This neutralizes the exploitation vector at the network edge without a code change. No mitigation SLA applies at MEDIUM — go straight to the 365-day remediation window, but this is cheap enough to do immediately.
  2. Upgrade @fastify/static to 10.1.2+ — Run npm update @fastify/static or pin >=10.1.2 in package.json. This is the definitive fix. Prioritize within your noisgate remediation SLA of 365 days for MEDIUM, though for internet-facing apps this is a one-line package.json change worth doing this sprint.
  3. Audit allowedPath callbacks for defense-in-depth — Review every allowedPath implementation in your codebase. Ensure the callback normalizes its input (e.g., path.normalize(pathname)) before making access decisions, as a belt-and-suspenders measure even after upgrading.
  4. Move sensitive files out of the static root — If you store access-controlled documents (reports, internal pages) in the same directory tree served by @fastify/static, relocate them to a separate route handler with proper authentication middleware. Static-file plugins should only serve truly public assets.
What doesn't work
  • Network-level ACLs / IP allowlisting alone — the attack uses a normal HTTP GET request on a legitimate port and path; IP filtering only helps if you can restrict the entire app to trusted sources, which defeats the purpose of a web app.
  • Content-Security-Policy headers — CSP controls what the *browser* loads, not what the *server* serves. It has zero effect on a direct curl-style file read.
  • Disabling directory listing (list: false) — this mitigates the sibling CVE-2026-6410 but does NOT fix the allowedPath bypass, which serves individual files, not directory listings.
06 · Verification

Crowdsourced verification payload.

Run this on any machine with node and npm installed (developer workstation or CI runner). It checks the installed version of @fastify/static in a given project directory. Usage: bash check_fastify_static.sh /path/to/your/project. No elevated privileges required.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash\n# check_fastify_static.sh — CVE-2026-18427 / allowedPath bypass checker\n# Usage: bash check_fastify_static.sh /path/to/project\n# Exit codes: 0 = PATCHED, 1 = VULNERABLE, 2 = UNKNOWN\n\nset -euo pipefail\n\nPROJECT_DIR=\"${1:-.}\"\nPKG=\"@fastify/static\"\nFIXED_MAJOR=10\nFIXED_MINOR=1\nFIXED_PATCH=2\n\nif [ ! -d \"$PROJECT_DIR/node_modules\" ]; then\n  echo \"UNKNOWN — node_modules not found in $PROJECT_DIR. Run npm install first.\"\n  exit 2\nfi\n\nPKG_JSON=\"$PROJECT_DIR/node_modules/@fastify/static/package.json\"\nif [ ! -f \"$PKG_JSON\" ]; then\n  echo \"UNKNOWN — $PKG is not installed in this project.\"\n  exit 2\nfi\n\nVERSION=$(node -e \"console.log(require('$PKG_JSON').version)\")\nIFS='.' read -r MAJOR MINOR PATCH <<< \"$VERSION\"\n\n# Strip any pre-release suffix from PATCH\nPATCH=$(echo \"$PATCH\" | grep -oE '^[0-9]+')\n\nif [ \"$MAJOR\" -gt \"$FIXED_MAJOR\" ] 2>/dev/null; then\n  echo \"PATCHED — $PKG v$VERSION is above the fixed version ($FIXED_MAJOR.$FIXED_MINOR.$FIXED_PATCH).\"\n  exit 0\nelif [ \"$MAJOR\" -eq \"$FIXED_MAJOR\" ] && [ \"$MINOR\" -gt \"$FIXED_MINOR\" ] 2>/dev/null; then\n  echo \"PATCHED — $PKG v$VERSION is above the fixed version.\"\n  exit 0\nelif [ \"$MAJOR\" -eq \"$FIXED_MAJOR\" ] && [ \"$MINOR\" -eq \"$FIXED_MINOR\" ] && [ \"$PATCH\" -ge \"$FIXED_PATCH\" ] 2>/dev/null; then\n  echo \"PATCHED — $PKG v$VERSION includes the fix.\"\n  exit 0\nelse\n  echo \"VULNERABLE — $PKG v$VERSION is below the fixed version $FIXED_MAJOR.$FIXED_MINOR.$FIXED_PATCH.\"\n  # Also check if allowedPath is used in the project\n  if grep -rl 'allowedPath' \"$PROJECT_DIR/src\" \"$PROJECT_DIR/lib\" \"$PROJECT_DIR/app\" 2>/dev/null | head -1 > /dev/null 2>&1; then\n    echo \"  WARNING: allowedPath usage detected in source — this app is actively exploitable.\"\n  else\n    echo \"  NOTE: No allowedPath usage found in common source dirs — app may not be exploitable.\"\n  fi\n  exit 1\nfi
07 · Bottom Line

If you remember one thing.

TL;DR
This is a MEDIUM finding after reassessment — downgraded from the reported HIGH/7.5 because the blast radius is capped at static-root file reads and only the subset of deployments using allowedPath are exploitable. There is no mitigation SLA at MEDIUM under the noisgate framework — go straight to the noisgate remediation SLA of 365 days by upgrading to @fastify/static >=10.1.2 (npm update @fastify/static). That said, the fix is a single dependency bump, so there is no reason to wait: have your application teams pick it up in their next sprint. For internet-facing Fastify services, enabling OWASP CRS path-traversal rules on your WAF provides immediate defense-in-depth at near-zero cost. Audit your codebase for allowedPath usage to identify which services are actually in the blast radius — if none of your apps use that callback, your effective risk is zero.

Sources

  1. GHSA-8pvw-jcv7-9cmj — @fastify/static Authorization Bypass via Non-Canonical URL Paths
  2. GitLab Advisory Database — CVE-2026-7120
  3. @fastify/static README and allowedPath documentation
  4. SecureLayer7 — CVE-2026-15074 Route Guard Bypass Analysis
  5. SentinelOne — CVE-2026-6414 Fastify Static Auth Bypass
  6. @fastify/static on npm
  7. Snyk — Directory Traversal in @fastify/static CVE-2026-6410
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.