How to check and fix the Node.js Brotli decoder flaw (CVE-2026-111497) in the process that serves traffic
Node.js 22.x below 22.23.4, 24.x below 24.22.1 and 26.x below 26.12.1 carry a Brotli decoder memory bug. Upgrade, restart and verify every serving process.

Drafted with AI assistance, then checked and edited by a human engineer · How we write
Short answer
CVE-2026-111497 is an out-of-bounds write in the Brotli decoder bundled with Node.js 22.x, 24.x and 26.x. Malformed Brotli data decompressed with node:zlib can crash the process, leak memory or run code. Upgrade to 22.23.4, 24.22.1 or 26.12.1, restart and check process.versions.node in every running instance.
| CVE | CVE-2026-111497, High, Brotli decoder out-of-bounds write |
|---|---|
| Affected | Node.js 22.x, 24.x and 26.x before the fixed builds; 20.x and older are end of life and always affected |
| Fixed | 22.23.4, 24.22.1, 26.12.1 (released 2026-10-21) |
| Trigger | malformed Brotli data that an application decompresses with node:zlib |
| Impact | crash, process memory disclosure or remote code execution |
| Our lab | 22.13.1 below the fixed version, 22.23.4 meets it, 2/2 valid Brotli round trips identical on both |
Upgrade affected Node.js runtimes to 22.23.4, 24.22.1 or 26.12.1, or a newer supported release, then restart and verify every serving process. CVE-2026-111497 lets attacker-controlled Brotli input corrupt memory in the native decoder used by node:zlib. Any 22.x, 24.x or 26.x service below those builds that decompresses Brotli from outside is exposed, and 20.x and older get no fix.
This guide is for systems you own or maintain.
Am I affected?
| Line | Affected | Fixed in |
|---|---|---|
| 26.x | below 26.12.1 | 26.12.1 |
| 24.x | below 24.22.1 | 24.22.1 |
| 22.x | below 22.23.4 | 22.23.4 |
| 20.x and older | always (end of life) | no fix |
Start with an inventory of the runtimes you have. These commands tell you what is installed or configured; they do not prove what an already running service executes, because each one reads a path or starts a fresh node.
# runtime inventory only
which -a node | while read n; do echo "$n $($n -v)"; done # every node on PATH
systemctl show -p ExecStart <service> # binary a unit is configured to start
docker exec <container> node -p process.versions.node # starts a new process in the container
kubectl exec <pod> -- node -p process.versions.node # same, per pod
The authoritative answer comes from inside the serving process. Add this to a debug or health endpoint you already protect:
app.get('/internal/runtime', (req, res) => res.json({
pid: process.pid,
node: process.versions.node,
execPath: process.execPath,
}));
process.versions reports the runtime executing that code. Call it on every replica and every worker (cluster workers, PM2 instances, pods) and record the PID or instance ID next to the version, so one patched replica behind a load balancer cannot answer for the rest.
To see which of your own inputs reach the decoder:
grep -rnE "brotliDecompress|createBrotliDecompress|BrotliDecompress" \
--include='*.js' --include='*.mjs' --include='*.ts' . | grep -v node_modules
No match does not clear you. Dependencies call node:zlib on your behalf, and The Hacker News reports fetch() of br responses as another path. The official advisory names node:zlib only.
Fix it now
nvm install 22 && nvm alias default 22 # resolves to 22.23.4 or newer
brew upgrade node@22 # Homebrew
sudo apt-get update && sudo apt-get install --only-upgrade nodejs # NodeSource deb
sudo dnf upgrade nodejs # rpm based
FROM node:22.23.4-bookworm-slim
Build containers with docker build --pull so a cached base layer is not reused. If your distribution links Node against a shared system Brotli library, the fix ships in that library's package; check your distribution's tracker for CVE-2026-111497.
A new binary on disk changes nothing for a process that started earlier. Restart, then call the endpoint on each instance:
sudo systemctl restart <service>
pm2 restart all
kubectl rollout restart deployment/<name> && kubectl rollout status deployment/<name>
Done means every PID or pod you listed answers with a fixed node version and the execPath you expect.
Why it is dangerous
Decompression runs at the edge of a service. An HTTP client decoding an upstream response, a proxy written in Node, or an upload handler inflating a request body all pass untrusted bytes to the decoder before validation or business logic sees them. That decoder is native code living in the same address space as your application, its secrets and its sockets.
upstream response / upload / message
│ untrusted bytes, Content-Encoding: br
v
node:zlib Brotli stream ──> native decoder state machine
│ invalid block switch
│ status: "need more input" (should be: format error)
v
caller keeps feeding chunks
v
8-byte internal buffer overrun ──> process memory corrupted
The fault is in how the decoder classifies one bad state. Per the Node.js advisory, the decoder treats an invalid block switch as a request for more input. Continued decoding can overrun its eight-byte internal buffer and corrupt process memory. The advisory lists crashes, memory disclosure and remote code execution as possible outcomes.
A streaming caller does the reasonable thing with "need more input": it supplies the next chunk. A permanent error would have ended the stream. That is why streaming decode paths on untrusted data are the ones to patch first.
Reproduce
We ran node labs/security/node-brotli-check-lab/node-brotli-check-lab.mjs on our Mac on 2026-11-06 at 11:01 UTC. The lab does not modify the installed runtime and never sends malformed Brotli data. The Node on PATH, /usr/local/bin/node, reported 22.13.1, below the fixed 22.23.4.
Experiment
The lab downloaded the official node-v22.23.4-darwin-arm64 build to a temp folder, matched its SHA-256 against nodejs.org's SHASUMS256.txt, and ran the same checks with both binaries.
| Test | Input | 22.13.1 | 22.23.4 |
|---|---|---|---|
| below fixed version | node version | true | false |
| Brotli round trip, sync API | 64 JSON rows | ok 7a3d975 | ok 7a3d975 |
| Brotli round trip, sync API | 4096 JSON rows | ok 0b20394 | ok 0b20394 |
The hashes are the first characters of the SHA-256 of the decompressed output. Both JSON fixtures round-tripped successfully on both builds, with identical output.
Fix
Staying in the same major line keeps the change small: 22.13.1 to 22.23.4. The new build met the fixed-version threshold. The scan found no matching Brotli API references in 295 scanned source files, with node_modules excluded. We still patch, since dependencies and fetch() can decode br without our code naming the API.
Report

