ADVISORYBrotli decoder flaw · check by handBrotli flaw · check by hand eMudhra certificate keys leaked · free checkeMudhra keys leaked · free check DEIMOS (JPL 402) RA 09ʰ10ᵐ47ˢ DEC +17°47′52″ Δ 1.486 AU AS OF 25 OCT 06:00 UTC REV 2026.10.25 · 06313bb
A free guide from Deimos AI LLCIncident · Brotli · 21 Oct 2026Part of Wardlatch

Wardlatch · Brotli · Checks by hand · Free

CVE-2026-111497The Brotli decoder flaw: are you affected?

Brotli is a compression library built into Node.js, Bun, Python packages, web browsers and the Envoy and Istio proxies. A flaw in its decoder lets crafted compressed data corrupt memory, and Google, which makes Brotli, says that could let an attacker who needs no login run code. Find the copies you run and check their versions below. Wardlatch doesn’t check this flaw yet, so these checks are by hand; each one only reads.

What fixes itFixed in
  • Node.jsthe 22, 24 and 26 lines22.23.4 · 24.22.1 · 26.12.1
  • Pythonthe Brotli and brotlicffi packagesBrotli 1.2.1 · brotlicffi 1.2.1.0
  • Bun1.0.21 to 1.4.3 are affected1.4.4
  • Envoywith Brotli decompression set up1.40.0 · 1.39.3 · 1.38.6 · 1.37.8 · 1.36.12
  • Istiowith Brotli decompression turned on1.31.2 · 1.30.6 · 1.29.9
  • The Brotli library1.0.3 to 1.2.0 are affected, but not 1.0.10 or 1.1.11.0.10 · 1.1.1 · 1.2.1
From each project’s advisory, read 24 Oct 2026: Google’s CVE record, Node.js, GitHub’s advisory for the Python packages, Bun, Envoy and Istio (sources). Node.js 20 and older get no fix from the Node.js project, which says end-of-life versions are always affected. A Node.js that uses the system’s Brotli, such as Homebrew’s or Debian’s, is fixed by updating that library instead (§02).
§01 / The flaw

What it is, and who is exposed.

CVE-2026-111497 is a buffer overflow in Brotli’s decoder, the part that unpacks compressed data. Google’s record says that in Brotli 1.0.3 to 1.2.0 (apart from 1.0.10 and 1.1.1, which are fixes), on every platform, a crafted compressed stream lets an attacker who needs no login corrupt the decoder’s memory, read memory they shouldn’t, crash the program, or possibly run code; Google rates it 9.2 out of 10, critical, and Node.js rates it high. The fix had been in Brotli’s public source code, though in no release, since 27 February 2026, under a title that didn’t mark it as a security fix; Google released it as 1.0.10, 1.1.1 and 1.2.1 on 21 October 2026. Node.js, Bun, Envoy, Istio and the Python packages have shipped fixed releases; Linux distributions are still rolling theirs out (§06).

What matters is whether something you run unpacks Brotli data that someone else can send it, and that often happens without anyone asking for it. Bun says its fetch() asks servers for Brotli and unpacks a Brotli answer by itself. Node.js’s fetch() does the same: we tested Node.js 26.12.1, and over HTTPS it asks for Brotli, and it unpacks a Brotli answer either way (Node.js’s own advisory names data an app decompresses with node:zlib). In Python, requests asks for Brotli and urllib3 unpacks it whenever a Brotli package is installed. So an app that downloads from servers you don’t control can reach the decoder without ever calling a decompress function. Envoy and Istio are affected where Brotli decompression is set up. When in doubt, update: it’s quicker than proving a copy is never reached.

OpenAI’s security researchers reported it: Node.js and Envoy credit them. Many programs carry their own copy of Brotli, built in; some, such as Homebrew’s Node.js and Debian’s, use the system’s library instead. Either way, a fix reaches you only when every copy is updated: each Node.js version you have installed, each Python environment, the system library, each container image and each app. Then restart services and long-running programs: a running program keeps the old decoder loaded until it restarts.

Wardlatch 0.1.9 doesn’t check for this flaw yet. The steps below find each copy by hand. They print versions and change nothing; the fix lines are marked as fixes.

§02 / Node.js

Check every Node.js on your path.

Paste this into a terminal on macOS or Linux running bash or zsh, the usual shells. It looks in each folder on your PATH for a program called node and prints its version beside where it is. No output means no node on your path; if node -v still prints a version, it most likely comes from a version manager that loads on first use (see the note below the rows). It reads ~/ entries as your home folder and skips other entries that aren’t full paths, since those point into whatever folder you’re in. The same node can appear twice where one folder links to another, such as /bin and /usr/bin.

