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.
4 steps from start to impact.
Identify Fastify target serving static assets
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.- Target runs @fastify/static <= 10.1.1
- Application is network-reachable
- Many Fastify deployments strip
x-powered-byor sit behind a reverse proxy / CDN that masks the origin framework
Confirm allowedPath is in use
allowedPath filtering is active. Without allowedPath, there is nothing to bypass; the static files are already public. This step is inferential and noisy.- Application uses
allowedPathcallback to restrict access to certain subtrees
- A large fraction of @fastify/static deployments do not use
allowedPathat 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
Craft non-canonical URL to bypass allowedPath
/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.- Target file exists within the static root but in a path subtree that allowedPath would normally deny
- 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
../ and // in request paths. IDS/IPS with HTTP normalization will also flag these.Exfiltrate restricted static content
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.- Sensitive data is stored as static files within the restricted subtree
- 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
The supporting signals.
| In-the-Wild Exploitation | No evidence. Not listed on CISA KEV. No GreyNoise tags or Shadowserver scans observed as of 2026-08-06. No known campaigns. |
|---|---|
| Proof-of-Concept | Trivial — 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 Score | Not yet scored (CVE published 2026-07-22). Sibling CVE-2026-7120 is also too recent for a reliable EPSS percentile. |
| KEV Status | Not listed. No CISA KEV entry as of 2026-08-06. |
| CVSS Vector | User-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 Version | 10.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 Date | 2026-07-22 (GHSA publication) / 2026-07-24 (GitLab Advisory Database). |
| Reporter | yuki-matsuhashi (finder). Fix by mcollina (Matteo Collina, Fastify lead maintainer). Reviewed by UlisesGascon. |
noisgate verdict.
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.
Why this verdict
- Prerequisite narrows population: Only apps that implement the
allowedPathcallback are exploitable. Deployments serving a fully publicdist/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.
What to do — in priority order.
- 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. - Upgrade @fastify/static to 10.1.2+ — Run
npm update @fastify/staticor pin>=10.1.2inpackage.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-linepackage.jsonchange worth doing this sprint. - Audit allowedPath callbacks for defense-in-depth — Review every
allowedPathimplementation 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. - 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.
- 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 theallowedPathbypass, which serves individual files, not directory listings.
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.
#!/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\nfiIf you remember one thing.
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
- GHSA-8pvw-jcv7-9cmj — @fastify/static Authorization Bypass via Non-Canonical URL Paths
- GitLab Advisory Database — CVE-2026-7120
- @fastify/static README and allowedPath documentation
- SecureLayer7 — CVE-2026-15074 Route Guard Bypass Analysis
- SentinelOne — CVE-2026-6414 Fastify Static Auth Bypass
- @fastify/static on npm
- Snyk — Directory Traversal in @fastify/static CVE-2026-6410
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.