| Check | Result |
|---|---|
| below fixed version (22.13.1) | 1/1 |
| fixed-version threshold met (22.23.4) | 1/1 |
| valid round trips passed on both | 2/2 |
The capture uses the lab's older labels ("attack inputs", "REPRODUCED", "BLOCKED"). Read them as the version comparison in the table above: no attack input was supplied.
What this lab does not prove: it does not reproduce the vulnerability or validate the fix. The memory write and its closure in 22.23.4 rest on the Node.js advisory. We compared versions on one Mac, tested only the synchronous API on two JSON fixtures, and looked at none of your containers or dependencies. Our take: the restart is where teams slip. A patched CLI next to a long-running old process looks fine from the shell, so the per-instance endpoint is the check we trust.
If you cannot update today
The official advisory lists no workaround or configuration switch. The remedy is the fixed release, and for 20.x and older a move to a supported line. Until then, rank services by exposure: those that decode Brotli from external upstreams, uploads or third-party messages go first. Verification after patching is the same per-instance process.versions.node check, with the PID or pod name recorded.
Timeline and sources
| Date | Event |
|---|---|
| 2026-10-21 | Node.js releases 22.23.4, 24.22.1 and 26.12.1 with the fix for CVE-2026-111497 |
| 2026-11-06 | Our lab run: 22.13.1 below the fixed version, 22.23.4 meets it |
We never call brotliDecompress ourselves. Are we exposed?
Possibly. HTTP clients, proxies and body parsers call node:zlib for you, and The Hacker News reports fetch() of br responses as a path. The official advisory names node:zlib only. Patch the runtime whatever your own code does.
Do we need to rebuild native addons?
Native addons using V8 or Node-specific APIs may need rebuilding across majors. Node-API addons can remain compatible; check each dependency. A patch release inside your current major line is the smallest change.
Our image is pinned to a fixed node tag. Is that enough?
Rebuild with docker build --pull, redeploy, and verify each running instance. A fixed tag sets the intended runtime version; the running containers are what you check.
Related on this site: the Node.js security guide, the APT signature check and the Rails upload flaw check, which use the same check-the-running-version habit.
Frequently asked questions
We never call brotliDecompress ourselves. Are we exposed?
Possibly. HTTP clients, proxies and body parsers call node:zlib for you, and The Hacker News reports fetch() of br responses as a path. The official advisory names node:zlib only. Patch the runtime whatever your own code does.
Do we need to rebuild native addons?
Native addons using V8 or Node-specific APIs may need rebuilding across majors. Node-API addons can remain compatible; check each dependency. A patch release inside your current major line is the smallest change.
Our image is pinned to a fixed node tag. Is that enough?
Rebuild with docker build --pull, redeploy, and verify each running instance. A fixed tag sets the intended runtime version; the running containers are what you check.
Sources
- Node.js, 2026-10-21: Wednesday, October 21, 2026 Security Releasesnodejs.org
- Node.js v22.23.4 release notesnodejs.org
- Node.js documentation: node:zlib (Brotli classes and options)nodejs.org
- Node.js documentation: process.versionsnodejs.org
- Node.js documentation: Node-API, implications of ABI stabilitynodejs.org
- The Hacker News, 2026-10: Brotli decoder flaw behind Node.js RCEthehackernews.com