Node.js · paste into a terminal
printf '%s\n' "$PATH" | tr ':' '\n' | while IFS= read -r d; do case $d in "~"/*) d=$HOME/${d#"~"/} ;; /*) ;; *) continue ;; esac; [ -f "$d/node" ] && [ -x "$d/node" ] && printf '%s  %s\n' "$("$d/node" -v </dev/null 2>&1)" "$d/node"; done

Most builds of Node.js carry their own Brotli, and the rows below go by their version. Some use the system’s library instead, and then that library decides it: Homebrew’s do, and Debian’s tracker says Debian’s own do. To tell, run otool -L (Mac) or ldd (Linux) on the path a line shows, for example otool -L /opt/homebrew/bin/node | grep brotli or ldd /usr/bin/node | grep brotli. A libbrotlidec line means the system library: check that in §06 instead, and ignore the version numbers on the line, which are the ones node was built against, not the one it uses now. No output means its own copy, and the rows below apply. On a Mac, otool comes with Xcode’s command-line tools; if they’re missing, run ls -l on the path instead: a node in, or pointing into, /opt/homebrew or a Cellar folder is Homebrew’s (check §06); one in /opt/local is MacPorts’, whose Node.js 26 also uses the system library (check §06); any other carries its own copy.

Fixed
v22.23.4 or later on the 22 line, v24.22.1 or later on 24, v26.12.1 or later on 26.
Update
An earlier version on the 22, 24 or 26 line: install the fixed release for your line from nodejs.org, your version manager or your package manager.
No fix
v20 and older, and the odd-numbered lines (v21, v23, v25): they are end-of-life and get no fix from the Node.js project. Move to 22, 24 or 26.

A version manager keeps one copy per installed version, and the line above only sees the one on your path. List the others with nvm ls, fnm list, volta list node, mise ls node or asdf list nodejs, and update or remove the old ones. Rebuild Docker images on a fixed Node.js, and update apps that carry their own Node.js, such as Electron apps, when their makers ship a fix.

§03 / Python

Find the Brotli and brotlicffi packages.

Each Python environment keeps its own copy of the Brotli or brotlicffi package, if it has one. GitHub’s advisory: Brotli 1.0.3 up to but not including 1.2.1, and brotlicffi 1.0.3 up to but not including 1.2.1.0, are affected. This lists each copy of those two packages in the folders it searches, and where it is; the version is in the name. It can take a few minutes. On a Mac it skips ~/Library, apart from the folders where Python, Poetry, PDM, Hatch and pipx keep environments, and macOS may ask whether Terminal may open some folders, such as Documents: you can say no, but environments inside those folders won’t be found.

Python · paste into a terminal
find -H "${HOME%/}" /usr /opt /srv /var/www /Library/Frameworks /Library/Python "${HOME%/}/Library/Python" "${HOME%/}/Library/Caches/pypoetry" "${HOME%/}/Library/Application Support/hatch" "${HOME%/}/Library/Application Support/pipx" "${HOME%/}/Library/Application Support/pdm" "${HOME%/}/Library/Application Support/pypoetry" \( -path "${HOME%/}/Library" -o -name node_modules -o -name .git -o -path '*/.cache/uv' -o -path '*/.local/share/containers' -o -path '*/.local/share/flatpak' -o -path '*/.local/share/docker' -o -path '*/pkgs/brotli-python-*' -o -path '*/pkgs/brotlicffi-*' \) -prune -o \( -iname 'brotli-*.dist-info' -o -iname 'brotlicffi-*.dist-info' -o -iname 'brotli-*.egg-info' -o -iname 'brotlicffi-*.egg-info' -o -iname 'brotli-*.egg' -o -iname 'brotlicffi-*.egg' \) -print 2>/dev/null
Fixed
…/brotli-1.2.1.dist-info or later, …/brotlicffi-1.2.1.0.dist-info or later (the name may start with a capital B).
Update
Anything earlier, such as …/Brotli-1.1.0.dist-info, in a virtual environment, conda environment, pyenv, user folder, python.org or Homebrew Python: update it (below).
Distribution
A line under /usr/lib/python3/dist-packages, /usr/lib/python3.X/site-packages or /usr/lib64/… is usually your Linux distribution’s package. Ask which package owns it: type the command, a space and the line in quotes, for example dpkg -S '/usr/lib/python3/dist-packages/Brotli-1.0.9.egg-info' on Debian or Ubuntu; rpm -qf (Fedora, Red Hat) and pacman -Qo (Arch) work the same way. On Alpine, run apk info --who-owns on the METADATA file inside that folder (apk before 3.0, in Alpine 3.22 and older, can’t look up a folder). If a package owns it, its folder may keep the original version number even after a security fix, so check that package’s version against your distribution’s security page (§06 links Ubuntu’s, Debian’s and Red Hat’s), and update it with the system’s package manager, not pip. If no package owns it, it was installed with pip as root: update it as below.
An app’s own
A line inside /opt/homebrew/Cellar/… or a libexec folder belongs to that Homebrew formula or app. It’s fixed when Homebrew or the app’s maker ships a version with Brotli 1.2.1: run brew upgrade and its name, or update the app, and check again; don’t change it with pip.
Nothing
No lines: no Brotli or brotlicffi package in the folders searched. It searches only your home folder (on a Mac without ~/Library, apart from the folders named above), /usr, /opt, /srv, /var/www and, on a Mac, /Library/Frameworks and /Library/Python. Everything else is not searched, including /Applications, other disks (/Volumes, /mnt, /media), /var/lib, Linuxbrew (/home/linuxbrew), Snap, Flatpak and Nix, folders reached through a link, node_modules folders, Git folders (.git), Docker and Podman images, other people’s home folders, uv’s and conda’s caches, and folders you can’t open or said no to.

