← Back to Feed CACHED · 2026-09-04 17:42:48 · CACHE_KEY CVE-2026-84890
CVE-2026-84890 · CWE-770 · Disclosed 2026-01-14

undici vulnerable to Denial of Service via unbounded decompression of compressed responses

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

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.

"Server-side decompression bomb requires victim to fetch from attacker — DoS only, vendor MEDIUM is fair."
02 · The Attack Path

4 steps from start to impact.

STEP 01

Attacker operates a malicious HTTP server

The attacker stands up an HTTP endpoint that returns a response with a 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.
Conditions required:
  • Attacker controls an HTTP endpoint reachable by the victim application
Where this breaks in practice:
  • 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
Detection/coverage: Outbound proxy logs showing responses with abnormally large Content-Encoding headers (>10 entries) would be anomalous. No standard IDS signature exists for this specific pattern.
STEP 02

Victim application issues fetch() to attacker endpoint

The vulnerable Node.js application must call 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.
Conditions required:
  • Victim app accepts or follows user-influenced URLs
  • OR victim app has an SSRF vulnerability that can be redirected to the attacker endpoint
Where this breaks in practice:
  • 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
Detection/coverage: WAF rules blocking SSRF patterns; outbound request logging with URL anomaly detection.
STEP 03

Unbounded decompression chain exhausts resources

Undici's response handler creates a 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.
Conditions required:
  • Victim runs an affected undici version (< 6.23.0 or >= 7.0.0 < 7.18.2)
Where this breaks in practice:
  • 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
Detection/coverage: Node.js process monitoring (RSS spike), container OOMKill events, Kubernetes pod restart counters.
STEP 04

Denial of service achieved

The target Node.js process crashes or becomes unresponsive. In a single-process deployment this means downtime until restart. In a clustered deployment (PM2 cluster, Kubernetes Deployment with replicas), only the affected worker restarts. The attacker achieves a transient DoS — not persistent, not data-affecting.
Conditions required:
  • No resource limits or process supervisors in place for prolonged impact
Where this breaks in practice:
  • Kubernetes liveness probes and replica sets limit downtime to seconds
  • The attack must be repeated for each process — there is no amplification across hosts
Detection/coverage: APM alerts on process restarts, 5xx error rate spikes, pod CrashLoopBackOff alerts.
03 · Intelligence Metadata

The supporting signals.

In-the-Wild ExploitationNone observed. Not listed on CISA KEV. No reports from threat intel vendors of active campaigns exploiting this vector.
Proof-of-Concept1 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 StatusNot listed. No federal mandate to patch on an accelerated timeline.
CVSS VectorCVSS: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 Versionsundici < 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 Versionsundici 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 / ExposureNot 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 Date2026-01-14 (GitHub Security Advisory GHSA-g9mf-h72j-4rw9). Fix released same day.
Reporting Researcherillia-v (HackerOne). Fix by mcollina (Node.js core maintainer). Related to curl CVE-2022-32206 (same decompression-chain class).
04 · The Call

noisgate verdict.

Final Verdict
= UNCHANGED to MEDIUM (5.9/10)

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.

HIGH Vulnerability mechanics and affected versions
HIGH Severity assessment — vendor MEDIUM aligns with real-world risk
MEDIUM Exploit adoption likelihood — trivial PoC but niche attack surface

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:H reflects 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.

05 · Compensating Control

What to do — in priority order.

  1. 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.
  2. 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-Encoding tokens. This neutralizes the bug without upgrading. Useful as a stopgap if you cannot immediately bump the dependency.
  3. 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.
  4. Upgrade undici to 6.23.0+ or 7.18.2+ — The definitive fix. Run npm audit fix or pin the patched version in package.json. For Node.js core bundled undici, upgrade to the Node.js release that ships the patched version — verify with node -e 'console.log(process.versions.undici)'.
What doesn't work
  • 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-size flag — zlib native allocations live outside the V8 heap. The V8 heap limit does not constrain the decompression memory usage.
06 · Verification

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

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/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 ;;
esac
07 · Bottom Line

If you remember one thing.

TL;DR
This is a legitimate but situational DoS bug in undici's HTTP decompression handling. At MEDIUM / 5.9 unchanged, there is no noisgate mitigation SLA — go straight to the noisgate remediation SLA of 365 days. Practically, run 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

  1. GitHub Advisory — GHSA-g9mf-h72j-4rw9 (CVE-2026-22036)
  2. undici Security Advisory — Unbounded Decompression Chain
  3. GitLab Advisory Database — CVE-2026-22036
  4. IBM Security Bulletin — Decompression Bomb in undici affecting watsonx.data
  5. SentinelOne Vulnerability Database — CVE-2026-22036
  6. CVEFeed — CVE-2026-22036 Detail
  7. Debian Package Tracker — node-undici
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.