Like hanging up on someone mid-sentence, then pretending the new caller is the same person while the first one waits on hold forever
CVE-2026-18149 is a resource-leak flaw in undici's retry handler (CWE-772). When a server sends a partial response with a declared Content-Length, closes the connection, and the retry handler retries the request but receives a non-retryable status (e.g. 400), the handler replaces the internal stream with the new response but never ends or destroys the original response body the application is still holding. The body timeout never fires because its timer is bound to the connection parser, not the orphaned stream. Affected versions are undici 7.11.0 through 7.29.0 and 8.0.0 through 8.10.1. Fixed in 7.29.1 and 8.10.2.
The vendor's MEDIUM / 5.9 rating is honest and arguably slightly generous. The CVSS vector sets Attack Complexity to High (AC:H) because the attacker must control the HTTP server the victim application connects to, the retry interceptor must be explicitly configured (it is not on by default), and the impact ceiling is availability-only — no confidentiality or integrity impact. In real-world terms, this narrows the exploitable population to Node.js services that (a) use undici’s retry interceptor and (b) fetch URLs from untrusted or user-supplied origins. That’s a meaningful subset but far from universal.
4 steps from start to impact.
Victim app fetches attacker-controlled URL via undici with retry interceptor
- Application uses undici 7.11.0–7.29.0 or 8.0.0–8.10.1
- Retry interceptor is explicitly enabled
- Application fetches attacker-controlled URL
- Retry interceptor is opt-in, not enabled by default
- Many Node.js apps use axios, node-fetch, or got instead of raw undici retry
- Server-side request targets are often allowlisted in security-conscious deployments
Attacker sends partial response then closes connection
- Attacker controls the HTTP server endpoint
- Requires only two short responses; no persistent connection needed
Retry returns non-retryable status; original stream orphaned
- Retry handler receives non-retryable status on second attempt
- Application must be actively reading the original response body for the orphan to matter
Orphaned streams and promises accumulate; application DoS
- Attacker can trigger multiple requests to the malicious server
- Rate limiting on outbound requests would slow accumulation
- Process supervisors (PM2, systemd) will restart crashed processes, limiting sustained impact
- Horizontal scaling behind a load balancer dilutes per-instance impact
--max-old-space-size limits will trigger OOM kills.The supporting signals.
| In-the-Wild Exploitation | No known exploitation. Not listed in CISA KEV. No public reports of active campaigns as of 2026-09-05. |
|---|---|
| Proof-of-Concept | No public PoC found. The advisory description provides enough detail to construct a triggering server, but no named researcher has published exploit code. |
| EPSS Score | Not yet scored. CVE was disclosed 2026-09-04; EPSS data typically populates within 1–3 days. Expected to be low given AC:H and DoS-only impact. |
| KEV Status | Not listed. No CISA KEV entry as of 2026-09-05. |
| CVSS Vector | CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H — Network-reachable but high complexity, no auth required, availability-only impact. Scope unchanged. |
| Affected Versions | undici 7.11.0 – 7.29.0 and 8.0.0 – 8.10.1. Earlier major versions (v5, v6) are not affected. |
| Fixed Versions | undici 7.29.1 and 8.10.2. Node.js core bundles undici; check node -e "require('undici/package.json').version" for the bundled version. |
| Exposure Data | undici has ~7,700+ direct dependents on npm and is bundled into Node.js core since v18. However, the retry interceptor is opt-in, limiting the exploitable subset. |
| Disclosure Date | 2026-09-04 via GitHub Security Advisory (GHSA). |
| Reporter | Reported via the OpenJS Foundation / Node.js security process. Specific researcher not named in public advisory. |
noisgate verdict.
The single most decisive factor is that exploitation requires the victim application to make outbound HTTP requests to an attacker-controlled server with the opt-in retry interceptor enabled, which compounds two prerequisites that exclude the vast majority of undici deployments. The impact ceiling is process-level denial of service with no path to code execution, data exfiltration, or lateral movement.
Why this verdict
- Attacker-controlled server prerequisite: The attacker must control the HTTP endpoint the victim connects to. This is not a listening-service vulnerability — the application must reach out to a malicious origin, which requires SSRF-like functionality or user-supplied URL fetching.
- Opt-in retry interceptor: undici’s retry handler is not enabled by default. Applications must explicitly configure it, narrowing the vulnerable population to a subset of undici users.
- Availability-only impact: The CVSS vector confirms C:N/I:N/A:H. There is no path from this bug to code execution, privilege escalation, or data compromise. Process restarts fully remediate the DoS condition.
- Role multiplier: undici is bundled in Node.js and used in CI/CD runners, API gateways, and production services. In high-value roles (CI/CD fetching package URLs, API gateways proxying upstream), a successful DoS would disrupt a single process instance. The blast radius is host-level at worst (process crash), not domain/fleet/supply-chain scale. No role produces an outcome above application-level DoS, so no floor override applies.
- No exploitation evidence: No KEV listing, no public PoC, no campaign reports. EPSS is expected to be low.
Why not higher?
There is no path to code execution, privilege escalation, or data exfiltration. The impact is strictly availability and limited to the process level — a crashed Node.js process restarts in seconds via PM2 or systemd. The attacker cannot leverage this for lateral movement or persistence, and the two compounding prerequisites (attacker-controlled server + opt-in retry config) dramatically narrow the exploitable population.
Why not lower?
The vulnerability is still network-reachable (AV:N) and requires no authentication or user interaction. In applications that do fetch untrusted URLs with the retry interceptor (webhook handlers, URL preview services, RSS aggregators), the attack is low-effort for the attacker once preconditions are met. A sustained DoS against a single-instance service could cause real downtime. The 5.0 score acknowledges this residual risk.
What to do — in priority order.
- Disable or remove the retry interceptor where not business-critical — If your application does not strictly need undici’s retry interceptor, removing it from the dispatcher chain eliminates the vulnerability entirely. Audit your codebase for
new RetryHandler()orretry:in dispatcher options. No mitigation SLA applies at MEDIUM — go straight to remediation. - Allowlist outbound request targets — Restrict the set of URLs your application can fetch to known-good origins. Use an allowlist of domains or IP ranges for any user-influenced URL fetching (webhooks, previews, imports). This blocks the attacker-controlled-server prerequisite.
- Set aggressive timeouts and concurrency limits on outbound HTTP — Configure
headersTimeout,bodyTimeout, andmaxConcurrentStreamson undici pools. While the body timeout doesn’t fire for orphaned streams (that’s the bug), overall pool exhaustion limits will cap the blast radius. - Upgrade undici to 7.29.1 or 8.10.2 — The definitive fix. Run
npm audit fixor pin the patched version directly. For Node.js core bundled undici, upgrade Node.js to the next patch release that bundles the fix. Target completion within the noisgate 365-day remediation SLA for MEDIUM.
- WAF / reverse proxy in front of the application — This is an outbound request vulnerability. Inbound WAF rules do not inspect or control the application’s outgoing HTTP behavior.
- Body timeout configuration — The advisory explicitly states the body timeout does not fire because the timer is tied to the connection parser, not the orphaned stream. Increasing or decreasing this value has no effect on the vulnerable code path.
- Node.js
--max-old-space-size— While this will eventually OOM-kill the process (limiting blast radius), it does not prevent the vulnerability from being triggered and will cause a hard crash rather than graceful degradation.
Crowdsourced verification payload.
Run this on any host where a Node.js application using undici is deployed. Execute as the application user or any user with read access to node_modules. Example: bash check_cve_2026_18149.sh /opt/myapp
#!/usr/bin/env bash
# check_cve_2026_18149.sh - Check for CVE-2026-18149 (undici retry handler resource leak)
# Usage: bash check_cve_2026_18149.sh <project_dir>
# Exit codes: 0 = PATCHED, 1 = VULNERABLE, 2 = UNKNOWN
set -euo pipefail
PROJECT_DIR="${1:-.}"
RESULT="UNKNOWN"
if ! command -v node &>/dev/null; then
echo "UNKNOWN - node not found in PATH"
exit 2
fi
# Check bundled undici in node_modules
PKG="${PROJECT_DIR}/node_modules/undici/package.json"
if [ -f "$PKG" ]; then
VERSION=$(node -e "console.log(require('${PKG}').version)" 2>/dev/null || echo "")
else
# Fall back to Node.js built-in undici
VERSION=$(node -e "try{console.log(require('undici/package.json').version)}catch(e){console.log('')}" 2>/dev/null || echo "")
fi
if [ -z "$VERSION" ]; then
echo "UNKNOWN - undici not found in ${PROJECT_DIR} or Node.js built-in"
exit 2
fi
echo "Detected undici version: ${VERSION}"
# Parse major.minor.patch
IFS='.' read -r MAJOR MINOR PATCH <<< "$VERSION"
# Affected: 7.11.0 - 7.29.0, 8.0.0 - 8.10.1
# Fixed: 7.29.1+, 8.10.2+
if [ "$MAJOR" -eq 7 ]; then
if [ "$MINOR" -lt 11 ]; then
RESULT="PATCHED" # Below affected range
elif [ "$MINOR" -lt 29 ]; then
RESULT="VULNERABLE"
elif [ "$MINOR" -eq 29 ] && [ "$PATCH" -eq 0 ]; then
RESULT="VULNERABLE"
else
RESULT="PATCHED"
fi
elif [ "$MAJOR" -eq 8 ]; then
if [ "$MINOR" -lt 10 ]; then
RESULT="VULNERABLE"
elif [ "$MINOR" -eq 10 ] && [ "$PATCH" -lt 2 ]; then
RESULT="VULNERABLE"
else
RESULT="PATCHED"
fi
else
RESULT="PATCHED" # v5, v6, v9+ not affected
fi
echo "$RESULT"
if [ "$RESULT" = "VULNERABLE" ]; then
exit 1
elif [ "$RESULT" = "PATCHED" ]; then
exit 0
else
exit 2
fiIf you remember one thing.
npm audit across your fleet to identify affected projects. For the majority of Node.js deployments that don’t use the retry interceptor or don’t fetch untrusted URLs, this CVE is low-urgency backlog work — bundle it into your next quarterly dependency refresh.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.