BLOG

CVE-2026-111497 Brotli Bug Reaches Far Beyond Node.js

Welcome to the Brotlipocalypse: it's like Log4j, but with a super popular compression algorithm you haven't heard of.

By c0a15726-c5b1-4b0d-85e6-fe15553df9e2 ·

CVE-2026-111497 Brotli Bug Reaches Far Beyond Node.js

So, about that Brotli bug…

On October 22, we showed that the PyPI Brotli package ships the same vulnerable decoder as Node.js. We disclosed the vulnerability in the brotli and brotlicffi packages to the Python Security team. They created two new advisories (PYSEC-2026-4322 and PYSEC-2026-4323), and then GHSA created GHSA-9v8h-qqgm-4m35.

We took a minute to breathe, then asked the obvious next question: What other technologies copied this vulnerable code?

Unfortunately, the answer is A lot because Brotli’s C decoder is small, has no dependencies, and it's easy to drop into a project. So it got dropped into language runtimes, database engines, font renderers, HTTP clients, native extensions for Ruby, PHP, Java and Rust, and plenty of things nobody remembers adding. Each of those copies has to be fixed separately, and many won't be fixed by updating Node.js or your Linux distribution’s libbrotli package.

Recap of Node2Shell

Brotli is a lossless data compression algorithm developed by Google that shrinks text-based web content (like HTML, CSS, and JavaScript) so websites load faster. CVE-2026-111497, nicknamed Node2Shell, is a high-severity out-of-bounds write vulnerability found in the Brotli compression decoder. The official CVE record describe it as "a buffer overflow vulnerability in the Brotli decoder c/dec/decode.c) in Google Brotli versions 1.0.3 through 1.2.0 on all platforms allows a remote unauthenticated attacker to corrupt adjacent decoder state memory, disclose sensitive memory contents, cause a denial of service, or potentially execute arbitrary code using a crafted Brotli-compressed stream. Users need to update any Brotli libraries or binaries between 1.0.3 through 1.2.0 to the latest available version: 1.0.10 or 1.1.1 or 1.2.1." Disclosed by OpenAI Codex Security, Node2Shell has as 9.4 severity rating on CVE v4, and it's associated with two CWEs:

To explain a little more simply, when a stream asks for a block-type switch that it's not allowed to make, the old decoder reports “I need more input” instead of “This stream is broken”. A streaming decoder then keeps appending input into an 8-byte internal buffer, and writes past the end of it. Imperva showed this turns into code execution in Node.js.

  • Affected: google/brotli 1.0.3 through 1.2.0

  • Fixed: 1.0.10, 1.1.1 and 1.2.1, all tagged 21 October 2026

  • Fix commit: 0ed4268a (“Detect and report invalid stream earlier”)

  • Only the decoder is affected. Software that only compresses with Brotli (for example nginx’s ngx_brotli filter or Apache’s mod_brotli) is not.

We think there's a detail that should be getting a lot more attention. The fix commit landed on google/brotli master on 27 February 2026, with no release and no advisory. It sat in public for eight months before anyone called it a security fix. Anyone diffing that repository had the bug long before the general public.

Why the brotli emergency reminds us of Log4j

We worked through the Log4j incident, and spent weeks trying to find it, document it and fix it. That vulnerability is famous because that logging utility was used EVERYWHERE. It was in software you wrote. It was in software you bought. Hell, it was even in hardware you had in your datacenter.

This vulnerability reminds us of that experience. We hope this isn't going to be as disruptive, but the reality is that Brotli, like Log4j, is used EVERYWHERE. And, in our testing, its relatively easy to at least get denial of service, if not RCE.

We think Node2Shell should be renamed Brotlipocalypse because the other floating around aren't descriptive of the real threat. Node2Shell, Python2Shell.... naw, we're gonna call this Brotlipocalypse.

How we identified affected projects

We started with what was already public:

  • The CVE record

  • Distro trackers (Debian, Red Hat, Amazon Linux)

  • Issues and pull requests mentioning CVE-2026-111497

  • Lists of packages that depend on the system libbrotli

Then we added projects known to bundle or wrap Brotli in each major ecosystem: Python, JavaScript, Ruby, PHP, Perl, Rust, Go, Java and .NET, plus browsers, proxies and databases.

For each project, we looked at the code on its default branch and its latest release, not at news coverage or scanner output. Two checks cover almost every case.

If a project vendors the Brotli source, the fix adds a new error code. If the code below finds nothing, the copy is not fixed:

grep -r BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH path/to/vendored/brotli/

If a project pulls Brotli in as a git submodule, we checked whether the pinned commit contains the fix (or one of its three release backports):

git -C google-brotli merge-base --is-ancestor 0ed4268a <submodule-sha> && echo fixed

Where a project downloads Brotli at build time, we read the pinned version from its build files.

Using this method, we identified severalopen-source projects that are affected by this vulnerability. We've reached out to them using multiple platforms and processes, and are working through the disclosure process. Some of these projects are huge, with hundreds of millions of users.

We'll update this section over the next several days with a list of organizations and projects. Stay tuned.

  • October 24 updates: Bazel Brotli module and Cloudflare impacted (see below)

Every Brotli module in the Bazel Central Registry is unpatched

The Bazel Central Registry is the default package registry for Bazel, the build system Google open sourced. When a Bazel project depends on brotli, this is usually where it comes from. As of October 24, the registry has five Brotli modules (1.1.0, 1.2.0, 1.2.0.bcr.1, 1.2.0.bcr.2, and 1.2.0.bcr.3), and none of them contain the fix for CVE-2026-111497.

