← Back to Feed CACHED · 2026-07-29 22:41:48 · CACHE_KEY CVE-2026-14643
CVE-2026-14643 · CWE-436 · Disclosed 2026-07-29

undici's cache interceptor mishandles optional whitespace placed around the equals sign of a qualified…

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

A whitespace typo in someone else's kitchen lets your fridge serve the wrong leftovers, but only if you installed a shelf you never asked for

CVE-2026-14643 is a parsing flaw in undici's cache interceptor — a feature introduced in v7 that is not enabled by default. When the interceptor is explicitly activated in *shared* cache mode, it fails to strip optional whitespace around the = sign in qualified Cache-Control directives like private=" authorization" or no-cache="\tauthorization". Because the padded field name no longer matches the literal string authorization, undici incorrectly classifies the response as fully cacheable. A subsequent unauthenticated caller whose request maps to the same cache key can then receive the first user's authenticated response body. Affected version ranges are undici 7.0.0 – 7.27.x and 8.0.0 – 8.4.x; fixes ship in 7.28.0 and 8.5.0.

The vendor's MEDIUM / 5.9 score is *generous* relative to real-world exploitability. The CVSS vector's AC:H acknowledges difficulty, but even that undersells the friction: three independent opt-in conditions must align — the developer must enable interceptors.cache(), set type: 'shared', and the upstream server must already emit non-canonical whitespace-padded directives that the attacker cannot control. No proof-of-concept code is public, no in-the-wild exploitation has been observed, and the vulnerability is not KEV-listed. The real-world population of deployments meeting all three conditions is vanishingly small, making the practical risk significantly lower than a network-reachable 5.9 suggests.

"Triple opt-in cache bug that the attacker cannot trigger — downgraded to LOW"
02 · The Attack Path

5 steps from start to impact.

STEP 01

Vulnerable undici version in use

The target application must import undici ≥7.0.0 (or ≥8.0.0) as a direct or transitive dependency. Node.js v24+ bundles undici v7, so applications on that runtime are in scope even if they never npm install undici explicitly. Earlier Node.js LTS releases (v20, v22) ship undici v6, which lacks the cache interceptor feature entirely and is not affected.
Conditions required:
  • Application runs undici 7.0.0–7.27.x or 8.0.0–8.4.x
Where this breaks in practice:
  • Node.js v20 and v22 LTS ship undici v6 — not affected
  • Cache interceptor is a v7+ feature; many production fleets are still on LTS
Detection/coverage: Run npm ls undici or check node -e "console.log(require('undici/package.json').version)" across your fleet.
STEP 02

Cache interceptor explicitly enabled in shared mode

The developer must have added interceptors.cache({ type: 'shared' }) to their undici dispatcher configuration. The cache interceptor is off by default and type defaults to 'private' when it is turned on. Only 'shared' mode is vulnerable, because RFC 9111 shared caches are designed to serve multiple users.
Conditions required:
  • Code explicitly calls interceptors.cache() with type: 'shared'
Where this breaks in practice:
  • Feature is opt-in and relatively new; adoption data suggests a tiny fraction of the >133M-weekly-download base uses it
  • Default mode is 'private', which is not affected
  • Most Node.js applications use external caching layers (Redis, CDN) rather than undici's built-in interceptor
Detection/coverage: Grep application source for interceptors.cache and inspect the type parameter.
STEP 03

Authorization headers forwarded upstream

The application must proxy or forward Authorization (or other sensitive) headers in outbound requests made through the cached dispatcher. This is typical of BFF (Backend-for-Frontend) or API gateway patterns but is not universal across all undici consumers.
Conditions required:
  • Outbound requests include Authorization headers
Where this breaks in practice:
  • Many microservice architectures use service-to-service tokens (mTLS, JWT in a separate header) rather than forwarding end-user Authorization headers
STEP 04

Upstream sends non-canonical whitespace-padded Cache-Control

The upstream origin server must emit a Cache-Control header with whitespace *inside* the quoted field-name of a qualified private or no-cache directive — e.g., private=" authorization". This is technically permitted by RFC 9110 optional whitespace (OWS) rules, but is extremely uncommon in practice. Crucially, the attacker cannot cause the upstream to emit this header; it is a server-side configuration property.
Conditions required:
  • Upstream server emits whitespace-padded qualified Cache-Control directives
Where this breaks in practice:
  • Virtually no mainstream web server, CDN, or framework emits this pattern
  • Attacker has zero control over upstream header formatting
  • Requires a specific, non-standard server configuration that most teams would never intentionally create
STEP 05

Attacker requests the same cache key

If all prior conditions are met, the authenticated response is incorrectly stored. An unauthenticated (or differently-authenticated) attacker issues a request matching the same cache key (same URL, method, and Vary-relevant headers). The cached response — containing the victim's authenticated data — is served to the attacker.
Conditions required:
  • Attacker can reach the same application endpoint
  • Request matches the stored cache key
Where this breaks in practice:
  • If Vary: Authorization is set upstream, cache keys will differ per user and the attack fails
  • Application-level access controls (API keys, session validation) may gate the endpoint independently of the cache
