Brotli CVE-2026-111497: Patch Node.js, Bun and Python Apps
CVE-2026-111497 is a memory-corruption bug in Google's Brotli decoder, the C code that unpacks Content-Encoding: br data. Crafted compressed data writes past the end of a buffer, which can crash the program, leak memory or run the attacker's code. Node.js, Bun, Python's Brotli and brotlicffi packages and Envoy each ship their own copy of that decoder, so each has to be updated on its own. This guide shows when an app is actually exposed, how to check a server in one command, and how to roll the fix out without missing a container.
How serious it is
- Google rates it 9.2 (critical) on CVSS 4.0, Node.js rates it high, Red Hat 8.1.
- Imperva's researchers ran their own code on an unpatched Node.js 24.22.0 on Linux ARM64, both through a fetched response and through an Express request body. The exploit is not public.
- CISA's assessment of October 23 recorded no exploitation in the wild, and there is no reliable public proof of concept.
- The flaw works both ways: a server that unpacks compressed input can be attacked by a client, and a client that unpacks compressed responses can be attacked by a server.
The fix was merged into Brotli's source on February 27, but no Brotli release carried it until October 21, which is why the patched runtimes all appeared in the same week.
Are you exposed?
Only code that decodes Brotli data someone else controls is at risk:
- Node.js and Bun - the built-in
fetch()reading a response sent withContent-Encoding: br,node:zlibBrotli calls on untrusted data, and request-body parsers that unpack Brotli uploads (Express 5, body-parser 2). A plain Node.js HTTP server does not decode request bodies by itself. - Python - having the Brotli or brotlicffi package installed is enough: requests, urllib3, httpx and aiohttp then decode Brotli responses on their own, even if your code never imports Brotli.
- Envoy and Istio - only if a Brotli decompressor filter is configured.
Any service that calls third-party APIs, webhooks, payment providers or scrapes pages runs fetch() against servers you do not control. Removing br from Accept-Encoding is not a fix: a server can send Brotli anyway.
| Software | Affected | Fixed in |
|---|---|---|
| Node.js | 22.x, 24.x, 26.x before the fix; 20.x and older (end of life) | 22.23.4, 24.22.1, 26.12.1 |
| Bun | 1.4.3 and earlier | 1.4.4 |
| Python Brotli | 1.2.0 and earlier | 1.2.1 |
| Python brotlicffi | 1.2.0.x and earlier | 1.2.1.0 |
| Google Brotli (C library) | 1.0.3 - 1.2.0 | 1.0.10, 1.1.1, 1.2.1 |
| Envoy | 1.39.2, 1.38.5, 1.37.7, 1.36.11 | 1.40.0, 1.39.3, 1.38.6, 1.37.8, 1.36.12 |
| Istio | 1.31.0 - 1.31.1, 1.30.0 - 1.30.5, 1.29.0 - 1.29.8 | 1.31.2, 1.30.6, 1.29.9 |
Check a server in one command
This POSIX shell script looks at the Bun, Node.js and Python on the machine (or inside a container) and says which are older than the fix. Save it as brotli-check.sh:
#!/bin/sh
# CVE-2026-111497 (Brotli decoder): which runtimes on this host are older than the fixes?
ver_lt() { [ "$1" = "$2" ] && return 1; [ "$(printf '%s\n%s\n' "$1" "$2" | sort -V | head -n1)" = "$1" ]; }
if command -v bun >/dev/null 2>&1; then
v=$(bun --version)
if ver_lt "$v" 1.4.4; then echo "bun $v: VULNERABLE, upgrade to 1.4.4+"; else echo "bun $v: fixed"; fi
fi
if command -v node >/dev/null 2>&1; then
v=$(node -p "process.versions.bun ? '' : process.versions.node")
if [ -z "$v" ]; then echo "node: this is Bun's node shim, see the bun line"; else major=${v%%.*}
case $major in
22) fix=22.23.4 ;; 24) fix=24.22.1 ;; 26) fix=26.12.1 ;;
*) fix= ;;
esac
if [ -z "$fix" ]; then echo "node $v: no fixed release for this line, move to 22, 24 or 26"
elif ver_lt "$v" "$fix"; then echo "node $v: VULNERABLE, upgrade to $fix+"
else echo "node $v: fixed"; fi
fi
fi
for py in python3 python; do
command -v $py >/dev/null 2>&1 || continue
$py - <<'PY'
from importlib import metadata
for pkg, fix in (("Brotli", "1.2.1"), ("brotlicffi", "1.2.1.0")):
try:
v = metadata.version(pkg)
except metadata.PackageNotFoundError:
print(f"python {pkg}: not installed")
continue
a = [int(x) for x in v.split(".") if x.isdigit()]
b = [int(x) for x in fix.split(".")]
print(f"python {pkg} {v}: {'VULNERABLE, upgrade to ' + fix + '+' if a < b else 'fixed'}")
PY
break
done
sh brotli-check.sh
docker exec my-app sh -s < brotli-check.sh # the same check inside a running container
Output checked on 2026-10-24 (a developer laptop, then official Docker images):
bun 1.4.1: VULNERABLE, upgrade to 1.4.4+
node 26.6.0: VULNERABLE, upgrade to 26.12.1+
python Brotli: not installed
python brotlicffi: not installed
bun 1.4.4: fixed
node: this is Bun's node shim, see the bun line
node 22.23.4: fixed
node 16.20.2: no fixed release for this line, move to 22, 24 or 26
python Brotli 1.1.0: VULNERABLE, upgrade to 1.2.1+
python brotlicffi: not installed
Run it in every container, not only on the host: the host's Node.js says nothing about the one baked into an image.
How to roll out the fix
- Find every copy. Images are the usual miss. List what you run:
docker ps --format '{{.Image}}', then theFROMlines of your Dockerfiles. A tag likenode:22oroven/bun:alpineonly moves forward when you pull it; a pinned tag such asoven/bun:1.4.1-slimnever does. - Update the runtime. Bump pinned tags (
oven/bun:1.4.4-slim,node:22.23.4-alpine),bun upgradeand your Node.js packages on hosts, andpip install -U "Brotli>=1.2.1"(orbrotlicffi>=1.2.1.0) in Python environments. If a Python app does not need Brotli,pip uninstall Brotli brotlicffialso closes the path: the HTTP clients fall back to gzip and deflate. - Rebuild and replace.
docker compose build --pull && docker compose up -d. A running process keeps the old decoder until it restarts, so restart services on hosts as well. - Verify. Run the check script again inside each container (
docker exec <name> bun --versionis the quick version). Lockfiles with--frozen-lockfilecan fail the rebuild after you bump dependencies: commit the updated lockfile together withpackage.json. - Cannot update yet? Reject requests with
Content-Encoding: brat the proxy before anything decodes the body, and avoid fetching from untrusted hosts. Vercel notes that firewall rules do not cover responses your own code fetches from other servers - only the update does.
For a Node.js stack on a VM see Cloud Server for Node.js, for Python Cloud Server for Python and Flask, and for images that rebuild cleanly Cloud Server for Docker Swarm.
What we did
Within days of the fixes we moved every DCXV service that runs on Bun to 1.4.4: pinned base images went to fixed tags, and all images were rebuilt with a fresh pull. Our Node.js components do not decode Brotli bodies, and our proxies have no Brotli decompression. Customer servers are managed by their owners: if you run Node.js, Bun or Python on a DCXV cloud server, run the check above today, or ask us about managed server administration.
Sources (checked 2026-10-24): the Node.js October 2026 security releases (22.23.4, 24.22.1, 26.12.1), the Bun 1.4.4 release, the CISA assessment of October 23, 2026 and the Imperva research.