Each module downloads an upstream Brotli release, either v1.1.0 or v1.2.0, and both are in the affected range. Each one then applies a few registry patches. We checked every patch. They change build files and encoder code, but none of them touch the decoder files the fix changes (c/dec/decode.c and c/include/brotli/decode.h). The .bcr suffix marks a registry packaging revision, not an upgrade, so 1.2.0.bcr.3 is still Brotli 1.2.0 underneath.

That means there’s no fixed version in the registry to upgrade to. Google tagged 1.2.1, 1.1.1, and 1.0.10 on October 21, but none of them has been added to the registry yet. Until one is, any project that takes Brotli from the registry builds with the vulnerable decoder unless it overrides the source itself.

Cloudflare’s open source workerd still builds with the unpatched module

Cloudflare’s open source Workers runtime, workerd, builds Brotli from the unpatched registry module, and as of October 24 no public fix has shipped.

Its dependency file pins brotli@1.2.0.bcr.3. The same file uses overrides to point other dependencies, like BoringSSL and zlib, at specific sources, but there’s no override for Brotli. Cloudflare did make a Brotli-related change on October 19 (c0b30c49), but that commit fixes a separate encoder issue with overlapping buffers, not this decoder bug.

We tested a local workerd instance by feeding malformed Brotli data to its Node.js-compatible brotliDecompressSync() function (we never touched Cloudflare’s production network, which their bounty terms prohibit). The malformed input reached the decoder and returned RangeError: Memory limit exceeded, and the Worker stayed up. That error appears to come from workerd’s own output-size limit, so this test doesn’t show memory corruption or a crash.

Juliano Rizzo listed workerd on X on October 22 among software carrying unpatched Brotli decoders. Our research traced the cause to the Bazel Central Registry module, and we reported it through Cloudflare’s HackerOne program. Cloudflare confirmed the Brotli dependency concern and said it had already identified the same decoder defect internally, so it closed our report as Informational. Cloudflare’s Mark Dembo has since replied to Rizzo’s post saying production Workers are already patched and a workerd fix is coming.

We haven’t found a public pull request that changes workerd's Brotli dependency yet. Until that fix ships, anyone running open source workerd themselves is building with the vulnerable decoder source.

DuckDB bundles an unpatched Brotli decoder, and a fix is waiting for review

DuckDB is widely-used, in-process analytics database that’s embedded in a long list of data tools. For example, Okta’s defensive cyber operations team has described running thousands of concurrent DuckDB instances to process 7.5 trillion security records. DuckDB carries its own copy of Brotli 1.1.0 to read and write Brotli-compressed Parquet files, and that copy doesn’t include the fix. Updating your system’s libbrotli won’t change that.

We started preparing a disclosure, then found the issue was already public. A user opened issue #27660 on October 22 noting that DuckDB’s bundled Brotli lacks the fix as of main and v2.0.0, and DuckDB labeled it “reproduced.” A contributor followed on October 23 with pull request #27791, which ports the upstream fix into DuckDB’s copy. The PR author reports that the current decoder runs past the invalid block switch in the Node.js regression test, while the patched one rejects it with the new BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH error. As of October 24, the PR is open with no maintainer reviews.

Some downstream users aren’t waiting. PostHog has already patched its own DuckDB fork and moved to refuse Brotli as a Parquet codec in its DuckLake fork.

The issue describes the urgency as low, because DuckDB assumes Parquet files come from trusted writers. Your risk depends on whether you read Brotli-compressed Parquet files from sources you don’t control. Until a DuckDB release includes the fix, don’t.

Projects that already shipped fixes

Credit where it’s due, there are several projects that have already shipped fixes:

Project

Fixed in

Node.js

22.23.4, 24.22.1, 26.12.1

Bun

1.4.4

PyPI Brotli

1.2.1

PyPI brotlicffi

1.2.1.0

Perl IO::Compress::Brotli

0.023

andybalholm/brotli (Go)

v1.2.7

Unity’s Mono fork

fix merged 22 October

Envoy

latest patch release on each supported line

Chrome / Firefox / Electron

Chrome 148+, Firefox 150+ / ESR 140.10+, Electron 42+ (fixed by re-vendoring, no advisory)

vcpkg, Conan Center, Homebrew, MSYS2, conda-forge

Brotli 1.2.1 available

How to discover if you're vulnerable + remediation steps

  1. Don’t stop at Node.js. Updating Node or your distro’s libbrotli does nothing for software that carries its own copy. The .NET runtime, DuckDB, Pillow, pyarrow and the native extensions above all do.

  2. Search for bundled copies. Run the grep above over your vendored code, your container images and your site-packages / node_modules / gem directories. Look for files named decode.c or decode.cpp next to brotli in the path.

  3. Upgrade what you can: PyPI Brotli>=1.2.1, brotlicffi>=1.2.1.0, andybalholm/brotli>=1.2.7, Node.js and Bun as above.

  4. Where no fix exists yet, stop decoding Brotli from untrusted sources. Remove br from Accept-Encoding on outbound requests (for .NET, leave DecompressionMethods.Brotli off), reject Content-Encoding: br on inbound requests, and don’t open Brotli-compressed Parquet files or WOFF2 fonts from people you don’t trust.

  5. Check your browsers, proxies and desktop apps: Chrome 148+, Firefox 150+ or ESR 140.10+, the latest Envoy patch release, and Electron 42+ for anything built on Electron.

  6. Replace abandoned packages (iltorb, jvm-brotli, brotlipy, brotli-sys).

  7. Rebuild. Most of these copies are compiled into wheels, gems, JARs and container images. A fixed library does nothing until the thing that embeds it is rebuilt.