Like leaving a gap between two train cars — only passengers walking between them at the exact wrong moment fall through
CVE-2026-18540 is a response-splitting flaw in undici's interceptors.retry() code path. When the retry interceptor resumes a partial HTTP response (e.g., after a socket close), it appends the resumed bytes to the already-delivered body without updating the original Content-Length header. An attacker who controls (or compromises) the upstream origin can craft a 404 Not Found with Content-Length: 2, send one byte, close the socket, and let the retry logic resume via a Range request — injecting additional bytes that spill past the declared boundary. If the calling application is a forwarding proxy or gateway that relays headers verbatim, the downstream HTTP parser sees extra bytes as the start of a new response, enabling classic HTTP response smuggling. Affected versions are undici < 6.28.1, 7.0.0–7.29.0, and 8.0.0–8.10.1. Patched in 6.28.1, 7.29.1, and 8.10.2.
The vendor rated this LOW / 3.7 and that score is honest. Three conditions must hold simultaneously: (1) the retry interceptor is explicitly enabled, (2) the attacker controls or influences the upstream origin, and (3) the downstream application forwards Content-Length without recalculation. Most undici consumers are outbound HTTP clients in server-side apps that consume responses locally — they never relay raw headers downstream. The proxy/gateway pattern where this matters is a small fraction of the 175M-weekly-download install base, and the impact ceiling is integrity-only (no confidentiality, no RCE). The vendor's LOW is appropriate.
4 steps from start to impact.
Attacker controls upstream origin
- Attacker controls or influences the upstream HTTP server responding to the undici client
- In most architectures the upstream origin is a trusted internal service or well-known third-party API — not attacker-controlled
- MITM requires TLS interception or a compromised network path
Retry interceptor enabled and triggers resume
interceptors.retry() on its undici client. The attacker sends a partial response (e.g., Content-Length: 2 but only 1 byte delivered), then closes the socket. The retry interceptor fires a Range-based resume request, appending additional bytes to the already-in-flight body without updating Content-Length.- Application has
interceptors.retry()enabled (not default behavior — must be explicitly configured) - Upstream response triggers a retry condition (socket close mid-body)
- Retry interceptor is opt-in; most undici users rely on default fetch/request without retry middleware
- Retry logic may be configured with
maxRetries: 0or limited to idempotent methods only
interceptors.retry() usage in application sourceDownstream forwarding without Content-Length recalculation
Content-Length causes the downstream HTTP parser to interpret trailing bytes as a new response, achieving response splitting/smuggling.- Application forwards upstream response headers (including
Content-Length) without stripping or recalculating them - Downstream connection is keep-alive (HTTP/1.1 pipelining or connection reuse)
- Most Node.js proxy frameworks (http-proxy, fastify-reply-from, Next.js SSR) recalculate
Content-Lengthor useTransfer-Encoding: chunked - HTTP/2 downstream eliminates classical response splitting since framing is binary, not length-delimited
Response smuggling impact
Set-Cookie, or serving malicious content to a different user sharing the same downstream connection.- Downstream client or intermediary (CDN, load balancer) reuses the poisoned connection for subsequent requests
- Modern CDNs and load balancers have their own response validation and framing checks
- Impact is limited to integrity — no code execution, no direct credential theft
The supporting signals.
| In-the-Wild Exploitation | None observed. No reports of active exploitation. Not listed in CISA KEV. |
|---|---|
| Proof of Concept | No public PoC exploit. The advisory description from reporter samuel871211 outlines the theoretical attack path but no weaponized tooling exists. |
| EPSS Score | Not yet scored (advisory published 2026-09-04, EPSS lag expected). Likely to land in the bottom 10th percentile given LOW severity and high attack complexity. |
| KEV Status | Not listed. No CISA KEV entry. |
| CVSS Vector | CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N — Network-reachable but High complexity, no privileges needed, integrity-only impact. No confidentiality or availability impact. |
| Affected Versions | undici < 6.28.1, 7.0.0 – 7.29.0, 8.0.0 – 8.10.1 |
| Fixed Versions | 6.28.1, 7.29.1, 8.10.2 |
| Scanning / Exposure | undici has ~175M weekly npm downloads. However, the vulnerable code path (retry interceptor in proxy mode) represents a small fraction of deployments. No Shodan/GreyNoise exposure surface — this is a library, not a listening service. |
| Disclosure Date | 2026-09-04 (GHSA-r53p-7pc4-xj5r published) |
| Reporter | samuel871211 (reporter); mcollina (remediation developer); UlisesGascon (reviewer) |
noisgate verdict.
The single most decisive factor is the triple-condition chain: exploitation requires attacker-controlled upstream, explicitly enabled retry interceptor, AND verbatim header forwarding — a combination that describes a vanishingly small subset of the 175M-weekly-download install base. The integrity-only impact ceiling (no RCE, no confidentiality breach) further confirms the vendor's LOW rating.
Why this verdict
- Triple prerequisite chain compresses reachable population. Three independent conditions — attacker-controlled upstream, retry interceptor enabled, verbatim header forwarding — must all hold. Each condition independently eliminates the majority of undici deployments.
- Integrity-only impact with no escalation path. Even when the chain succeeds, the outcome is HTTP response desynchronization — cache poisoning or injected headers. There is no path to RCE, credential theft, or lateral movement from this flaw alone.
- Role multiplier: low blast radius across deployment roles. undici is predominantly used as an outbound HTTP client in server-side Node.js apps. (a) *Low-value role*: CLI tools, dev servers, scripts — not affected (no downstream forwarding). (b) *Typical role*: API backends consuming upstream services — consume responses locally, never relay raw headers. (c) *High-value role*: Node.js-based reverse proxies or API gateways — this is where the chain could succeed, but these represent <5% of undici installs, and the blast radius is limited to downstream response corruption on that single proxy's connections (not domain/fleet/supply-chain scale). No high-value-role floor trigger applies.
- No exploitation evidence or tooling. No PoC, no KEV listing, no observed campaigns. The advisory is 1 day old with zero community weaponization.
Why not higher?
Upgrading to MEDIUM or above would require either a broader reachable population or a more severe impact. The attack demands the attacker already control the upstream origin — if they have that, they can serve arbitrary malicious content directly, making this smuggling vector a marginal incremental risk. The integrity-only impact with no confidentiality or availability component, combined with the opt-in retry interceptor requirement, keeps this firmly in LOW territory.
Why not lower?
IGNORE would understate the risk for the small population of Node.js-based forwarding proxies that do use retry interceptors. HTTP response smuggling is a well-understood attack class (CWE-444) with real consequences when conditions align — cache poisoning can affect many downstream users. The flaw is real, patched, and deserves tracking even if urgency is minimal.
What to do — in priority order.
- Disable the retry interceptor on proxy/gateway workloads — Set
maxRetries: 0or removeinterceptors.retry()from undici client configuration in any application that forwards responses downstream. This eliminates the vulnerable code path entirely. No mitigation SLA applies for LOW severity — go straight to remediation. - Recalculate or strip Content-Length before forwarding — If your Node.js proxy forwards upstream responses, ensure you delete the
Content-Lengthheader and useTransfer-Encoding: chunked, or recalculateContent-Lengthfrom the actual body bytes. This breaks the smuggling vector regardless of undici version. - Use HTTP/2 for downstream connections — HTTP/2's binary framing eliminates classical Content-Length-based response splitting. If your proxy serves downstream clients over HTTP/2 (h2 or h2c), the smuggling payload cannot desynchronize the connection.
- WAF signature for request smuggling — this is *response* splitting, not request smuggling. Most WAF CL/TE desync rules inspect inbound requests, not outbound responses from your own proxy.
- Upgrading Node.js without upgrading undici — Node.js bundles undici but application-level
npm install undicitakes precedence. You must upgrade the explicit undici dependency in yourpackage.json, not just the Node.js runtime.
Crowdsourced verification payload.
Run this on any host where the target Node.js application is deployed, or in your CI pipeline. Requires access to the application's node_modules directory. Example: bash check_cve_2026_18540.sh /app (pass the project root as the first argument). No elevated privileges needed.
#!/usr/bin/env bash
# check_cve_2026_18540.sh — Detect undici versions vulnerable to CVE-2026-18540
# Usage: bash check_cve_2026_18540.sh /path/to/project
set -euo pipefail
PROJECT_DIR="${1:-.}"
PKG="$PROJECT_DIR/node_modules/undici/package.json"
if [ ! -f "$PKG" ]; then
echo "UNKNOWN — undici not found in $PROJECT_DIR/node_modules"
exit 2
fi
VERSION=$(grep '"version"' "$PKG" | head -1 | sed 's/.*"version": *"\([^"]*\)".*/\1/')
MAJOR=$(echo "$VERSION" | cut -d. -f1)
MINOR=$(echo "$VERSION" | cut -d. -f2)
PATCH=$(echo "$VERSION" | cut -d. -f3)
echo "Detected undici version: $VERSION"
vulnerable=0
# Branch 6.x: vulnerable if < 6.28.1
if [ "$MAJOR" -lt 6 ]; then
vulnerable=1
elif [ "$MAJOR" -eq 6 ]; then
if [ "$MINOR" -lt 28 ]; then
vulnerable=1
elif [ "$MINOR" -eq 28 ] && [ "$PATCH" -lt 1 ]; then
vulnerable=1
fi
# Branch 7.x: vulnerable if 7.0.0 – 7.29.0
elif [ "$MAJOR" -eq 7 ]; then
if [ "$MINOR" -lt 29 ]; then
vulnerable=1
elif [ "$MINOR" -eq 29 ] && [ "$PATCH" -lt 1 ]; then
vulnerable=1
fi
# Branch 8.x: vulnerable if 8.0.0 – 8.10.1
elif [ "$MAJOR" -eq 8 ]; then
if [ "$MINOR" -lt 10 ]; then
vulnerable=1
elif [ "$MINOR" -eq 10 ] && [ "$PATCH" -lt 2 ]; then
vulnerable=1
fi
fi
if [ "$vulnerable" -eq 1 ]; then
echo "VULNERABLE — undici $VERSION is affected by CVE-2026-18540"
# Check if retry interceptor is used in the project
if grep -rq 'interceptors\.retry\|interceptors\.retry(' "$PROJECT_DIR/src" "$PROJECT_DIR/lib" "$PROJECT_DIR/app" 2>/dev/null; then
echo "WARNING — retry interceptor usage detected in source code"
else
echo "NOTE — no retry interceptor usage found in common source directories (reduced risk)"
fi
exit 1
else
echo "PATCHED — undici $VERSION is not affected"
exit 0
fiIf you remember one thing.
npm install [email protected] or the appropriate branch) into your next scheduled dependency update cycle. If you operate a Node.js-based reverse proxy or API gateway that uses interceptors.retry(), bump priority slightly and confirm your forwarding layer recalculates Content-Length. For the vast majority of teams consuming undici as an outbound HTTP client, this CVE requires no emergency action — update when convenient within your normal noisgate remediation SLA cadence and move on.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.