# Brotli CVE-2026-111497: Find and Patch Every Copy

> CVE-2026-111497 is an out-of-bounds write in Google's Brotli decoder, now on CISA's KEV list. It ships inside Node.js, Python packages, Go modules, Envoy and more, and patched builds can still report the old Brotli version. Here is how to find every copy and check it for the fix.

- URL: https://firewallsync.com/posts/brotli-cve-2026-111497-find-and-patch-every-copy/
- Site: FirewallSync (https://firewallsync.com/)
- Topic: Security Operations
- Tags: vulnerability management, supply chain, node.js, python, remote code execution
- Published: 2026-10-28
- Author: John Marshall

## Key takeaways

- CVE-2026-111497 is an out-of-bounds write in Google Brotli's decoder, versions 1.0.3 through 1.2.0. Fixed upstream releases are 1.0.10, 1.1.1 and 1.2.1. CISA added it to the KEV catalog on October 27, 2026, with a federal due date of November 10.
- The bug is in the decoder, so the exposure is anything that decompresses Brotli data an attacker controls: request bodies, responses from URLs an attacker can influence, uploaded files.
- Node.js fixed it in 22.23.4, 24.22.1 and 26.12.1, but those builds still report process.versions.brotli as 1.1.0 or 1.2.0. Check the Node.js version, not the Brotli version.
- Python's Brotli (1.2.1) and brotlicffi (1.2.1.0), Go's andybalholm/brotli (v1.2.7), Envoy, Istio and Bun all shipped fixes. requests and urllib3 start advertising Brotli as soon as either Python package is installed.
- For vendored copies, check the code rather than the version string. Our script below spots the fix in decode.c or decode.go and correctly classified 12 upstream, Node.js and Go sources.



CVE-2026-111497 is a memory-corruption bug in Google's Brotli decompressor. Upstream versions 1.0.3 through 1.2.0 are affected, and 1.0.10, 1.1.1 and 1.2.1 fix it. CISA added it to the [Known Exploited Vulnerabilities catalog](https://www.cisa.gov/known-exploited-vulnerabilities-catalog) on October 27, 2026, with a federal remediation date of November 10. The hard part is not the patch. Brotli is compiled into Node.js, Python wheels, Go modules, proxies and service meshes, and several patched builds still report the old version number. Here is where it hides, what "fixed" looks like for each, and a tested way to check code you vendor yourself.

## Are you affected?

You are exposed if a vulnerable Brotli decoder processes data an attacker can supply. The [CVE record](https://www.cve.org/CVERecord?id=CVE-2026-111497) says an attacker with "a crafted Brotli-compressed stream" can "corrupt adjacent decoder state memory, disclose sensitive memory contents, cause a denial of service, or potentially execute arbitrary code". Google's CNA scored it CVSS 4.0 9.2 (Critical). The Node.js project rates its copy High.

| Where Brotli lives | Affected | Fixed in | How to check | Source |
|---|---|---|---|---|
| Google Brotli / libbrotli | 1.0.3 to 1.2.0 | 1.0.10, 1.1.1, 1.2.1 | Package version, or the code check below for vendored copies | CVE record, upstream CHANGELOG |
| Node.js 22, 24, 26 | Earlier builds on those lines | 22.23.4, 24.22.1, 26.12.1 | `node -v` (not `process.versions.brotli`) | Node.js advisory |
| End-of-life Node.js | Assume affected | None: upgrade | `node -v` | Node.js advisory |
| Python `Brotli` | 1.0.3 to 1.2.0 | 1.2.1 | `pip show Brotli` | Aikido; PyPI release date |
| Python `brotlicffi` | 1.2.0.x | 1.2.1.0 | `pip show brotlicffi` | Aikido; PyPI release date |
| Go `github.com/andybalholm/brotli` | Before v1.2.7 | v1.2.7 | `go list -m github.com/andybalholm/brotli` | CVE record reference, source diff |
| Envoy | 1.36.11, 1.37.7, 1.38.5, 1.39.2 | 1.36.12, 1.37.8, 1.38.6, 1.39.3, 1.40.0 | `envoy --version` | Wiz (qualified); we checked 1.39.2 against 1.39.3 |
| Istio | 1.29.0 to 1.29.8, 1.30.0 to 1.30.5, 1.31.0 to 1.31.1 | 1.29.9, 1.30.6, 1.31.2 | `istioctl version` | Wiz (qualified) |
| Bun | 1.0.21 to 1.4.3 | 1.4.4 | `bun --version` | Wiz (qualified) |
| Distro packages (libbrotli1, brotli) | Depends on backport | Distro security update | Your distro's security tracker | — |

The Envoy, Istio and Bun ranges come from [Wiz's write-up](https://www.wiz.io/blog/cve-2026-111497-brotli-rce-node-js), not from those projects' advisories. We did not read those advisories directly. For Envoy we checked the source instead: `bazel/repository_locations.bzl` pins Brotli `1.2.0` at tag v1.39.2, while v1.39.3 pins a Brotli commit whose `decode.c` contains the fix.

Linux distributions usually backport fixes and keep the upstream version. Our Ubuntu 24.04 sandbox, for example, has `libbrotli1 1.1.0-2ubuntu0.1`. That string alone does not tell you whether the fix is in. Use your distribution's security tracker for the CVE.

### Why the version string can mislead you

Node.js patched its bundled Brotli in place. On the fixed Node.js 22.23.4 the reported library version is still the old one:

```console
$ node -v
v22.23.4
$ node -p process.versions.brotli
1.1.0
```

We ran that in our sandbox. The source tree agrees: `deps/brotli/c/common/version.h` declares 1.1.0 at both v22.23.3 and v22.23.4, and 1.2.0 at v24.22.1 and v26.12.1, but only the fixed tags contain the patched decoder (v22.23.3 and v24.22.0 do not). A scanner that flags `process.versions.brotli < 1.2.1` will raise false positives on fixed Node.js. A scanner that trusts a vendored `version.h` can miss real exposure the other way round. For Node.js, use the Node.js version. For anything else you vendor, check the code.

## What the bug is

The [Node.js advisory](https://nodejs.org/en/blog/vulnerability/october-2026-security-releases) gives the clearest description: "An invalid block switch was reported as a temporary need for more input instead of a permanent format error, so the streaming decoder kept appending input bytes to an eight-byte internal buffer."

The upstream diff between v1.2.0 and v1.2.1 of `c/dec/decode.c` shows exactly that. `DecodeBlockTypeAndLength()` used to return a plain boolean, so "invalid stream" and "need more input" looked the same, and the non-safe callers ignored the result altogether (`static void DecodeLiteralBlockSwitch(...)`). After the fix the function returns a `BrotliDecoderErrorCode`, emits `BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH` for the invalid case, and every caller checks it through a new `BROTLI_SAFE_WITH_STATUS` macro. That matches the two weaknesses in the CVE record: CWE-252 (unchecked return value) and CWE-787 (out-of-bounds write).

The flaw was found by Khai Tran and Sylvie Mayer of OpenAI Codex Security, who are credited in both the CVE record and the Node.js advisory.

## Where untrusted Brotli reaches your code

The flaw is in decompression, so look for places that decode data you did not produce:

- **Request bodies.** Any endpoint or middleware that inflates `Content-Encoding: br` uploads.
- **Outbound HTTP.** HTTP clients decode Brotli responses for you. If an attacker controls or can redirect the URL (webhooks, link previews, feed fetchers, scrapers), they control the compressed bytes.
- **Files and archives** that contain Brotli streams.
- **Proxies and meshes** that decompress traffic to inspect or transform it.

Two behaviours we checked, because they mean code that never mentions Brotli can still run it:

- **Node.js `fetch()` decodes Brotli it never asked for.** On Node.js 22.23.4, `fetch()` sent `accept-encoding: gzip, deflate`. When our local test server replied with `content-encoding: br`, `fetch()` decompressed the body anyway. A hostile server can choose Brotli whatever the client advertises.
- **Python's requests turns Brotli on when it is installed.** urllib3's `util/request.py` appends `br` to its default `Accept-Encoding` when `brotlicffi` or `brotli` can be imported. With Brotli 1.2.1 installed, `requests.utils.default_headers()` returned `gzip, deflate, br` in our test. The decoding then runs in whichever Brotli package is installed.

## What to do now

1. **Update the runtimes.** Node.js to 22.23.4, 24.22.1 or 26.12.1 or later. Move off end-of-life Node.js lines: the advisory notes that "End-of-Life versions are always affected when a security release occurs."
2. **Update language packages** in every lockfile and image: `Brotli>=1.2.1`, `brotlicffi>=1.2.1.0`, `github.com/andybalholm/brotli v1.2.7`. Both Python fixes were published to PyPI on October 22, 2026.
3. **Update proxies and meshes** (Envoy, Istio) and Bun to the fixed releases above. Confirm the ranges against each project's own advisory.
4. **Apply distro updates** for `libbrotli`/`brotli`, then restart the long-running processes that loaded the old library.
5. **Rebuild container images.** A fixed base image does not help a running container that was built from the old one.
6. **Hunt for vendored copies** with the check below.

If you cannot patch a service today, reduce what it decodes. As a stopgap, reject `Content-Encoding: br` request bodies at the edge for services that do not need them, and stop server-side fetchers from decoding responses from untrusted URLs. This is our editorial suggestion, not vendor guidance, and it does not replace the update.

### Inventory commands

```sh
# Node.js: the version that matters
node -v

# Python: every environment and image
pip show Brotli brotlicffi 2>/dev/null | grep -E '^(Name|Version)'
pipdeptree -r -p Brotli          # who pulls it in

# Go modules
go list -m all | grep andybalholm/brotli

# Debian/Ubuntu and RHEL-family system libraries
dpkg -l 'libbrotli*' 2>/dev/null | awk '/^ii/{print $2, $3}'
rpm -q brotli libbrotli 2>/dev/null
```

The `pip`, `node` and `dpkg` lines were run in our sandbox. The `go`, `pipdeptree` and `rpm` lines are standard tool usage that we did not run here.

### Checking vendored source for the fix

C and C++ projects often copy `c/dec/decode.c` into their tree, and Go programs carry `decode.go` in the module cache or a `vendor/` directory. This script looks for the patched function signature instead of trusting any version string:

```sh
#!/bin/sh
# brotli-fixcheck.sh DIR...: find vendored Brotli decoder sources and report
# whether they carry the CVE-2026-111497 fix. Looks at the code, not the
# version string, because patched Node.js still reports the old version.
for root in "$@"; do
  find "$root" \( -name decode.c -path '*dec*' \) -o \( -name decode.go -path '*brotli*' \) 2>/dev/null |
  while read -r f; do
    if grep -q 'BrotliDecoderErrorCode DecodeBlockTypeAndLength' "$f" ||
       grep -q 'func decodeBlockTypeAndLength(.*) int {' "$f"; then
      echo "PATCHED    $f"
    elif grep -q 'DecodeBlockTypeAndLength\|decodeBlockTypeAndLength' "$f"; then
      echo "VULNERABLE $f"
    fi
  done
done
```

We tested it against 12 sources pulled from GitHub: upstream Brotli v1.0.9, v1.0.10, v1.1.0, v1.1.1, v1.2.0 and v1.2.1; Node.js `deps/brotli` at v22.23.3, v22.23.4, v24.22.0 and v24.22.1; and `andybalholm/brotli` v1.2.6 and v1.2.7. It reported every fixed release as `PATCHED` and every earlier one as `VULNERABLE`. Run it over source trees, `vendor/` directories and `$(go env GOMODCACHE)`.

It cannot inspect compiled binaries, and a heavily modified fork may not match either pattern. Treat "no output" as "no recognisable copy found", not as "safe".

## What to hunt for

No vendor or agency has published indicators of compromise for this CVE, and CISA's KEV entry does not require forensic triage (`forensicTriage: No`). Practical checks:

- **Crashes.** Segfaults or aborts in Node.js, Python or proxy processes since late October, especially in services that fetch remote content or accept compressed uploads.
- **Unexpected Brotli.** Requests with `Content-Encoding: br` to endpoints that never receive compressed bodies. Wiz recommends reviewing WAF, application and network logs for suspicious Brotli-compressed traffic.
- **Outbound fetches** from services that took user-supplied URLs, close in time to a crash.

## Limits of this article

We did not reproduce the memory corruption or test any exploit. Our checks cover version strings, source code and HTTP client behaviour in a sandbox: Node.js 22.23.4, Python with Brotli 1.2.1, Ubuntu 24.04. The Envoy, Istio and Bun ranges come from a security vendor's summary. The list of affected software is not complete. Wiz notes that other products that bundle or link vulnerable versions of the Brotli C decoder may also be affected.

For the wider process of keeping dependency updates moving, see [dependency audits without slowing down releases](/posts/dependency-audits-without-slowing-down-releases/). For publishing or reading "not affected" statements about a library like this, see [VEX explained](/posts/vex-explained-what-a-not-affected-statement-means-and-how-to-read-one/). Last week's [PHP2Shell patch-and-hunt guide](/posts/php2shell-cve-2026-109618-cve-2026-109619-patch-and-hunt/) covers the two PHP runtime flaws CISA added to KEV the day before.

## Sources and verification

| Important claim | Source | Status |
|---|---|---|
| Affected 1.0.3–1.2.0; fixed 1.0.10, 1.1.1, 1.2.1; CVSS 4.0 9.2; CWE-252/787; credit | [CVE-2026-111497 record (cvelistV5)](https://github.com/CVEProject/cvelistV5/blob/main/cves/2026/111xxx/CVE-2026-111497.json) | Verified |
| KEV added 2026-10-27, due 2026-11-10, forensicTriage No; SSVC exploitation active | [CISA KEV data](https://github.com/cisagov/kev-data); CISA ADP in the CVE record | Verified |
| 1.2.1 released 2026-10-21 with the CVE fix; code change | [google/brotli CHANGELOG](https://github.com/google/brotli/blob/v1.2.1/CHANGELOG.md); `c/dec/decode.c` v1.2.0 vs v1.2.1 | Verified |
| Node.js fixed releases, root cause, EOL statement | [Node.js October 2026 security releases](https://nodejs.org/en/blog/vulnerability/october-2026-security-releases) | Verified |
| Patched Node.js reports the old Brotli version | `node -p process.versions.brotli` on v22.23.4; `deps/brotli` at the listed tags | Verified (our test) |
| Go fix in andybalholm/brotli v1.2.7 | [Release v1.2.7](https://github.com/andybalholm/brotli/releases/tag/v1.2.7); `decode.go` v1.2.6 vs v1.2.7 | Verified |
| Python Brotli 1.2.1, brotlicffi 1.2.1.0 | [Aikido](https://www.aikido.dev/blog/python-packages-vulnerable-node2shell-brotli-cve-2026-111497); PyPI upload dates 2026-10-22 | Verified |
| urllib3/requests advertise `br` when Brotli is installed | [urllib3 `util/request.py`](https://github.com/urllib3/urllib3/blob/main/src/urllib3/util/request.py); our test | Verified |
| Envoy, Istio, Bun ranges | [Wiz](https://www.wiz.io/blog/cve-2026-111497-brotli-rce-node-js); Envoy `repository_locations.bzl` at v1.39.2/v1.39.3 | Qualified |
| Envoy and other consumers affected | [oss-security, 2026-10-23](https://www.openwall.com/lists/oss-security/2026/10/23/25), [2026-10-24](https://www.openwall.com/lists/oss-security/2026/10/24/6) | Verified |

## Frequently asked questions

### Is CVE-2026-111497 being exploited?

CISA added it to the Known Exploited Vulnerabilities catalog on October 27, 2026, and its CVE record rates exploitation as 'active'. Neither CISA nor the vendors have published details of the attacks. Wiz writes that researchers have demonstrated exploitation against unpatched Node.js.

### My Node.js says process.versions.brotli is 1.1.0. Am I vulnerable?

Not necessarily. Node.js applied the fix to its bundled copy without changing the version string. Node.js 22.23.4 reports Brotli 1.1.0 but contains the fix. Go by the Node.js version: 22.23.4, 24.22.1 or 26.12.1 and later are fixed.

### Does this affect web servers that only compress responses?

The flaw is in the decoder. Compressing your own responses does not decode untrusted data. Risk comes from decompressing data someone else controls: request bodies, upstream or third-party responses, or files. Patch anyway: many servers and proxies link the full library.

### Which Python packages do I need to update?

Brotli to 1.2.1 and brotlicffi to 1.2.1.0. Both build the Brotli C decoder. requests, urllib3 and other HTTP clients use whichever one is installed, so updating these two packages covers those clients too.


Image credit: Photo by [Haberdoedas](https://unsplash.com/photos/knoPXXK07MQ) on Unsplash