03 · Intelligence Metadata

The supporting signals.

In-the-wild exploitationNone observed. Not listed in CISA KEV. No campaign or threat-actor reporting references this CVE.
Proof-of-conceptNo public PoC. The advisory was reported responsibly by AndrewMohawk (Andrew MacPherson). The fix was developed by mcollina and reviewed by UlisesGascon. No exploit code has been published.
EPSSNot yet scored (disclosed 2026-07-29). Expected to be very low given the multi-condition prerequisite chain and absence of weaponization.
KEV statusNot listed. No CISA KEV entry as of 2026-07-30.
CVSS vectorCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N — Network-reachable, high attack complexity, no privileges required, high confidentiality impact, no integrity or availability impact. The AC:H reflects the multi-condition chain but still overestimates practical exploitability.
Affected versionsundici 7.0.0 – 7.27.x and 8.0.0 – 8.4.x. Node.js v20/v22 LTS ship undici v6 (not affected). Node.js v24 bundles undici v7 (potentially affected if cache interceptor is enabled in shared mode).
Fixed versionsundici 7.28.0 and undici 8.5.0. No distro backports needed — undici is an npm package, not a system library.
Scanning / exposure dataNot fingerprint-able via Shodan/Censys — undici is a client-side HTTP library, not a listening service. Exposure depends entirely on application-level code inspection. No internet-wide scan data is applicable.
Disclosure timelineReported by AndrewMohawk → patched by mcollina → advisory published 2026-07-29 via GHSA-pr7r-676h-xcf6.
Affected install baseundici has ~133M weekly npm downloads, but the cache interceptor in shared mode is an opt-in feature with negligible adoption relative to the total package footprint. Estimated vulnerable population: <0.1% of undici installs.
04 · The Call

noisgate verdict.

Final Verdict
DOWNGRADED to LOW (3.5/10)

The single most decisive factor is the triple opt-in prerequisite chain: the developer must explicitly enable the cache interceptor, explicitly set shared mode, AND the upstream server must independently emit non-canonical whitespace-padded Cache-Control directives that the attacker cannot influence. This compounding friction reduces the practically-exploitable population to well under 0.1% of undici installations, placing the real-world risk firmly in the LOW bucket.

HIGH Vulnerability mechanism and affected version ranges
HIGH Assessment that cache interceptor shared mode adoption is negligible
MEDIUM Absence of in-the-wild exploitation (CVE is <48 hours old)

Why this verdict

  • Triple opt-in chain compresses exploitable population to near zero. Three independent developer decisions — enabling the cache interceptor, selecting shared mode, and forwarding Authorization headers — must all be true simultaneously. Each is a minority configuration; their intersection is vanishingly small.
  • Attacker cannot control the critical prerequisite. The whitespace-padded Cache-Control directive must originate from the upstream server. The attacker has no mechanism to inject or modify upstream response headers, making this a passive, opportunistic flaw rather than an actively weaponizable one.
  • No exploitation evidence, no PoC, no KEV listing. The CVE was disclosed <48 hours ago with no public exploit code. The attack complexity is genuinely high even by the vendor's own assessment (AC:H), and real-world conditions make it harder still.
  • Role multiplier: undici is a general-purpose HTTP client library. The cache interceptor in shared mode is not a canonically high-value-role component (it is not an IdP, DC, hypervisor, backup system, kernel-mode agent, or network edge appliance). In theory, a Node.js-based API gateway using shared caching could leak authenticated data between users (tenant-level blast radius), but this represents <0.1% of the installed base. The high-value-role floor does not apply because the fraction of installs occupying that role is far below the 1% threshold.
  • Impact ceiling is confidentiality-only. Even in a worst-case scenario, the attacker obtains cached response bodies — no code execution, no persistence, no integrity compromise, no availability impact. Blast radius is limited to responses sharing a single cache key.

Why not higher?

The vendor's MEDIUM (5.9) already accounts for high attack complexity, but it does not adequately penalize the three independent opt-in conditions, the attacker's inability to control the upstream header, and the near-zero real-world exposure. Upgrading to MEDIUM or higher would require either active exploitation evidence, a public PoC lowering the bar, or data showing meaningful adoption of shared-mode caching — none of which exist.

Why not lower?

An IGNORE verdict would be inappropriate because the *mechanism* is real: if the conditions align, authenticated data genuinely leaks between users with no privilege required. The fix is a simple version bump, and defenders should still track it in their dependency hygiene backlog rather than pretend it doesn't exist.

05 · Compensating Control

