Like mailing someone a zip bomb, except they have to voluntarily download your package first
CVE-2026-84890 (tracked publicly as CVE-2026-22036) affects the undici HTTP client bundled with Node.js. The fetch() API honors chained Content-Encoding headers per RFC 9110 (e.g., gzip, br, gzip, …) but places no upper bound on the chain depth. A malicious HTTP server can return a response with thousands of compression layers, forcing the client to allocate a decompression stream per layer — burning CPU and RAM until the process crashes. Affected versions: undici < 6.23.0 and >= 7.0.0, < 7.18.2. Because undici ships inside Node.js core, this implicitly touches Node.js 20.x, 22.x (v6 line) and 24.x (v7 line) until patched releases landed.
The vendor-assigned MEDIUM / 5.9 is accurate. The CVSS vector flags AC:H — high attack complexity — because the attacker must *be* the HTTP server the victim application connects to (or redirect the victim via SSRF). That is a meaningful prerequisite: in most enterprise deployments, outbound HTTP targets are either allow-listed or internal. The impact ceiling is Availability only — no data leaks, no code execution, no integrity violation. A process supervisor (systemd, PM2, Kubernetes liveness probe) restarts the crashed worker in seconds. This is a real bug that deserves patching, but it is not the kind of bug that wakes up the SOC at 2 AM.
4 steps from start to impact.
Attacker operates a malicious HTTP server
Content-Encoding header containing thousands of chained compression algorithms (e.g., gzip, deflate, gzip, deflate, … repeated ~4,000 times within the default 16 KB maxHeaderSize). The response body is a small, validly compressed payload. No special tooling is required beyond a custom HTTP server script.- Attacker controls an HTTP endpoint reachable by the victim application
- Enterprise egress proxies and allow-lists limit which external hosts internal apps can reach
- Most server-to-server calls target known internal or SaaS endpoints, not arbitrary URLs
Victim application issues fetch() to attacker endpoint
fetch() (or undici's request()) targeting the attacker-controlled URL. This could happen via user-supplied URLs in webhooks, SSRF, open redirects, or URL-preview features. The application does not need to be authenticated — the attacker just needs the app to issue a GET/POST.- Victim app accepts or follows user-influenced URLs
- OR victim app has an SSRF vulnerability that can be redirected to the attacker endpoint
- Well-architected services validate and allowlist outbound URLs
- SSRF protections (SSRF-guard libraries, metadata endpoint blocking) reduce the attack surface
- Internal-only services behind NAT/firewall never reach attacker infrastructure
Unbounded decompression chain exhausts resources
Transform stream for each Content-Encoding token. With thousands of tokens, Node.js allocates thousands of zlib/brotli stream objects. Memory usage spikes into gigabytes and CPU saturates decompressing the chain, causing the process to become unresponsive or crash with an OOM error. The V8 heap limit does not cap native zlib allocations.- Victim runs an affected undici version (< 6.23.0 or >= 7.0.0 < 7.18.2)
- Container memory limits (cgroups) and Kubernetes OOMKill will terminate the process before host-level impact
- Process managers restart the worker within seconds, limiting blast duration
Denial of service achieved
- No resource limits or process supervisors in place for prolonged impact
- Kubernetes liveness probes and replica sets limit downtime to seconds
- The attack must be repeated for each process — there is no amplification across hosts
The supporting signals.
| In-the-Wild Exploitation | None observed. Not listed on CISA KEV. No reports from threat intel vendors of active campaigns exploiting this vector. |
|---|---|
| Proof-of-Concept | 1 public PoC on GitHub (analysis gist by Darkcrai86). The exploit is trivial — a ~20-line HTTP server returning a crafted Content-Encoding header. Reported by illia-v via HackerOne (report #3456148). |
| EPSS Score | ~0.43% probability of exploitation in next 30 days — low. Consistent with a DoS-only, client-side bug requiring victim-initiated connections. |
| KEV Status | Not listed. No federal mandate to patch on an accelerated timeline. |
| CVSS Vector | CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H — Network-accessible but high complexity (attacker must be the server). Availability-only impact, no scope change. |
| Affected Versions | undici < 6.23.0 (ships in Node.js 20.x/22.x) and >= 7.0.0, < 7.18.2 (ships in Node.js 24.x). The fetch() global and undici.request() are both affected. |
| Fixed Versions | undici 6.23.0 and 7.18.2. Node.js releases incorporating the fix: check with process.versions.undici at runtime. Distro backports: check Debian node-undici tracker. |
| Scanning / Exposure | Not directly scannable via Shodan/Censys — this is a client-side library vulnerability, not an exposed service. Software composition analysis (SCA) tools (Snyk, npm audit, Trivy) flag this in dependency trees. |
| Disclosure Date | 2026-01-14 (GitHub Security Advisory GHSA-g9mf-h72j-4rw9). Fix released same day. |
| Reporting Researcher | illia-v (HackerOne). Fix by mcollina (Node.js core maintainer). Related to curl CVE-2022-32206 (same decompression-chain class). |
noisgate verdict.
The single most decisive factor is the reversed attack direction: the attacker must be the HTTP server, meaning the victim application must initiate an outbound connection to attacker-controlled infrastructure — a prerequisite that drastically narrows the reachable population to apps processing user-supplied URLs. The impact ceiling is a transient, single-process DoS with no data confidentiality or integrity consequences.
Why this verdict
- Reversed attack direction narrows exposure. Unlike a typical server-side vuln, the attacker must *be* the HTTP server. The victim app must voluntarily fetch from the malicious endpoint. This limits exploitation to apps that follow user-supplied URLs, have SSRF bugs, or process webhooks from untrusted sources — a subset of Node.js deployments.
- Availability-only impact with no escalation path. The chain ends at a process crash. There is no code execution, no data exfiltration, no lateral movement. Process managers restart workers in seconds, limiting blast duration to a transient outage.
- AC:H is real, not cosmetic. The CVSS
AC:Hreflects a genuine prerequisite — the attacker cannot spray this at arbitrary targets. They need the target to come to them. Enterprise egress controls (proxy allow-lists, SSRF guards) further reduce the reachable population. - Role multiplier: undici is bundled in Node.js and runs everywhere Node runs — workstations, API servers, CI/CD runners, microservices. In CI/CD roles (GitHub Actions self-hosted runners, Jenkins agents fetching dependencies), a decompression bomb would crash the build process, causing a failed build — annoying but not supply-chain compromise (no code injection, no artifact tampering). In API gateway or webhook processor roles, it causes a transient pod restart. In no high-value role does this chain produce domain takeover, fleet compromise, mass data egress, or supply-chain pivot. The worst outcome across all roles is temporary service disruption — the blast radius stays at single-process / single-pod scope.
Why not higher?
Elevating to HIGH would require either active exploitation, a path to code execution, or a blast radius beyond a single process. None of those conditions exist. The reversed attack direction (victim must connect to attacker) means the reachable population is a fraction of Node.js deployments — those that fetch user-influenced URLs. No high-value-role scenario produces domain-scale or fleet-scale impact.
Why not lower?
Dropping to LOW would understate the risk for the subset of applications that *do* process untrusted URLs (webhook receivers, URL-preview services, SSRF-prone apps). The PoC is trivial and public, and undici's ubiquity in Node.js means the vulnerable code is present on a very large number of hosts even if most are not directly exploitable. A 5.9 MEDIUM correctly captures 'real but situational' risk.
What to do — in priority order.
- Validate and allowlist outbound URLs in application code — The attack requires the victim to fetch from a malicious server. Restricting outbound
fetch()targets to known-good domains eliminates the attack path entirely. Implement this as part of your standard SSRF prevention controls. No mitigation SLA applies at MEDIUM — go straight to the 365-day remediation window. - Apply undici interceptor to cap Content-Encoding chain depth — The advisory documents a workaround: register a custom undici interceptor that rejects responses with more than ~10
Content-Encodingtokens. This neutralizes the bug without upgrading. Useful as a stopgap if you cannot immediately bump the dependency. - Enforce container memory limits and liveness probes — Set Kubernetes memory limits and liveness/readiness probes so that a memory-exhaustion crash is contained to a single pod and auto-heals in seconds. This does not prevent the bug but limits blast radius to a transient restart.
- Upgrade undici to 6.23.0+ or 7.18.2+ — The definitive fix. Run
npm audit fixor pin the patched version inpackage.json. For Node.js core bundled undici, upgrade to the Node.js release that ships the patched version — verify withnode -e 'console.log(process.versions.undici)'.
- WAF / reverse proxy in front of the Node.js app — the malicious Content-Encoding header is in the *response* from the attacker's server, not in the inbound *request* to your app. Your WAF never sees it.
- Rate limiting inbound requests — the attack payload is an outbound fetch response, not an inbound flood. Rate limiting your API does not help.
- Node.js
--max-old-space-sizeflag — zlib native allocations live outside the V8 heap. The V8 heap limit does not constrain the decompression memory usage.
Crowdsourced verification payload.
Run this on any host where Node.js applications are deployed. It checks the installed undici version in node_modules and via the Node.js built-in. Requires read access to the project directory. Example: bash check_cve_2026_84890.sh /opt/myapp
#!/usr/bin/env bash
# check_cve_2026_84890.sh — Detect undici decompression chain vulnerability
# Usage: bash check_cve_2026_84890.sh [/path/to/node/project]
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN
set -euo pipefail
PROJECT_DIR="${1:-.}"
RESULT="UNKNOWN"
# Helper: compare semver (returns 0 if $1 >= $2)
ver_gte() {
printf '%s\n%s' "$2" "$1" | sort -t. -k1,1n -k2,2n -k3,3n -C 2>/dev/null
}
# Check 1: Node.js bundled undici
if command -v node &>/dev/null; then
BUNDLED=$(node -e 'console.log(process.versions.undici || "none")' 2>/dev/null || echo "none")
if [ "$BUNDLED" != "none" ]; then
echo "[*] Node.js bundled undici version: $BUNDLED"
MAJOR=$(echo "$BUNDLED" | cut -d. -f1)
if [ "$MAJOR" -ge 7 ]; then
if ver_gte "$BUNDLED" "7.18.2"; then
echo "[+] Bundled undici >= 7.18.2 — PATCHED"
RESULT="PATCHED"
else
echo "[-] Bundled undici < 7.18.2 — VULNERABLE"
RESULT="VULNERABLE"
fi
elif [ "$MAJOR" -eq 6 ]; then
if ver_gte "$BUNDLED" "6.23.0"; then
echo "[+] Bundled undici >= 6.23.0 — PATCHED"
RESULT="PATCHED"
else
echo "[-] Bundled undici < 6.23.0 — VULNERABLE"
RESULT="VULNERABLE"
fi
else
echo "[?] Bundled undici major version $MAJOR — not in affected range"
RESULT="PATCHED"
fi
else
echo "[*] No bundled undici reported by Node.js (older Node version)"
fi
fi
# Check 2: npm dependency undici in project
PKG="$PROJECT_DIR/node_modules/undici/package.json"
if [ -f "$PKG" ]; then
DEP_VER=$(grep '"version"' "$PKG" | head -1 | sed 's/[^0-9.]//g')
echo "[*] Project undici dependency version: $DEP_VER"
DEP_MAJOR=$(echo "$DEP_VER" | cut -d. -f1)
if [ "$DEP_MAJOR" -ge 7 ]; then
if ver_gte "$DEP_VER" "7.18.2"; then
echo "[+] Dependency undici >= 7.18.2 — PATCHED"
[ "$RESULT" != "VULNERABLE" ] && RESULT="PATCHED"
else
echo "[-] Dependency undici < 7.18.2 — VULNERABLE"
RESULT="VULNERABLE"
fi
elif [ "$DEP_MAJOR" -eq 6 ]; then
if ver_gte "$DEP_VER" "6.23.0"; then
echo "[+] Dependency undici >= 6.23.0 — PATCHED"
[ "$RESULT" != "VULNERABLE" ] && RESULT="PATCHED"
else
echo "[-] Dependency undici < 6.23.0 — VULNERABLE"
RESULT="VULNERABLE"
fi
fi
else
echo "[*] No undici found in $PROJECT_DIR/node_modules/"
fi
echo ""
echo "=== RESULT: $RESULT ==="
case "$RESULT" in
PATCHED) exit 0 ;;
VULNERABLE) exit 1 ;;
*) exit 2 ;;
esacIf you remember one thing.
npm audit across your Node.js fleet Monday morning and add this to your next quarterly dependency-update cycle. Prioritize patching first on any service that fetches user-supplied URLs (webhook processors, URL-preview services, open proxy endpoints) — those are the only apps directly exploitable. For everything else, bundle the fix into your next scheduled Node.js minor-version upgrade. If you run containerized workloads, confirm your pods have memory limits and liveness probes so that even an unpatched crash self-heals in seconds.Sources
- GitHub Advisory — GHSA-g9mf-h72j-4rw9 (CVE-2026-22036)
- undici Security Advisory — Unbounded Decompression Chain
- GitLab Advisory Database — CVE-2026-22036
- IBM Security Bulletin — Decompression Bomb in undici affecting watsonx.data
- SentinelOne Vulnerability Database — CVE-2026-22036
- CVEFeed — CVE-2026-22036 Detail
- Debian Package Tracker — node-undici
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.