Offgrid Studio logo Offgrid Studio

Tech Analysis

Broccoli2Shell: How a Brotli Bug Reached Node.js and Python

October 2026. This is a developing story, and details may change.

One bug, many names

On 21 October 2026, Node.js shipped security releases that fixed ten vulnerabilities. The most serious, CVE-2026-111497, is an out-of-bounds write in Brotli, the compression decoder bundled into Node.js. Within days the same flaw turned up under several names, Node2Shell, Python2Shell, HTTP2Shell and Broccoli2Shell, because it lives in Google’s reference Brotli C decoder, so it reaches everything built on that code: Node.js, Python’s brotli and brotlicffi packages, Bun, Envoy and others. This piece explains how a bug in a compression decoder can matter this much, who is affected, and what to do.

What Brotli is, and why it is everywhere

Brotli is a general-purpose compression format, standardised as RFC 7932, that web servers use to shrink pages, scripts and API responses. When a client says it accepts Brotli, the server can reply with a Content-Encoding of “br”, and the client has to decode it before anyone sees the content. That makes the decoder one of the first things to touch data from the network, often before any application logic has run.

Because it’s so useful, the decoder is copied into a lot of software. Node.js bundles its own copy, so the Brotli library installed on your system doesn’t matter there. Python’s brotli packages wrap the same C code. Coverage also lists projects such as DuckDB and Apache Arrow as bundling the decoder. One flaw, many carriers.

How an out-of-bounds write happens

A decoder reads a stream of compressed bytes and rebuilds the original data in memory. To do that it keeps internal buffers of fixed sizes. In this bug, according to the Node.js advisory, the streaming decoder kept appending attacker-supplied bytes to an internal buffer of eight bytes and wrote past its end. That overwrote neighbouring memory, including the decoder’s own state.

Think of a row of eight lockers. If a program keeps stuffing items into locker eight and never checks it is full, they spill into locker nine, which belongs to something else. If an attacker controls what spills, they can crash the program, read memory that should be private, or in the worst case steer what the program does next. The fix, a patch from the Brotli project that Node.js copied in, makes the decoder detect the invalid input and stop earlier. Patched Node.js returns a new decoder error code when it rejects such a stream.

This class of bug is familiar. Decoders parse untrusted binary formats, and they are usually written in C or C++ for speed, languages that leave bounds checking to the programmer. It’s one reason compression, image and media decoders show up so often in security advisories.

Who is affected, and what fixes it

  • Brotli C library. Versions 1.0.3 through 1.2.0 are affected. Fixed in 1.0.10, 1.1.1 and 1.2.1.
  • Node.js. The 22.x, 24.x and 26.x lines before 22.23.4, 24.22.1 and 26.12.1. End-of-life lines such as 20.x and older get no fix.
  • Python. The brotli package up to 1.2.0 and brotlicffi up to 1.2.0.x. Fixed in 1.2.1 and 1.2.1.0.
  • Bun and Envoy. Bun is reported fixed in 1.4.4. Envoy is affected where it is set up to decompress Brotli.

When are you exposed?

The danger is not in having the library, but in decoding Brotli that an attacker can influence. Reported exposure points include:

  • Node.js code that fetches a response from a server it doesn’t control, since fetch() decodes Brotli automatically, or reads Brotli through the zlib module.
  • Express and body-parser servers that accept request bodies compressed with Brotli.
  • Proxies and gateways that decompress Brotli traffic.
  • Python HTTP clients such as urllib3 (and so requests), httpx and aiohttp, which decode Brotli responses whenever a Brotli package is installed. Scrapy requires it, so Scrapy installs are exposed until upgraded. pip is not affected, because its bundled urllib3 has Brotli turned off.

If your program only decodes Brotli from sources you control, the practical risk is lower, but patching is still the safe course.

How serious is it?

Node.js rates the flaw High, not Critical, and calls the outcome remote code execution. Reports on whether that is realistic differ, and it’s worth reading carefully:

  • Demonstrated. Imperva’s Red Team reported running attacker-supplied code on an unmodified Node.js 24.22.0 build for Linux on ARM64, delivered both as a fetched response and as an Express request body.
  • Not shown on stock builds elsewhere. A public proof of concept published on 22 October reportedly reached code execution only against a deliberately weakened test build. On a standard build, its author reported a crash and a limited read of memory, and said a working attack on a normal build was not achieved.
  • Python. Code execution has not been shown for the Python packages, though the flaw is present.

So the honest summary is that memory corruption is confirmed, code execution is shown in at least one realistic environment, and the exact risk depends on the platform and build. “Possible RCE” labels shouldn’t be read as “easy RCE”.

What to do

  • Check your versions. In Node.js, run node --version, and node -p "process.versions.brotli" to see the bundled Brotli. In Python, check pip show brotli and brotlicffi.
  • Upgrade. Move to the fixed Node.js releases, Python brotli 1.2.1 or brotlicffi 1.2.1.0, and Bun 1.4.4 where relevant. Reports note Node.js has published no workaround, so upgrading is the fix.
  • Replace end-of-life runtimes. Lines that no longer get security releases can’t be patched.
  • Check extras and containers. Optional extras such as httpx[brotli] can reinstall the vulnerable package, and base images may bundle an old runtime. Rebuild images after upgrading.
  • Patch the exposed services first. Anything that decompresses Brotli from the internet comes before internal tooling.

The bigger lesson

This bug is a small illustration of how modern software shares code. A defect in one C library, maintained upstream, showed up in several runtimes at once, each with its own copy and its own patch schedule. The dependency doesn’t appear in your own code, so you only find it if you know to look. That’s why advisories like this lean so heavily on version checks: the question isn’t whether you wrote a vulnerable line, but which of the pieces you run still carries one.

Sources

For disclosure: we use AI tools, including Claude, to help research and draft our posts, and we check the facts and sources ourselves. The flaw was reported by OpenAI’s Codex Security team, according to The Hacker News. We are not security professionals, this is a developing story, and you should follow the advisories from Node.js, your Python package maintainers and your vendors for your own setup.

More from Offgrid Studio

LiteLLM Was Compromised Twice. What an AI Gateway Holds
NVIDIA OpenShell: A Sandbox for AI Agents, Explained
What a Hospital for Coding Agents Teaches About Review

Back to the blog