What to do — in priority order.

  1. Add Vary: Authorization on upstream origins — If the upstream response includes Vary: Authorization, the cache key incorporates the Authorization header value and responses are never shared across users. This is the single most effective server-side mitigation and should be verified on all origins serving authenticated content. No mitigation SLA applies at LOW severity — treat as backlog hygiene.
  2. Switch cache interceptor to private mode — Change interceptors.cache({ type: 'shared' }) to interceptors.cache({ type: 'private' }) or omit the type parameter entirely (default is private). Private caches are scoped to a single user context and are not affected by this vulnerability.
  3. Disable cache interceptor for authenticated traffic — If shared caching is required for performance, configure the interceptor to bypass caching when the outbound request includes an Authorization header. This eliminates the information-disclosure vector entirely.
  4. Upgrade undici to ≥7.28.0 or ≥8.5.0 — The definitive fix. The patched versions correctly strip optional whitespace from qualified private and no-cache field names before comparison. At LOW severity, apply within the 365-day noisgate remediation window during your next dependency update cycle.
What doesn't work
  • WAF rules — the malformed whitespace is in the *upstream origin's response header*, not in the inbound request. No WAF inspects or normalizes outbound response Cache-Control directives before they reach undici's parser.
  • CDN-level caching controls — if a CDN sits in front of the Node.js application, it handles its own cache logic. The undici cache interceptor operates on the *outbound* leg (Node.js → upstream API), which CDN rules do not govern.
  • Network segmentation / firewall rules — this is an application-layer logic bug in header parsing. Network controls have no visibility into Cache-Control directive formatting.
06 · Verification

Crowdsourced verification payload.

Run this on each application host (or in CI) where your Node.js services are deployed. It checks undici's installed version and greps for shared-mode cache interceptor usage. No special privileges needed — any user who can read node_modules and the application source. Example: bash check_cve_2026_14643.sh /opt/myapp

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# check_cve_2026_14643.sh — Detect undici cache interceptor whitespace bypass
# Usage: bash check_cve_2026_14643.sh <app_root_directory>
# Exit codes: 0 = PATCHED/NOT_AFFECTED, 1 = VULNERABLE, 2 = UNKNOWN

set -euo pipefail

APP_DIR="${1:-.}"

if [ ! -d "$APP_DIR" ]; then
  echo "UNKNOWN — directory $APP_DIR does not exist"
  exit 2
fi

# --- Step 1: Find undici version ---
PKG_JSON="$APP_DIR/node_modules/undici/package.json"
if [ ! -f "$PKG_JSON" ]; then
  echo "PATCHED — undici not installed (or not in node_modules); not affected"
  exit 0
fi

VERSION=$(node -e "console.log(require('$PKG_JSON').version)" 2>/dev/null || echo "")
if [ -z "$VERSION" ]; then
  echo "UNKNOWN — could not read undici version"
  exit 2
fi

echo "[*] undici version detected: $VERSION"

# --- Step 2: Check version range ---
MAJOR=$(echo "$VERSION" | cut -d. -f1)
MINOR=$(echo "$VERSION" | cut -d. -f2)

VULN_VERSION=false
if [ "$MAJOR" -eq 7 ] && [ "$MINOR" -lt 28 ]; then
  VULN_VERSION=true
elif [ "$MAJOR" -eq 8 ] && [ "$MINOR" -lt 5 ]; then
  VULN_VERSION=true
fi

if [ "$VULN_VERSION" = false ]; then
  echo "PATCHED — undici $VERSION is not in the affected range (7.0.0-7.27.x / 8.0.0-8.4.x)"
  exit 0
fi

echo "[!] undici $VERSION is in the affected range"

# --- Step 3: Check if shared-mode cache interceptor is used in source ---
echo "[*] Scanning source for interceptors.cache() in shared mode..."
if grep -rn --include='*.js' --include='*.ts' --include='*.mjs' --include='*.cjs' \
   -E 'interceptors\.cache|cache.*shared|type.*shared' "$APP_DIR/src" "$APP_DIR/lib" "$APP_DIR/app" "$APP_DIR/server" 2>/dev/null | \
   grep -qi 'shared'; then
  echo "VULNERABLE — undici $VERSION with shared-mode cache interceptor detected in source"
  exit 1
else
  echo "PATCHED — undici $VERSION is in the affected range but no shared-mode cache interceptor usage found in source (check manually if source is elsewhere)"
  exit 0
fi
07 · Bottom Line

If you remember one thing.

TL;DR
This CVE is a LOW-severity library-level parsing quirk that requires three independent opt-in conditions and an upstream server behavior the attacker cannot control. There is no mitigation SLA at LOW severity under the noisgate framework — go straight to the 365-day noisgate remediation SLA and fold the undici upgrade (≥7.28.0 or ≥8.5.0) into your next scheduled dependency refresh cycle. If you happen to know you run interceptors.cache({ type: 'shared' }) in production, verify immediately and either switch to type: 'private' or add Vary: Authorization upstream as a same-day fix. For everyone else — and that is the vast majority of Node.js shops — log this in your dependency-hygiene backlog and move on to higher-priority work.

Sources

  1. GitHub Security Advisory GHSA-pr7r-676h-xcf6
  2. GitLab Advisory Database — undici
  3. SecurityOnline — Four undici Vulnerabilities (133M Downloads)
  4. undici on npm — version history
  5. Platformatic Blog — Bringing HTTP Caching to Node.js
  6. undici GitHub repository
  7. Socket.dev — undici package analysis
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.