To update a line, find its Python. For a virtual environment, pyenv, python.org or Homebrew Python (for a conda environment, see conda below), take the part of the line before /lib/python3.X/ and add /bin/python3.X, with the version from the line for 3.X. Keep the quotes: some of these paths have spaces in them.

Fix · one environment at a time
'/path/to/env/bin/python3.X' -m pip install --upgrade 'Brotli>=1.2.1'
  • Change the path between the quotes to that Python. For brotlicffi, use 'brotlicffi>=1.2.1.0'; on an Intel Mac, pip builds brotlicffi 1.2.1.0 from source, which needs Xcode’s command-line tools.
  • A user folder. ~/Library/Python/3.9/… belongs to Apple’s Python: /usr/bin/python3 -m pip install --user --upgrade 'Brotli>=1.2.1'. For another ~/Library/Python/3.X/…, or ~/.local/lib/python3.X/… on Linux: python3.X -m pip install --user --upgrade 'Brotli>=1.2.1'.
  • Apple’s Python (/Library/Python/3.9/…, installed there with sudo): sudo /usr/bin/python3 -m pip install --upgrade 'Brotli>=1.2.1'.
  • Installed with sudo pip on Linux (/usr/local/lib/python3.X/…, /usr/local/lib64/…, or a /usr/lib line no package owns): sudo python3.X -m pip install --upgrade 'Brotli>=1.2.1'.
  • “Externally managed.” If pip refuses with that (Homebrew’s Python, Debian 12, Ubuntu 23.04 and later), add --break-system-packages to the line. Homebrew has no formula for this package, so a Brotli in Homebrew’s Python was installed with pip; you can also remove it and use a virtual environment.
  • uv environments have no pip: uv pip install --python '/path/to/env/bin/python3' --upgrade 'brotli>=1.2.1'. uvx and uv run --with run from uv’s cache, which the search skips: uv cache clean makes them fetch fixed versions next time.
  • conda: conda list -n ENV brotli lists the Brotli packages in an environment, including the library itself (libbrotlidec), which the search above doesn’t see. Update with conda install -n ENV 'brotli-python>=1.2.1' (or 'brotlicffi>=1.2.1.0'), and conda install -n ENV 'libbrotlidec>=1.2.1' if the library is older. On 24 October conda-forge had all three for every platform it builds them for. Anaconda’s own channel had brotli-python 1.2.1 only for Linux (x86-64 and ARM), Apple-silicon Macs and 64-bit Windows, and no fixed brotlicffi; for brotlicffi, and on other platforms, add -c conda-forge to those lines.
  • Lock files put the old version back on the next install: in a uv project, uv lock --upgrade-package brotli then uv sync; with Poetry, poetry update brotli; in a requirements file, raise the pin to Brotli>=1.2.1.

Some Python packages carry their own copy of the Brotli library inside them, which the search above doesn’t see. On our own Mac, the macOS wheels of Pillow 12.3.0 and 13.0.0 (the latest on 24 October) carried libbrotlidec.1.2.0, and opencv-python 5.0.0.93 (also the latest) carried libbrotlidec.1.1.0; copies like these are fixed when those packages ship a release with a fixed Brotli. That comes from looking inside our installed wheels, not from an advisory.

§04 / Bun

Update Bun to 1.4.4.

Bun’s advisory: Bun 1.0.21 to 1.4.3 bundle an affected Brotli, and Bun 1.4.4 bundles the fixed Brotli 1.2.1.

