2026.10.29 · 4 min · security · javascript · backend · news
Your server unzips what strangers send.
Content-Encoding works in both directions: a client can POST a Brotli-compressed body without asking, and Express 5 inflates it before your code runs. That is why CVE-2026-111497, a bug in Node's Brotli decoder, is reachable by any POST, and why the fix is a Node update, not an npm one. As of 2026.10.
Most of us only know compression as "the server gzips responses". It goes the other way too. A client can label its request body Content-Encoding: br and send it without asking, and in Express 5 express.json() decompresses it before your handler runs. So when Node's Brotli decoder has a bug, any stranger who can send a POST can reach it. As of 2026.10, one does: CVE-2026-111497, "Broccoli2Shell".
Watch the 60-second version on Instagram.
Responses are negotiated, requests are not
For a response there is a handshake. The browser sends Accept-Encoding with the encodings it can read, the server picks one and labels the body with Content-Encoding.
A request has no such step. The client just labels its body Content-Encoding: br and sends it. RFC 9110 gives the server one way to say no afterwards: reply 415 Unsupported Media Type, optionally listing what it does accept in Accept-Encoding. Nothing asks first.
Express 5 inflates by default
body-parser 2.x is what express.json() runs on. Its README says the json, raw, text and urlencoded parsers support "automatic inflation of gzip, br, deflate and zstd". The inflate option defaults to true, and an encoding it doesn't know gets a 415.
Brotli support arrived in body-parser 2.0.0, and Express 5 depends on body-parser 2. So this one line is enough:
// Express 5: this inflates br/gzip/deflate/zstd bodies by default
app.use(express.json());And this is all an attacker needs to reach the decoder:
curl -X POST localhost:3000/api \
-H 'Content-Type: application/json' \
-H 'Content-Encoding: br' --data-binary @body.json.brThe decoder and the parser both run inside express.json(). Your handler only ever sees req.body, after the Brotli stream has already been decoded.
The bug, in one paragraph
Node's advisory describes it plainly: an invalid block switch was reported as "need more input" rather than as an error, so the decoder kept writing into an 8-byte buffer. Node rates it High and says it "can lead to a crash, disclosure of process memory, or remote code execution."
CISA added CVE-2026-111497 to its Known Exploited Vulnerabilities catalog on 2026.10.27, so it is actively exploited. ELLIO, a security vendor, reports exploit traffic carrying a 23 MB Brotli body, and RCE on stock x86-64 builds including Express 5.3.0 with express.json(). Each build needs its own offset, ELLIO's own staged payload got no shell, and the patched 24.22.1 answers 400. Those are one vendor's findings, so I read them as "can lead to RCE", not "always RCE".
A bare node:http server doesn't decode request bodies on its own. According to the Broccoli2Shell write-up, you are exposed there only if your code passes the body to node:zlib, and ELLIO reports RCE on exactly such a server.
The fix is Node, not npm
| Node line | Fixed in |
|---|---|
| 22 | 22.23.4 |
| 24 | 24.22.1 |
| 26 | 26.12.1 |
| 20 and older | No fix: "End-of-Life versions are always affected" |
The releases came out on 2026.10.21. What people get wrong:
- Upgrading Express or body-parser. It does nothing here. The decoder belongs to Node.
- Updating Node without restarting. The old binary keeps running until the process restarts. Containers need a rebuild too: a
FROM node:24tag, lockfiles and CI caches can all keep serving the old version. - Thinking
inflate: falsecloses it.express.json({ inflate: false })refuses compressed request bodies with a 415, but the outbound path,fetch()decoding abrresponse, still goes through the same decoder. - Saying it's "only Express 5.0 to 5.3". body-parser 2.4.0 still inflates
br(it adds zstd), and a fresh Express 5.3.0 install pulls it in. Treat it as "Express 5 / body-parser 2".
On a client project I'd check node -v on every running process, not on my laptop, and I'd count the fix as done only when the image is rebuilt and the process has restarted.
The short version
- Responses negotiate compression with
Accept-Encoding. Requests just label the bodyContent-Encodingand send it. - In Express 5,
express.json()inflates gzip, br, deflate and zstd bodies by default, before your handler runs. - CVE-2026-111497 is a bug in Node's Brotli decoder. It is actively exploited and can lead to RCE.
- Update Node to 22.23.4, 24.22.1 or 26.12.1. Node 20 and older get no fix.
- Restart the processes and rebuild the images, or the old Node keeps running.
Sources: Node.js, October 2026 security releases · MDN, Content-Encoding · RFC 9110 · body-parser README · body-parser HISTORY.md · Express History.md · Broccoli2Shell write-up · ELLIO, Node2Shell RCE analysis · CISA KEV alert, 2026.10.27