Brotli CVE-2026-111497: Patch Node.js, Bun and Python Apps

Brotli CVE-2026-111497: Patch Node.js, Bun and Python Apps

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 with Content-Encoding: br, node:zlib Brotli 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

  1. Find every copy. Images are the usual miss. List what you run: docker ps --format '{{.Image}}', then the FROM lines of your Dockerfiles. A tag like node:22 or oven/bun:alpine only moves forward when you pull it; a pinned tag such as oven/bun:1.4.1-slim never does.
  2. Update the runtime. Bump pinned tags (oven/bun:1.4.4-slim, node:22.23.4-alpine), bun upgrade and your Node.js packages on hosts, and pip install -U "Brotli>=1.2.1" (or brotlicffi>=1.2.1.0) in Python environments. If a Python app does not need Brotli, pip uninstall Brotli brotlicffi also closes the path: the HTTP clients fall back to gzip and deflate.
  3. 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.
  4. Verify. Run the check script again inside each container (docker exec <name> bun --version is the quick version). Lockfiles with --frozen-lockfile can fail the rebuild after you bump dependencies: commit the updated lockfile together with package.json.
  5. Cannot update yet? Reject requests with Content-Encoding: br at 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.

Starlink Mini 2 Behind CGNAT: Reaching a Site That Moves
networkingcloudinfrastructure

Starlink Mini 2 Behind CGNAT: Reaching a Site That Moves

Starlink Mini 2 is out, but standard plans sit behind CGNAT. We counted 990,208 IPv4 addresses for 12M+ customers and compare four ways to reach a remote site.

LACNIC IPv4 Transfers in 2026: Rules, Fees, One-Year Hold
ipv4transfernetworking

LACNIC IPv4 Transfers in 2026: Rules, Fees, One-Year Hold

LACNIC IPv4 transfers in 2026: section 2.3.2.18 rules, the US$ 1,037 fee, NIC.br and NIC México, and our count of 1,929 logged transfers from 2016 to 2026.

Data Center Protests Reach Europe: What Buyers Should Do
clouddedicatedinfrastructure

Data Center Protests Reach Europe: What Buyers Should Do

Hundreds entered Microsoft's Amsterdam data center site on 6 November. The Dutch power numbers behind it, measured latency to EU hubs and what buyers should do.

US Voters Rejected Data Centers 45 to 1: What Buyers Learn
clouddedicatedinfrastructure

US Voters Rejected Data Centers 45 to 1: What Buyers Learn

On 3 November 47 US ballot measures on data centers went to voters and the restrictive side won 45. We recount the raw data: what it means for server buyers.

AWS BYOIP in 2026: $43.80 per IPv4 vs Your Own /24
ipv4pricingcloud

AWS BYOIP in 2026: $43.80 per IPv4 vs Your Own /24

AWS bills $0.005 an hour per public IPv4, $43.80 a year. BYOIP addresses carry no charge: when a bought or leased /24 pays back, with a ROA readiness check.