Why a Compression Library Can Bring Down Your Stack
Most of us treat Brotli like electricity — it’s there, it works, and we don’t think about it until something breaks. That mindset is exactly why CVE-2026-111497 (dubbed “Node2Shell”) sat dormant in Google’s Brotli decoder for years, quietly bundled into Node.js, Bun, Python, and even Envoy. Then, on October 21, Node.js shipped emergency patches for ten flaws, and Imperva demonstrated a working remote code execution chain on the official Node.js Linux arm64 image. The original disclosure and patch details are tracked in the node2shell GitHub advisory and The Hacker News breakdown; the official Node.js security release has the full version matrix.
I’ve seen too many Philippine government agencies skip library updates because “it’s just compression.” This bug proves that assumption is no longer safe. Below is a practical, tested guide to check whether your runtimes are exposed and patch them before an attacker sends a crafted compressed payload your way.

What CVE-2026-111497 Actually Is
CVE-2026-111497 is an out-of-bounds write in Google’s Brotli decoder. The vulnerable versions are Brotli 1.0.3 through 1.2.0. Because major runtimes bundle the C library directly — rather than linking against a system package — you can be running a vulnerable decoder even if your OS is fully patched.
The attack path is straightforward: an attacker sends HTTP content encoded with a malicious Brotli stream. When your server, worker, or reverse proxy decompresses it, the corrupted stream triggers the out-of-bounds write. Imperva’s proof-of-concept showed this can escalate to remote code execution on a default Node.js Linux arm64 container. CVSS v4.0 scores this at 9.2 Critical.
The scary part is breadth. This isn’t a single package you can npm audit and forget. It’s a decoder bug that rippled across ecosystems — the same flawed C code lives inside Node.js, Bun, Python’s Brotli and brotlicffi packages, and Envoy’s Brotli decompressor filter.
What’s Affected (and What’s Already Fixed)
Here is the current patch status as of late October 2026. Cross-check these against your own environments — don’t trust a single source. I pulled these from the Node.js security release, the GitHub node2shell advisory tracker, and the Python PyPI security notices.
- Node.js: 22.x → 22.23.4, 24.x → 24.22.1, 26.x → 26.12.1. Versions 20.x and older are end-of-life and received no fix — migrate.
- Bun: 1.4.4 ships the corrected Brotli 1.2.1. Earlier versions (1.0.21–1.4.3) are vulnerable.
- Python Brotli (PyPI): 1.2.1 fixes the issue. Versions 1.0.3–1.2.0 are vulnerable.
- Python brotlicffi (PyPI): 1.2.1.0 fixes it. Versions 1.0.3–1.2.0.x are vulnerable.
- Google Brotli (C library): 1.0.10, 1.1.1, and 1.2.1 are safe. Anything from 1.0.3 through 1.2.0 needs replacement.
- Envoy: 1.40.0, 1.39.3, 1.38.6, 1.37.8, and 1.36.12 (the latter is the final 1.36 patch; that branch is now unsupported). Only affected if the Brotli decompressor filter is configured.
- Istio: 1.31.2 and 1.30.6 update the bundled Envoy libbrotli. End-of-life 1.29.x got 1.29.9 only.
Step 1 — Audit Your Runtimes
Before patching, confirm what you’re actually running. In a government or enterprise environment, assumptions are dangerous — I’ve seen teams “upgrade” Node.js in development while production stayed on an old LTS because nobody checked the container image.
Run these checks on every host, container, and CI runner that touches untrusted compressed input:
# Node.js — check both CLI and container image
node --version
# In Docker, inspect the actual image layer
docker inspect --format='{{.Config.Image}}' <container>
# Bun
bun --version
# Python Brotli packages
python3 -c "import Brotli; print(Brotli.__version__)"
python3 -c "import brotlicffi; print(brotlicffi.__version__)"
# Google Brotli C library (if statically linked in your image)
# Check for the shared object or static archive
ldconfig -p | grep brotli
# Or inside the binary
readelf -d <binary> | grep NEEDED | grep brotli
If you’re running managed Kubernetes or PaaS workloads, check your base image tags. An oven/bun:1.4.3-slim or node:22.22-alpine image still carries the old decoder even if you updated your application code yesterday.
Step 2 — Patch Each Runtime
The fix is simple in concept: upgrade to a fixed version, rebuild your images or executables, and restart every process. In practice, the trick is making sure the old decoder is actually gone from memory.
Node.js
Update to 22.23.4, 24.22.1, or 26.12.1. If you use containers, rebuild your Docker images on the fixed tag and redeploy. A running Node.js process keeps the old decoder in memory until it restarts — a rolling restart is not enough if some pods stay warm.
# Direct host install
npm install -g [email protected]
# Docker base image update
# Change in Dockerfile:
FROM node:22.23.4-slim
# Then rebuild
docker compose build --pull
docker compose up -d
Bun
Upgrade to 1.4.4. If you have compiled executables built with bun build --compile, rebuild them — updating Bun on the host does not patch already-compiled binaries.
# macOS / Linux host
brew upgrade bun
# or
bun upgrade
# Rebuild compiled binaries
bun build ./src/index.ts --compile --outfile myapp
Python
Upgrade both PyPI packages and pin the floors in your requirements or lock files. If you use Conda, update there as well.
pip install -U "Brotli>=1.2.1" "brotlicffi>=1.2.1.0"
# In requirements.txt, set minimums
Brotli>=1.2.1
brotlicffi>=1.2.1.0
Restart long-running workers after the upgrade. A Gunicorn or Celery process loaded the old decoder at startup and will not pick up the new package until it restarts.
Envoy / Istio
Only vulnerable when the Brotli decompressor filter is enabled. Upgrade Envoy to 1.36.12, 1.37.8, 1.38.6, 1.39.3, or 1.40.0. For Istio, upgrade to 1.31.2 or 1.30.6. After the upgrade, validate that the sidecar or gateway proxy actually reloaded the new binary — don’t trust a rolling restart without a version check.
Step 3 — Verify the Fix Landed
Patching is only half the job. I’ve watched teams declare victory while half their fleet still ran the old decoder — a failure mode I cover in secure deployment checklists. because a canary never rebooted. Verify with the same commands from Step 1, and add one more check: confirm your load balancer or reverse proxy is no longer advertising Brotli decompression if you intentionally disabled it as a mitigation.
If you need a quick negative test, send a crafted Brotli payload to a test instance. Several PoCs are available in the node2shell repository; run them in an isolated lab, never against production. The goal is to see a clean rejection or graceful error, not a segfault.
What to Do If You Can’t Patch Immediately
Not every team can patch in minutes. A government agency with change-control boards or a legacy Envoy deployment on an unsupported branch needs options.
First, disable the Brotli decompressor filter in Envoy if you can live without it. In Node.js, there is no runtime toggle to disable Brotli decoding — the library is always available once the process starts. Your only immediate option is to front the service with a WAF or reverse proxy that strips Content-Encoding: br before the request reaches your Node.js process.
Second, restrict outbound connections from the vulnerable service to a minimal allowlist — the same principle I covered in API token hygiene. If the process can’t reach attacker infrastructure, exploitation becomes harder even if the decoder is still present.
Third, prioritize customer-facing and internet-exposed services. Internal batch workers on private networks are lower priority than public API servers or reverse proxies handling untrusted client traffic.
The Bigger Lesson: Bundled Code Bites Back
CVE-2026-111497 is a reminder that “dependencies” aren’t just the packages you install. They’re the libraries your runtime vendors compile in. Node.js, Bun, and Python all shipped the vulnerable Brotli decoder because they bundled Google’s C code directly.
That’s why the PHP2Shell bugs earlier this year were so dangerous — PHP’s core had a memory-unsafe path that nobody noticed for fourteen years. Same pattern: trusted infrastructure software, bundled vulnerable code, zero visibility until an AI or researcher finds it.
The practical takeaway for any ICT manager: maintain a Software Bill of Materials for your runtimes, not just your application dependencies. Know what’s compiled into your Node.js image, your Bun binary, and your Envoy proxy. When a CVE like this drops, you’ll know in minutes whether you’re exposed — not days.
Quick-Reference Patch Table
| Component | Affected Versions | Fixed Version | Patch Command |
|---|---|---|---|
| Node.js | 22.x, 24.x, 26.x before fixes; 20.x EOL | 22.23.4, 24.22.1, 26.12.1 | Upgrade runtime; rebuild containers |
| Bun | 1.0.21 – 1.4.3 | 1.4.4 | bun upgrade; rebuild compiled binaries |
| Python Brotli | 1.0.3 – 1.2.0 | 1.2.1 | pip install -U "Brotli>=1.2.1" |
| Python brotlicffi | 1.0.3 – 1.2.0.x | 1.2.1.0 | pip install -U "brotlicffi>=1.2.1.0" |
| Envoy | 1.36.x–1.39.x before fixes | 1.36.12, 1.37.8, 1.38.6, 1.39.3, 1.40.0 | Upgrade proxy; verify Brotli filter config |
Commands and affected-version ranges are cross-referenced from the GitHub node2shell advisory tracker, the Beazley Security Labs advisory BSL-A1236, and the Node.js October 2026 security release. Run these in a lab first, then push to staging, then production.