Bun · paste into a terminal
bun --version

It needs to print 1.4.4 or later. Update the way you installed it: bun upgrade for Bun’s own installer, brew upgrade bun for Homebrew, and a 1.4.4 or later image tag in Docker and CI. A program built with bun build --compile carries the Bun it was built with: Bun says it stays affected until you rebuild it with 1.4.4 or later.

§05 / Envoy and Istio

Proxies that unpack Brotli.

Envoy’s advisory covers deployments that have the Brotli decompressor configured; Istio’s covers users who turn Brotli decompression on, for example with an EnvoyFilter. If you use either, check the version you run. Envoy usually runs from a container image, whose tag is its version.

Envoy and Istio · paste into a terminal
envoy --version
istioctl version
ProjectAffectedFixed in
EnvoyThe advisory names 1.39.2, 1.38.5, 1.37.7 and 1.36.11; going by Google’s range, earlier releases on those lines are affected too1.40.0, 1.39.3, 1.38.6, 1.37.8, 1.36.12
Istio1.31.0 to 1.31.1, 1.30.0 to 1.30.5, 1.29.0 to 1.29.81.31.2, 1.30.6, 1.29.9 (ISTIO-SECURITY-2026-007)

istioctl version also asks your current cluster for its versions, which only reads; add --remote=false to see the local tool alone. Istio says 1.29.9 is the last 1.29 release, as 1.29 reached end of life on 15 October: plan a move to 1.30 or later. On an older line of either project, move to a fixed line.

§06 / The library itself

The Brotli on your system.

Some programs use the system’s copy of Brotli instead of their own, Homebrew’s Node.js among them. On a Mac with Homebrew, the fix is Brotli 1.2.1.

Homebrew · paste into Terminal
brew list --versions brotli

It should list 1.2.1. If it lists only older versions, run brew upgrade brotli; Homebrew may upgrade programs that depend on it at the same time. Homebrew keeps old versions on disk until you remove them, so it can list two: after the upgrade, 1.2.1 is the one in use, and brew cleanup brotli removes the rest. If it prints nothing, Homebrew’s Brotli isn’t installed; “command not found” means there is no Homebrew.

On Linux, distributions often fix a library without changing its version number, so follow your distribution’s own page for this CVE: Ubuntu, Debian, Red Hat, and install its updates when they arrive. To see what you have: apt list --installed '*brotli*' (Debian, Ubuntu), rpm -qa '*brotli*' (Fedora, Red Hat) or apk list --installed 'brotli*' (Alpine); with MacPorts on a Mac, port installed brotli (the one marked “(active)” should be 1.2.1; if it isn’t, upgrade brotli with MacPorts). On 24 October, Debian had fixed Brotli in 1.0.9-2+deb12u1 (Debian 12) and 1.1.0-2+deb13u1 (Debian 13), and Ubuntu’s page still said “needs evaluation”. Web browsers unpack Brotli on most websites: keep them updated. Other apps that bundle Brotli are fixed when their makers ship an update; Google lists 1.0.10, 1.1.1 and 1.2.1 as the fixed versions to look for.

§07 / Can’t update today?

Narrow the way in, then update.

The exposure is unpacking Brotli data that someone else controls. Some of that can be switched off; much of it can’t, so updating is the fix.

  • Envoy and Istio: remove the Brotli decompressor, and any Istio EnvoyFilter that adds it.
  • Bun: pass decompress: false to each fetch() that talks to a server you don’t control or uses plain http://; Bun then neither asks for Brotli nor unpacks the answer. Don’t pass untrusted data to node:zlib’s Brotli functions or to DecompressionStream with the brotli format. Bun’s advisory says there is no process-wide workaround.
  • Python: requests asks for Brotli, and urllib3 unpacks it, only while a Brotli package can be imported, so removing Brotli and brotlicffi from an environment that doesn’t need them stops that. This comes from urllib3’s own code, not an advisory; check first that nothing in the environment imports Brotli itself.
  • Node.js: its fetch() unpacks Brotli answers by itself, and Node.js’s advisory offers no workaround: update.
§08 / Sources

