Back to Blog
BrotliNode.jsPythonCybersecurityVulnerability ManagementLinux

Brotli Flaw CVE-2026-111497: Who Is Exposed and How to Patch Node.js, Python and Linux

CVE-2026-111497 is a memory-corruption flaw in the Brotli decoder used by Node.js, Python and Linux. Who is actually exposed, how to check your real patch level, and how to fix it.

P
Davi Nunes
October 23, 202610 min read

On 21 October 2026, the Brotli project shipped three releases at once (1.2.1, 1.1.1 and 1.0.10) to close CVE-2026-111497, an out-of-bounds write in the Brotli decoder. It is rated 9.2 out of 10 (CVSS v4.0). Given a crafted compressed stream, it can crash the process doing the decompressing, leak that process's memory, or potentially let an attacker run code inside it.

Few teams think of Brotli as a dependency. It is one of the compression formats behind everyday web traffic, its decoder is compiled into Node.js, the most widely used Python HTTP stack decodes it whenever the binding is installed, and it comes as a system library with curl on most Linux servers. Node.js shipped fixes for every supported release line the same day.

Below: who is actually exposed (fewer systems than the headline suggests, though not the ones you would guess), how to check your real patch level (the Brotli version number will mislead you), how to fix it, and what to do while some Linux distributions still have no fixed package.

What the Flaw Actually Is

Brotli data is decoded as a stream. Bytes arrive in chunks, and when the decoder reaches the end of a chunk halfway through something, it reports that it needs more input and keeps the leftover bytes in a small internal buffer until the next chunk arrives.

The bug is in how the decoder handles one particular kind of malformed data, an invalid block switch. Instead of reporting a permanent format error and stopping, the decoder reported it as a temporary need for more input. The caller fed it more, and the decoder kept appending those bytes to an internal buffer only eight bytes long, writing past its end into the memory next to it.

That neighbouring memory holds the decoder's own state, so whoever controls the stream controls what gets overwritten. The possible outcomes, from most to least likely: a crash, disclosure of process memory, and remote code execution with the permissions of the process.

Affected: every Brotli release from 1.0.3 through 1.2.0, which covers everything published since March 2018. Fixed: 1.0.10, 1.1.1 and 1.2.1.

Who Is Exposed

The most important fact about this bug: only decoding is affected. A web server that compresses its responses with Brotli is using the encoder, and the encoder is not involved. If your Brotli exposure is "our CDN and our web server send compressed pages", this bug is not about that.

What matters is whether any of your software decompresses Brotli data that someone else controls. In practice, that happens in four places.

  • Your server fetching addresses it does not control. Webhook senders, link previews, URL importers, scrapers, feed readers, image proxies: anything that downloads from an address a user or a third party supplied. The built-in fetch in Node.js decompresses a response whenever the server labels it as Brotli, whether or not the request asked for Brotli. We tested it: a request that explicitly asks for identity still comes back decoded. Asking only for gzip is not a defence.
  • Your server accepting compressed request bodies. Express 5 decompresses request bodies sent with Content-Encoding: br out of the box. Express 4 rejects them. Other frameworks, API gateways and proxies vary, so check yours.
  • Python services with a Brotli binding installed. The most widely used Python HTTP stack only advertises and decodes Brotli when the Brotli or brotlicffi package is present, and many applications pull one in indirectly, through a scraping framework or an HTTP client extra, without anyone deciding to.
  • Anything built on the system library. On Debian and Ubuntu, curl, FreeType (which uses Brotli for web fonts), the distribution's own Node.js package and its .NET runtime packages all link the system libbrotli. In the Ubuntu 24.04 archive, around forty packages depend on it directly.

Browsers, and desktop applications built on them, carry their own copies of the decoder. Keep them on automatic updates.

How Big This Is

  • Every supported Node.js line was affected. Node.js 22, 24 and 26 all shipped the vulnerable decoder, and all three received fixes on 21 October. Anything older is out of support and gets no fix at all.
  • Eight years of releases. The affected range starts at Brotli 1.0.3, published in March 2018, so practically every copy in production today is in scope until it is updated.
  • Tens of millions of Python installs a month. The Python binding alone was downloaded about 83 million times in the past month, and the alternative binding another 1.7 million.
  • Not every Linux distribution has a fix yet. At the time of writing, Debian and Ubuntu still list their libbrotli packages as vulnerable or awaiting triage. Alpine 3.21 and newer already ship fixed packages.

The honest caveat: at the time of writing, no exploitation has been reported, and the bug is not considered easy to exploit automatically at scale. This is not PHP2Shell, where attacks started before the patch existed. If you also run PHP, that one comes first.

That is a reason to patch this week, calmly, not a reason to skip it. The fix is public, and a public fix points straight at the bug. Even an attacker who never gets code execution can use a crafted stream to crash a process, and a service that crashes on every request it receives is an outage.

Not sure where Brotli runs in your stack? That is the hard part of this one. Request a free Brotli exposure assessment and our security team will map it with you.

Step 1: Find Every Copy, and Check the Right Version

A single server can carry several independent copies of Brotli: the system library, a copy compiled into Node.js, a copy inside a Python package, and another inside every container image. Each one is patched separately, so the inventory has to cover all of them.

Node.js

Do not trust the Brotli version that Node.js reports. The fix was applied to the copy bundled with Node.js without changing its version string: a fully patched Node.js 22.23.4 still reports Brotli 1.1.0, exactly like the vulnerable 22.23.3. Check the Node.js version instead:

bash
node -v

