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.

Round straw bales stacked in tight columns, each bound with thin blue netting
Photo by Haberdoedas on Unsplash

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 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 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 livesAffectedFixed inHow to checkSource
Google Brotli / libbrotli1.0.3 to 1.2.01.0.10, 1.1.1, 1.2.1Package version, or the code check below for vendored copiesCVE record, upstream CHANGELOG
Node.js 22, 24, 26Earlier builds on those lines22.23.4, 24.22.1, 26.12.1node -v (not process.versions.brotli)Node.js advisory
End-of-life Node.jsAssume affectedNone: upgradenode -vNode.js advisory
Python Brotli1.0.3 to 1.2.01.2.1pip show BrotliAikido; PyPI release date
Python brotlicffi1.2.0.x1.2.1.0pip show brotlicffiAikido; PyPI release date
Go github.com/andybalholm/brotliBefore v1.2.7v1.2.7go list -m github.com/andybalholm/brotliCVE record reference, source diff
Envoy1.36.11, 1.37.7, 1.38.5, 1.39.21.36.12, 1.37.8, 1.38.6, 1.39.3, 1.40.0envoy --versionWiz (qualified); we checked 1.39.2 against 1.39.3
Istio1.29.0 to 1.29.8, 1.30.0 to 1.30.5, 1.31.0 to 1.31.11.29.9, 1.30.6, 1.31.2istioctl versionWiz (qualified)
Bun1.0.21 to 1.4.31.4.4bun --versionWiz (qualified)
Distro packages (libbrotli1, brotli)Depends on backportDistro security updateYour distro’s security tracker—

The Envoy, Istio and Bun ranges come from Wiz’s write-up, 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:

$ 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 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

# 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:

#!/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. For publishing or reading “not affected” statements about a library like this, see VEX explained. Last week’s PHP2Shell patch-and-hunt guide covers the two PHP runtime flaws CISA added to KEV the day before.

Sources and verification

Important claimSourceStatus
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; creditCVE-2026-111497 record (cvelistV5)Verified
KEV added 2026-10-27, due 2026-11-10, forensicTriage No; SSVC exploitation activeCISA KEV data; CISA ADP in the CVE recordVerified
1.2.1 released 2026-10-21 with the CVE fix; code changegoogle/brotli CHANGELOG; c/dec/decode.c v1.2.0 vs v1.2.1Verified
Node.js fixed releases, root cause, EOL statementNode.js October 2026 security releasesVerified
Patched Node.js reports the old Brotli versionnode -p process.versions.brotli on v22.23.4; deps/brotli at the listed tagsVerified (our test)
Go fix in andybalholm/brotli v1.2.7Release v1.2.7; decode.go v1.2.6 vs v1.2.7Verified
Python Brotli 1.2.1, brotlicffi 1.2.1.0Aikido; PyPI upload dates 2026-10-22Verified
urllib3/requests advertise br when Brotli is installedurllib3 util/request.py; our testVerified
Envoy, Istio, Bun rangesWiz; Envoy repository_locations.bzl at v1.39.2/v1.39.3Qualified
Envoy and other consumers affectedoss-security, 2026-10-23, 2026-10-24Verified

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.

Vulnerability ManagementSupply ChainNode.jsPythonRemote Code Execution