Sources.

  1. Google’s CVE record, CVE-2026-111497: Brotli 1.0.3 to 1.2.0 affected, apart from 1.0.10 and 1.1.1; fixed in 1.0.10, 1.1.1 and 1.2.1; CVSS 4.0 score 9.2. Published 22 Oct 2026.
  2. Google’s Brotli releases 1.2.1, 1.1.1 and 1.0.10, 21 Oct 2026. The fix itself landed in Brotli’s source code as commit 0ed4268a, “Detect and report invalid stream earlier.”, on 27 Feb 2026; GitHub’s advisory lists it among the fix commits.
  3. Node.js, October 2026 security releases: 22.x, 24.x and 26.x affected; the releases 22.23.4, 24.22.1 and 26.12.1; end-of-life versions always affected.
  4. GitHub’s advisory for the Python packages, GHSA-9v8h-qqgm-4m35: Brotli from 1.0.3 to below 1.2.1, brotlicffi from 1.0.3 to below 1.2.1.0.
  5. Bun, GHSA-663w-cv8g-jj2p: Bun 1.0.21 to 1.4.3 affected, fixed in 1.4.4; fetch unpacks Brotli by itself; decompress: false per request; no process-wide workaround. Published 23 Oct 2026.
  6. Envoy, GHSA-3wvj-j6qm-62mr: fixed in 1.40.0, 1.39.3, 1.38.6, 1.37.8 and 1.36.12. Published 23 Oct 2026.
  7. Istio, ISTIO-SECURITY-2026-007: Istio 1.29.0 to 1.29.8, 1.30.0 to 1.30.5 and 1.31.0 to 1.31.1; upgrade to 1.31.2, 1.30.6 or 1.29.9, the final 1.29 release.
  8. The distributions’ pages for the CVE, read 24 Oct 2026: Debian (its nodejs uses the system Brotli library; Brotli fixed for Debian 12 and 13), Ubuntu (needs evaluation) and Red Hat.
  9. MacPorts’ nodejs26 Portfile, built with --shared-brotli against its brotli port, read 24 Oct 2026.
  10. Package indexes, read 24 Oct 2026: PyPI’s Brotli 1.2.1 and brotlicffi 1.2.1.0 files (no Intel-Mac wheel for brotlicffi), and conda-forge and Anaconda’s brotli-python builds. urllib3’s own code, 2.7.0 and 2.8.0, the latest (urllib3/util/request.py, urllib3/response.py), for when it asks for and unpacks Brotli.
Briefing / Q&A

Briefing: common questions

What is CVE-2026-111497?

A buffer overflow in the decoder of Brotli, Google’s compression library. Google’s record says that in Brotli 1.0.3 to 1.2.0 (apart from 1.0.10 and 1.1.1, which are fixes) a crafted compressed stream lets an attacker who needs no login corrupt memory, read memory, crash the program, or possibly run code, and rates it 9.2 out of 10. Brotli 1.0.10, 1.1.1 and 1.2.1, released on 21 October 2026, fix it.

Is Node.js affected, and which version fixes it?

Yes: Node.js says the 22, 24 and 26 lines are affected, through applications that decompress Brotli with node:zlib, and its fetch() unpacks Brotli answers by itself too (we tested it). Node.js 22.23.4, 24.22.1 and 26.12.1 fix it. Node.js 20 and older get no fix from the Node.js project: move to 22, 24 or 26. Homebrew’s and Debian’s Node.js use the system’s Brotli library instead, so updating that library fixes them. The check lists every node on your path with its version.

Does this affect Python?

Through the Brotli and brotlicffi packages: GitHub’s advisory lists Brotli from 1.0.3 to below 1.2.1 and brotlicffi from 1.0.3 to below 1.2.1.0 as affected. requests asks servers for Brotli, and urllib3 unpacks it, whenever one of them is installed. Each environment has its own copy, so update each one; the check finds them in the usual places. Some packages also carry a Brotli library inside them: we found one in the Mac wheels of Pillow and opencv-python, which are fixed when those packages ship an update.

Are Bun, Envoy and Istio affected?

Bun 1.0.21 to 1.4.3 are, and Bun 1.4.4 fixes it (Bun’s advisory). Envoy and Istio are where Brotli decompression is set up: Envoy 1.40.0, 1.39.3, 1.38.6, 1.37.8 and 1.36.12 fix it (Envoy’s advisory), and Istio 1.31.2, 1.30.6 and 1.29.9 (ISTIO-SECURITY-2026-007).

Does Wardlatch check for this?

Not yet. Wardlatch 0.1.9 doesn’t check for CVE-2026-111497. This page’s checks are by hand: they print versions and change nothing.

What if I can’t update today?

Narrow the way in: remove the Brotli decompressor from Envoy, and any Istio EnvoyFilter that adds it; in Bun, pass decompress: false to each fetch() that talks to a server you don’t control or uses plain http://; in Python, remove the Brotli and brotlicffi packages from environments that don’t need them, so requests and urllib3 stop asking for and unpacking Brotli. Bun says there is no process-wide workaround, and Node.js’s advisory offers none, so updating is the fix.