You need 22.23.4, 24.22.1 or 26.12.1, or later on the same line. Node.js 20 and older have no fixed release.

Then check where that Node.js came from, because it decides how you fix it:

bash
ldd "$(which node)" | grep brotli

No output means Node.js carries its own copy. That is the case for the official binaries and the official container images, and only a Node.js upgrade fixes them. If libbrotlidec shows up, your Node.js comes from your distribution and uses the system library, so the system update below is what fixes it.

The system library

bash
# Debian and Ubuntu
dpkg -l 'libbrotli*' | grep ^ii

# RHEL, AlmaLinux, Rocky Linux, Fedora
rpm -qa | grep -i brotli

# Alpine
apk info -v brotli-libs

On Alpine, brotli-libs 1.1.1 or 1.2.1 is fixed. On the other families, compare what you see with your distribution's advisory for CVE-2026-111497. As usual with distribution fixes, the full package version is what counts, not the upstream number inside it.

Python

bash
pip show Brotli brotlicffi

You need Brotli 1.2.1 or brotlicffi 1.2.1.0, or later. Run it in every virtual environment and every image, because each one has its own copy.

Containers

Check inside the running container, not the Dockerfile:

bash
docker exec <container> node -v
docker exec <container> pip show Brotli

An image or SBOM scanner that reads both operating-system and language packages will find the copies nobody remembers.

Step 2: Patch

Node.js

Upgrade to the fixed release for your line. For containers, pin the patch release and rebuild with fresh layers, so the build cannot quietly reuse an old cached base:

dockerfile
FROM node:22.23.4-alpine
bash
docker build --pull --no-cache -t my-app:node-22.23.4 .

Then roll the new image out. A running container never changes because its image was updated. If you are still on Node.js 20 or older, this is the moment to move to 22 or 24: there is no fixed release for your line, and there will not be one.

Python

Upgrade the binding, then update your lockfile so the next build does not bring the old version back:

bash
pip install --upgrade "Brotli>=1.2.1"

If nothing in the service needs Brotli, uninstalling the binding is a legitimate fix: the HTTP stack simply stops decoding it.

The system library

On Alpine 3.21 and newer:

bash
apk upgrade brotli-libs

On Debian and Ubuntu, install the update as soon as it is published:

bash
sudo apt update
sudo apt install --only-upgrade libbrotli1

Until it is, apply the mitigations in Step 4 to every service that decodes untrusted data.

Step 3: Restart What Loaded the Old Library

A library update replaces the file on disk. Every process that loaded the old library keeps running the old code until it restarts. After the update, list the processes still holding the old copy:

bash
sudo grep -l 'libbrotli.*(deleted)' /proc/[0-9]*/maps

Each path that comes back contains the ID of a process that needs a restart. When in doubt, reboot.

Step 4: If You Cannot Patch Yet

Some systems will have to wait: the distribution package is not out, an application is pinned to an old runtime, a release is frozen. The goal until then is simple: stop decoding Brotli that strangers send you. None of this replaces the update.

Reject compressed request bodies at the edge. Very few legitimate clients send Brotli-compressed requests. In Nginx:

nginx
if ($http_content_encoding ~* "br") {
    return 415;
}

Turn off body decompression in the application. In Express 5, express.json({ inflate: false }) rejects compressed request bodies instead of decompressing them.

Fetch untrusted addresses without automatic decompression. The lower-level http and https clients in Node.js do not decompress anything on their own, so use them for user-supplied addresses and refuse responses that arrive as br. In Python, removing the Brotli binding has the same effect.

Isolate the fetchers. Code that downloads from user-supplied addresses belongs in a separate worker with no secrets in its environment, restricted outbound access and tight memory limits, so a memory-corruption bug there reaches nothing worth stealing.

Step 5: Watch for Crashes

Memory-corruption attempts are noisy when they fail, and most attempts fail. A run of crashes in the processes that decode untrusted data is the earliest sign that someone is probing.

On a host, segmentation faults end up in the kernel log:

bash
sudo journalctl -k | grep -i segfault

On Kubernetes, look for containers that restart with exit code 139, the code for a segmentation fault:

bash
kubectl get pods -A
kubectl describe pod <pod> | grep -A3 'Last State'

A crash you cannot explain, in a web-facing Node.js or Python process, deserves a look at what it was processing when it died.

The Real Problem Is the Copy You Did Not Know About

Patching Brotli is not hard. Finding it is. It is missing from most dependency lists because nobody chose it: it came with the runtime, with an HTTP client extra, with the operating system, with a base image. That is how a bug in a compression library stays in production for months after the fix, on servers whose owners are sure they patched.

The useful question is not "did we update?" but "which of our services decompress data from outside, and which copy of the decoder does each one use?"

Get a Free Brotli Exposure Assessment

If you are not sure where Brotli runs in your stack, or which of your services decode data an attacker could send, our security team will work it out with you at no cost.

The free assessment covers:

  • An inventory of every copy of Brotli across your servers, container images, runtimes and language packages, with the real patch status of each one rather than its version string
  • A map of the paths that decode untrusted data: outbound fetches of user-supplied addresses, compressed request bodies, uploaded files
  • A prioritised plan for anything that cannot be patched yet, including systems waiting on a distribution package, with the mitigations that fit each case

You get a written list of what is exposed and what to do about it, with no obligation attached.

Request your free Brotli exposure assessment, or read more about our cybersecurity practice and how our DevSecOps work keeps base images patched and rebuilt before advisories like this one land.

Brotli CVE-2026-111497: How to Fix | Privum Consulting