← Back to Feed CACHED · 2026-09-17 07:26:43 · CACHE_KEY CVE-2026-90982
CVE-2026-90982 · CWE-178 · Disclosed 2026-09-17

@fastify/static is a Fastify plugin that serves static files from a configured root directory.

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

Your route guard checks the spelling on the invitation, but Windows doesn't care about uppercase

CVE-2026-90982 is the sixth bypass variant in a rolling series of @fastify/static route-guard / allowedPath evasion bugs disclosed since April 2026. This one exploits CWE-178 (Improper Handling of Case Sensitivity): on case-insensitive filesystems (Windows, default macOS), a request to /DEEP/secret.txt does not match the guarded /deep/* route in Fastify's router (find-my-way), so the allowedPath callback never fires — yet the OS happily resolves the uppercase path to the same file. Affected versions: all @fastify/static < 10.1.4. Fixed in 10.1.4. Impact is read-only (confidentiality of files already within the configured static root); no directory traversal outside the root, no code execution, no write.

The vendor's MEDIUM 5.3 is honest but slightly generous. The CVSS vector AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N correctly captures unauthenticated remote access and low confidentiality impact, but it does not encode the platform prerequisite: the bug is a no-op on Linux (case-sensitive), which is where the overwhelming majority of production Node.js workloads run. Factor in that exploitation also requires the application to actually *use* allowedPath or route guards (without guards, there is nothing to bypass), and the real-world blast radius shrinks further. noisgate adjusts to MEDIUM 4.8 — the narrowing factors don't justify dropping to LOW because the attack is trivially simple when conditions align, but they do pull the score below the vendor's 5.3.

"Case-folding bypass only bites Windows/macOS deploys behind route guards — most prod is Linux."
02 · The Attack Path

4 steps from start to impact.

STEP 01

Identify a @fastify/static endpoint with route guards

The attacker discovers a Fastify application serving static files with allowedPath or route-level middleware that restricts access to certain paths (e.g., /deep/* requires authentication). This is observable through normal interaction — requesting a guarded path returns 401/403.
Conditions required:
  • Target runs @fastify/static < 10.1.4
  • Target uses allowedPath callback or route-level guards on static paths
Where this breaks in practice:
  • Many @fastify/static deployments serve fully public assets and use no guards at all — nothing to bypass
STEP 02

Determine the target OS is case-insensitive

The attacker needs the server's filesystem to be case-insensitive (Windows or default macOS). A quick test: request /Favicon.ICO vs /favicon.ico — if both return the same file, the filesystem is case-insensitive. On Linux (ext4, XFS) this returns 404 for the wrong case, and the attack stops here.
Conditions required:
  • Server runs on Windows or macOS with default HFS+ / APFS (case-insensitive)
Where this breaks in practice:
  • ~80-90% of production Node.js servers run Linux (case-sensitive), making this a non-issue for the majority of the fleet
  • macOS is primarily used for development, not production hosting
Detection/coverage: Web application scanners (Burp, Nuclei) can detect case-insensitive behavior automatically
STEP 03

Send case-altered request to bypass route guard

The attacker sends a request like GET /DEEP/secret.txt or GET /Deep/Secret.txt. Fastify's find-my-way router does not match this against the /deep/* route (case-sensitive match), so the request falls through to the static file handler's catch-all. The allowedPath callback, if bound to the guarded route, never executes. The OS resolves the file case-insensitively and @fastify/send serves it.
Conditions required:
  • Previous two conditions met
Where this breaks in practice:
  • The file must exist within the configured static root — no traversal outside it
  • Impact is read-only; no write, no RCE, no lateral movement
Detection/coverage: WAF rules that normalize URL case before routing would block this; request logging that flags unusual casing patterns can detect it
STEP 04

Exfiltrate guarded static content

The attacker reads files that were supposed to be behind the route guard. These might include configuration snippets, internal documentation, or other static assets the developer intended to protect. The blast radius is bounded by what's in the static root directory — not the entire filesystem.
Conditions required:
  • Sensitive files exist within the static root behind the bypassed guard
Where this breaks in practice:
  • Truly sensitive secrets (API keys, database credentials) rarely live in a static-file directory
  • If the static root contains only frontend bundles (JS/CSS/images), the bypass yields nothing of value
Detection/coverage: Access logs will show 200 responses for paths that normally return 401/403 — anomaly detection on status-code patterns catches this
03 · Intelligence Metadata

The supporting signals.

In-the-Wild ExploitationNo evidence of active exploitation. Not on CISA KEV. Disclosed same day as this assessment (2026-09-17).
Proof of ConceptTrivial — curl https://target/DEEP/secret.txt on a Windows-hosted Fastify app. No weaponized tooling needed. Credited to 0xRenSec.
EPSS ScoreNot yet scored — CVE published 2026-09-17, EPSS typically lags 1-2 days. Expect low probability given platform constraint.
KEV StatusNot listed. No federal mandate for accelerated patching.
CVSS VectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N — unauthenticated network access, low confidentiality impact only. No integrity or availability impact.
Affected Versions@fastify/static < 10.1.4 (all prior versions back to at least 8.x). The legacy fastify-static package (pre-scoped) is likely affected but unmaintained.
Fixed Version10.1.4 — validates requested path spelling against on-disk names, rejecting case-aliased variants before authorization.
Bypass Series Context6th bypass in the allowedPath/route-guard series since April 2026: CVE-2026-6410, CVE-2026-6414, CVE-2026-7120, CVE-2026-15074, CVE-2026-18427, now CVE-2026-90982. Pattern suggests more variants may follow.
Exposure Data~852K weekly npm downloads. However, exploitability requires Windows/macOS hosting AND active use of route guards — estimated exposed population is <5% of installs.
Disclosure2026-09-17 via GitHub Security Advisory GHSA-r799-r9gc-m956. Coordinated disclosure with fix available same day.
04 · The Call

noisgate verdict.

Final Verdict
= UNCHANGED to MEDIUM (4.8/10)

The single most decisive factor is platform restriction: this bypass only functions on case-insensitive filesystems (Windows, default macOS), which represent a small minority of production Node.js deployments. The vendor's MEDIUM 5.3 is reasonable as a ceiling, but the real-world exposed population — Windows/macOS hosts running @fastify/static with active route guards protecting sensitive static content — is conservatively under 5% of installs, justifying a modest downward adjustment to 4.8.

HIGH Vulnerability mechanism and affected versions
HIGH Platform restriction (case-insensitive FS requirement)
MEDIUM Exposed population estimate (<5% of installs)

Why this verdict

  • Platform narrowing: The bug is a complete no-op on Linux (ext4, XFS, Btrfs — all case-sensitive). The vast majority of production Node.js runs on Linux. This single prerequisite eliminates ~85% of the installed base from exposure.
  • Guard prerequisite: Exploitation requires the application to use allowedPath or route-level middleware on static paths. Applications serving fully public static assets have nothing to bypass, further shrinking the vulnerable population.
  • Read-only, root-scoped impact: The CVSS vector correctly assigns C:L with I:N/A:N. The attacker can only read files already within the configured static root — no traversal beyond it, no code execution, no write capability. Blast radius is a subset of one application's static directory.
  • Role multiplier: @fastify/static is a web application library for serving frontend assets. It is not a canonical high-value-role component (not an IdP, hypervisor, DC, backup server, CI/CD, or network edge). In its typical deployment role, a successful exploit yields read access to static files (JS bundles, images, HTML) — blast radius is application-scoped, not fleet-scoped. No high-value role floor applies.
  • Trivial exploitability when conditions align: Despite the narrowing factors, the attack requires zero authentication, zero user interaction, and zero sophistication — a single HTTP request. This prevents further downgrade below MEDIUM.

Why not higher?

The bug cannot execute code, write files, or escape the static root. It only affects case-insensitive filesystems, which excludes the dominant Linux production base. The affected component (@fastify/static) is an application-layer library, not infrastructure — even a successful exploit is scoped to one application's static directory, not a domain, fleet, or supply chain. No KEV listing, no active exploitation, no high-value role multiplier.

Why not lower?

The attack is unauthenticated and trivially simple — a single HTTP request with altered casing. When conditions align (Windows server, guarded paths, sensitive files in static root), an attacker gets immediate read access with no tooling. The ongoing bypass series (6 variants in 5 months) signals systemic normalization issues in the codebase, raising the probability that deployments lagging on patches are exposed to multiple compounding bypasses. This is not backlog-tier.

05 · Compensating Control

What to do — in priority order.

  1. Upgrade to @fastify/static 10.1.4 immediately — The fix validates requested path spelling against on-disk names and rejects case-aliased variants before authorization. This is the definitive remediation. Given the MEDIUM severity, go straight to the noisgate remediation SLA of 365 days, but the trivial fix (npm update) warrants doing it within your next sprint.
  2. Deploy on Linux for production — Case-sensitive filesystems (ext4, XFS, Btrfs) are immune to this class of bypass entirely. If you're running Node.js on Windows in production, this is one more reason to migrate. This eliminates the entire CWE-178 attack surface.
  3. Normalize URL case in a reverse proxy or WAF before it reaches Fastify — Configure nginx, Caddy, or your WAF to lowercase all incoming URL paths before forwarding to the Fastify backend. This neutralizes the case-folding mismatch. Example nginx: set $uri_lower $uri; # use lua or map to lowercase.
  4. Audit allowedPath callbacks for case-insensitive comparison — If you cannot upgrade immediately, modify your allowedPath callback to compare paths case-insensitively: return requestedPath.toLowerCase().startsWith('/allowed/'). This closes the specific bypass at the application level.
What doesn't work
  • Network-level controls (firewall, segmentation) — the attack is a normal HTTP GET request on the application's legitimate port. No network control can distinguish it from a valid request.
  • Authentication middleware on non-static routes — the bypass specifically targets the mismatch between the router's case-sensitive matching and the OS's case-insensitive resolution. Auth on other routes does not protect the static handler's catch-all.
  • Content Security Policy / CORS headers — these are response-side controls; they do not prevent the server from resolving and serving the file to the attacker's direct request.
06 · Verification

Crowdsourced verification payload.

Run this on your auditor workstation against each target URL. No special privileges needed — it's just HTTP requests. Example: bash check_cve_2026_90982.sh https://myapp.example.com/deep/secret.txt

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# CVE-2026-90982 — @fastify/static case-folding route guard bypass checker
# Usage: ./check_cve_2026_90982.sh <guarded_url>
#   e.g. ./check_cve_2026_90982.sh https://app.example.com/deep/secret.txt
# The URL should be a path you KNOW is behind a route guard (returns 401/403).
# No privileges required — runs HTTP requests only.

set -euo pipefail

if [ $# -lt 1 ]; then
  echo "Usage: $0 <guarded_url>"
  echo "  Provide a URL that should return 401/403 when accessed normally."
  exit 2
fi

URL="$1"

# Step 1: Confirm the original path is guarded
ORIG_STATUS=$(curl -s -o /dev/null -w '%{http_code}' "$URL")
if [ "$ORIG_STATUS" -lt 400 ]; then
  echo "UNKNOWN — Original URL returned HTTP $ORIG_STATUS (expected 401/403). Is this path actually guarded?"
  exit 2
fi
echo "[+] Original path returns HTTP $ORIG_STATUS (guarded) — good."

# Step 2: Build an uppercase variant of the path component
# Extract scheme+host and path separately
SCHEME_HOST=$(echo "$URL" | grep -oP '^https?://[^/]+')
PATH_PART=$(echo "$URL" | grep -oP '^https?://[^/]+\K.*')
UPPER_PATH=$(echo "$PATH_PART" | tr '[:lower:]' '[:upper:]')
BYPASS_URL="${SCHEME_HOST}${UPPER_PATH}"

echo "[+] Testing bypass URL: $BYPASS_URL"
BYPASS_STATUS=$(curl -s -o /dev/null -w '%{http_code}' "$BYPASS_URL")

if [ "$BYPASS_STATUS" -ge 200 ] && [ "$BYPASS_STATUS" -lt 300 ]; then
  echo "VULNERABLE — Uppercase path returned HTTP $BYPASS_STATUS (bypassed guard). CVE-2026-90982 confirmed."
  exit 1
elif [ "$BYPASS_STATUS" -ge 400 ]; then
  echo "PATCHED — Uppercase path returned HTTP $BYPASS_STATUS (guard held or case-sensitive FS). Not exploitable."
  exit 0
else
  echo "UNKNOWN — Uppercase path returned HTTP $BYPASS_STATUS. Manual review recommended."
  exit 2
fi
07 · Bottom Line

If you remember one thing.

TL;DR
CVE-2026-90982 is a case-folding route-guard bypass in @fastify/static < 10.1.4 that only works on case-insensitive filesystems (Windows, default macOS). If you run Node.js on Linux in production — and most shops do — this is a non-issue for your live fleet, though macOS dev environments may be affected. For Windows-hosted Fastify apps with allowedPath guards, run npm install @fastify/[email protected] in your next change window. At noisgate MEDIUM, there is no mitigation SLA — go straight to the noisgate remediation SLA of 365 days. That said, this is a one-line package.json bump with a same-day vendor fix, so there's no reason to wait: roll it into your next scheduled dependency update cycle. Keep an eye on this package — six guard-bypass CVEs in five months signals systemic normalization debt in the router↔static-handler interface, and more variants are likely incoming.

Sources

  1. GitHub Advisory GHSA-r799-r9gc-m956
  2. CWE-178: Improper Handling of Case Sensitivity
  3. @fastify/static on npm
  4. GitLab Advisory Database — @fastify/static CVE-2026-7120
  5. GHSA-423g-23ch-w7c6 — Non-canonical path segments bypass
  6. Snyk — @fastify/static vulnerabilities
  7. fastify/fastify-static Security Overview
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.