On Wednesday, October 21, Node.js disclosed CVE-2026-111497: a Node process that decompresses attacker-controlled Brotli data can leak memory or run arbitrary code. People called it Node2Shell and the Node release lines got their patches.
By Friday, the same CVE had a new name and a much bigger blast radius. Aikido's follow-up disclosure calls it HTTP2Shell: the flaw lives in Google's Brotli decoder, and Brotli is bundled or auto-loaded by far more than Node. Python's most common HTTP clients, curl builds, Bun, Envoy, Istio. If your stack speaks HTTPS, some part of it probably decodes Brotli.
This is the weekend patch you didn't plan. Here is what is actually known, from the primary sources, and the exact checks I would run.
What CVE-2026-111497 is
It is an out-of-bounds write in the Google Brotli decoder, affecting versions 1.0.3 through 1.2.0 on all platforms, with a CVSS v4.0 score of 9.2 (Critical). The decoder keeps accepting bytes when it should reject them; those bytes overflow a small working buffer and corrupt memory. A remote, unauthenticated attacker can crash the process, leak sensitive data, or potentially execute code.
The detail that should change your weekend planning: it works in both directions. OpenAI, which discovered the vulnerability, describes it as unauthenticated remote code execution where a malicious client can take over an unpatched server, and a malicious server can take over an unpatched client that accepts compressed responses. Your public API is one attack surface. Every script, worker, and cron job in your infrastructure that calls out to someone else's API is the other one.
The February part of the story
OpenAI found the issue in February and reported it. The root cause was fixed in the Brotli source code at the time, but nobody recognized it as a security vulnerability, so no release shipped with the fix. The fix sat in the source tree for months while everything that bundles Brotli kept shipping the vulnerable decoder.
That is why this patch is unusually annoying: there is no single "upgrade Brotli" button. Every project that bundles the decoder has to ship its own update, and you have to chase each one.
Why your dependency scanner is quiet
This is the part most likely to bite Python teams. The PyPI packages Brotli and brotlicffi compile that same decoder, and requests, httpx, and aiohttp load them automatically whenever they are installed. Aikido counts roughly 82.8 million monthly downloads for the Brotli package.
You are exposed if any environment has Brotli 1.2.0 or earlier, or brotlicffi 1.2.0.x, installed. And here is the catch: as of October 23, the advisory databases do not map the CVE to either PyPI package. pip-audit, Dependabot, and Safety will not flag the vulnerable versions. A green scan this week means nothing for this CVE. You have to check the installed versions yourself.
The check is boring, which is the point:
pip show Brotli brotlicffi 2>/dev/null | grep -E 'Name|Version'
# Vulnerable: Brotli <= 1.2.0, brotlicffi 1.2.0.x
# Fixed: Brotli 1.2.1, brotlicffi 1.2.1.0
Check every environment, not just production: CI images, lambdas, the container your side project runs in. Then rebuild any image that bundles them. A patched requirements.txt with an unrebuilt image is still the old decoder.
The fix list, per stack
From Aikido's reviewed list of fixed releases (as of October 23):
- Node.js: upgrade to 22.23.4, 24.22.1, or 26.12.1, or a newer release in your line. Node bundles Brotli, so the runtime version is what matters.
- Google Brotli upstream: 1.0.10, 1.1.1, or 1.2.1, depending on your branch.
- Python packages: Brotli 1.2.1 and brotlicffi 1.2.1.0.
- Bun: 1.4.4, and rebuild compiled executables that bundle it.
- Envoy: 1.40.0, 1.39.3, 1.38.6, or 1.37.8 (1.36.12 has the fix but that branch is unsupported).
- Istio: 1.29.9, 1.30.6, or 1.31.2.
- curl/libcurl: update its Brotli dependency or install your vendor's security-fixed package; statically linked copies need an application rebuild.
- OS packages: install the vendor security update. Vendors may backport the fix without changing the upstream version number, so read the changelog, not just the version string.
Two useful exceptions: Apache's mod_brotli does not decode Brotli, and Nginx does not decode Brotli by default. Your classic reverse proxy is probably not the problem. The application runtimes behind it are.
Then the unglamorous part: update locks, rebuild, redeploy, restart, and verify that the running workload actually uses the patched component. "Merged the fix" is not "running the fix."
If you can't patch everything this weekend
Aikido's workarounds, in order of preference:
For Python apps where Brotli is optional, uninstall both Brotli and brotlicffi, remove extras like httpx[brotli] from your requirements, regenerate locks, rebuild, restart. HTTPX, Requests, and urllib3 keep gzip and deflate without them. Two caveats: a server that insists on sending Brotli may now produce an unreadable response (fallback is not guaranteed), and this does nothing about Brotli decoders bundled elsewhere in the stack.
For inbound traffic, reject requests whose Content-Encoding includes br at the proxy, load balancer, or middleware, before any vulnerable decoder touches the body. Match case-insensitively, including inside encoding lists.
For outbound traffic, remove br from Accept-Encoding and reject responses that arrive with Content-Encoding: br before decoding. The gotcha is real: changing Accept-Encoding alone is insufficient, because a malicious server can send a Brotli response no matter what you advertise, and urllib3 picks its decoder from the response header. The check has to happen before automatic body processing; after the client has decoded the response is too late.
The pattern worth keeping
Yesterday it was PHP2Shell, today HTTP2Shell. Different ecosystems, same shape: a memory bug in a low-level library that everything else quietly bundles, discovered months before it was understood as a vulnerability, and a patch that has to be shipped one runtime at a time.
The practical habit that survives both: know what decompresses on your behalf. Compression, parsing, and deserialization are where "just a library" turns into remote code execution, and the version that matters is the one in the running container, not the one in your lockfile's intent.
Sources: Aikido's HTTP2Shell disclosure and fixed-release review (aikido.dev, Oct 23-24), the Node.js security releases of October 21, and OpenAI's advisories referenced by Aikido. Version numbers and CVE details above are from those documents; where scanner coverage is described as a snapshot ("as of October 23"), treat it as dated and re-check.
Top comments (0)