Node.js 22.23.4, 24.22.1 and 26.12.1 fix CVE-2026-111497, a Brotli decoder overflow that can lead to RCE. Who is exposed, how to check, what to do.

On Wednesday, October 21, the Node.js project shipped security releases for all three supported lines: v26.12.1 (Current), v24.22.1 (Active LTS, "Krypton") and v22.23.4. Together they close 10 CVEs. One of them matters more than the other nine combined.
CVE-2026-111497 sits in the Brotli decoder that ships inside Node.js. The official advisory explains the bug in two sentences: "An invalid block switch was reported as a temporary need for more input instead of a permanent format error, so the streaming decoder kept appending input bytes to an eight-byte internal buffer." In plain terms, malformed compressed data tells the decoder to keep waiting, and every byte that arrives while it waits is written past the end of a tiny buffer.
According to Node.js, an attacker who can feed Brotli data to anything that decompresses it with node:zlib can corrupt process memory. The outcome ranges from a crash, to leaking process memory, to remote code execution. Node.js rates it HIGH. Qualys scores it CVSS 4.0 9.2, Critical. Node.js published no workaround. Upgrading is the fix.

The bug was reported by OpenAI Codex Security (Khai Tran and Sylvie Mayer) and fixed by Matteo Collina and Antoine du Hamel, who cherry-picked an upstream Brotli commit by Evgenii Kliuchnikov. Researchers on social media have started calling it "Node2Shell". Node.js does not use that name.
A memory bug in a decompressor only matters if untrusted compressed bytes reach it. The uncomfortable part is how ordinary those paths are. The Node.js advisory does not list them; the mapping below comes from security firms Brandefense and Astra.
Express 5 is reachable through express.json(). Its body-parser 2.x decodes br, gzip, deflate and zstd request bodies, so a client that sends Content-Encoding: br hands its payload straight to the vulnerable decoder. Express 4 is not reachable this way, because its body-parser rejects Brotli bodies with a 415. That is a rare case where the older framework is the safer one, and it is not a reason to stay on Node 20.

Server-side fetch() is a path too. The undici code behind Node's built-in fetch adds a Brotli decompressor whenever a response says br. A service that fetches URLs supplied by users (link previews, webhooks, scrapers, image proxies) can be attacked by pointing it at a hostile server. Astra adds WebSocket frames using Brotli compression and any service that unpacks uploaded compressed files.
Qualys looked at 3.3 million Node.js installations across its customers' assets and published the numbers on October 23. Only about 42,000, or 1.2%, were on a fixed version (1.4% if you count only the 3.1 million installs whose version Qualys could identify). Another 1.3 million were on a supported line but not yet updated, and 15% of all the installs sat on internet-facing machines. That is Qualys' own telemetry, not a census of the internet, but there is little reason to think the wider picture is better.

The bigger number is the 1.7 million installs on end-of-life versions. Those will never receive a fix. Node.js 20 reached end-of-life on April 30, 2026, and the odd-numbered lines (21, 23 and 25) were never long-term releases. For all of them, the only remedy is a major-version upgrade, with the dependency and native-module breakage that can bring.
Run node --version inside the runtime that actually serves traffic. That means the container image in production, not the laptop you develop on and not the base image tag you think you are using. Then compare it with this table.
| You run | Status | Move to |
|---|---|---|
| 26.x | Current, becomes LTS on October 28 | 26.12.1 |
| 24.x | LTS, moving to maintenance as 26 takes over | 24.22.1 |
| 22.x | Maintenance LTS | 22.23.4 |
| 25.x, 23.x, 21.x | End-of-life, no fix | 24.22.1 or 26.12.1 |
| 20.x and older | End-of-life, no fix | 22.23.4 at minimum; 24 or 26 is the better jump |
The same releases also refresh bundled dependencies. In v24.22.1 that means OpenSSL 3.5.9, c-ares 1.34.9, undici 7.30.1, NSS 3.131.1 root certificates and the 2026e timezone data, so test anything that pins certificate behaviour before rolling out.

If you are planning a jump from 20 or 22 anyway, the timing helps. Node.js 26 enters LTS on October 28, 2026 and is supported through April 30, 2029, which buys more runway than landing on 22 and doing this again next year.
Node.js offers nothing short of the patch, so the stopgaps come from Qualys, and they reduce exposure rather than remove it. Stop decompressing Brotli from untrusted sources where you can. Strip or reject Content-Encoding: br on inbound requests at the reverse proxy or WAF, and cap compressed payload sizes there. For fetch-heavy services, restrict which hosts the server will call. None of that covers a path you have not found, which is why each one is a bridge to the upgrade and not a substitute.
The Brotli bug overshadows the rest, but three more are rated HIGH and two of them undercut TLS hostname checks. Not every bug hits every line:
| CVE | Severity | What it does | Lines |
|---|---|---|---|
| CVE-2026-105482 | High | dns.resolve truncates names containing a NUL character | 26, 24, 22 |
| CVE-2026-105483 | High | TLS hostname check bypassed when the IP is written in fullwidth digits | 26, 24 |
| CVE-2026-106069 | High | TLS hostname check bypassed when the host loosely resolves to an IP address | 26, 24, 22 |
| CVE-2026-105481 | Medium | Keep-alive socket processes a response written before the request was sent | see advisory |
| CVE-2026-106072 | Medium | DNS SANs can authenticate IP endpoints via non-canonical IPv4 hostnames | see advisory |
| CVE-2026-106074 | Medium | crypto.randomUUID() and randomInt() entropy caches saved into startup snapshots and SEAs | see advisory |
| CVE-2026-106070 | Low | ZipFile returns attacker-controlled prefix bytes after a failed header check | 26 |
| CVE-2026-106071 | Low | Permission Model filesystem over-grant via wildcard lookup | 26 |
| CVE-2026-106073 | Low | Permission Model bypass with fs.realpathSync | 26, 24, 22 |
The randomness bug deserves a second look if you ship single executable applications: every copy built from the same snapshot could start with the same cached entropy.
Not confirmed as of October 23, 2026, 22:00 UTC, the latest check Brandefense published. CISA's Known Exploited Vulnerabilities catalogue did not list CVE-2026-111497 at that point, and Astra reports no in-the-wild exploitation. Public proof-of-concept code on GitHub reliably crashes unpatched Node.js. Remote code execution is less settled: one researcher says their team reproduced it before authentication, and Aikido reports researchers ran code on the official Node.js 24.22.0 image, though "the exploit did not work every time".
A crash on demand is already a denial-of-service bug against any exposed Express 5 API, and exploits rarely get less reliable over time. It is the second runtime to need an emergency fix this month, after PHP. Treat the upgrade as this week's work, not next quarter's, and use the Node 26 LTS date on October 28 as the deadline for anything still on an end-of-life line.
