Node.js CVE-2026-111497: the Brotli bug your code never calls.
On 21 October 2026 the Node.js project shipped security releases for every supported line, fixing ten vulnerabilities. The one that matters most sits in the Brotli decompressor bundled inside Node — and the commonest way to reach it is through code you did not write, because the built-in fetch(), Express 5 and the popular HTTP clients all decompress Brotli automatically.
VR
SemperWise Vulnerability Research·
13 min read
·
Vulnerability Intelligence
10CVEs fixed in one release
9.2CVSS v4.0, as scored by Google
3 lines22.x, 24.x and 26.x patched
No fixFor Node.js 20 and older
What happened on 21 October
The Node.js project released v22.23.4 (LTS “Jod”), v24.22.1 (LTS “Krypton”) and v26.12.1 (Current). Between them they fix ten Node.js CVEs — four rated High, three Medium and three Low by the project — and update five bundled components: c-ares, OpenSSL (on 22.x and 24.x), SQLite (on 22.x), undici, and the root-certificate store.
That last change is the Node.js response to the compromise of eight eMudhra intermediate certificate authorities, which we cover separately in the eMudhra CA compromise. It landed the same day as critical releases from PHP and GitLab, and an Unbound DNS fix — an unusually heavy day of patching for anyone who runs web infrastructure.
CVE-2026-111497 in plain terms
Brotli is a compression format, used heavily on the web as Content-Encoding: br. Node.js ships Google’s C implementation of the Brotli decoder inside the runtime and exposes it through node:zlib.
The Node.js advisory describes the defect precisely: “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.” Every byte written past the end of that buffer corrupts the decoder’s neighbouring memory. In the project’s words, an attacker who can supply Brotli data that an application decompresses “can corrupt process memory, which can lead to a crash, disclosure of process memory, or remote code execution.”
This is an upstream bug, not a Node.js-only one. Google, which assigned the CVE, lists Brotli 1.0.3 through 1.2.0 as affected and released fixed versions 1.0.10, 1.1.1 and 1.2.1 on 21 October. The same CVE therefore covers the Python brotli and brotlicffi packages (fixed in 1.2.1 and 1.2.1.0), Envoy deployments with the Brotli decompressor configured, and the libbrotli packages shipped by Linux distributions.
One detail deserves attention. The upstream fix — commit 0ed4268a, “Detect and report invalid stream earlier.” — was authored on 27 February 2026 and sat in Brotli’s public repository for almost eight months before any tagged release contained it. The patch that shows where the bug is has been readable by anyone for most of the year.
How severe? Four official readings of one bug
Who
Rating
What it reflects
Node.js project
High
No CVSS number is published in the advisory.
Google (the CVE assigner)
CVSS v4.0 9.2 — Critical
AV:N/AC:L/AT:P/PR:N/UI:N — network-reachable and unauthenticated, but the target must actually decompress attacker input.
Red Hat
CVSS 3.1 8.1 — Important
Rates attack complexity as High; Amazon Linux publishes the same score.
CISA (SSVC, 23 Oct)
Exploitation: none · Automatable: no · Technical impact: total
A decision aid rather than a score — “total” means full control if it works.
Scores published by the parties responsible, as of 23 October 2026. Figures quoted elsewhere that match none of these should be treated with caution.
Why you can be exposed without ever calling zlib
The advisory’s condition — an application that decompresses attacker-supplied Brotli with node:zlib — sounds narrow. It is not, because most of that decompression happens inside libraries, automatically, keyed on a header the other side controls.
Code path
Decodes Brotli automatically?
Who can send the malicious data
Built-in fetch() (bundled undici)
Yes — any response carrying Content-Encoding: br; over HTTPS it also advertises br by default
Any server your application fetches from: webhooks, link previews, URL importers, partner APIs, anything an SSRF can point at
Express 5 / body-parser 2.x
Yes — request bodies with Content-Encoding: br, unless inflate: false is set
Anyone who can send a request to your API
axios (Node.js adapter)
Yes by default, unless decompress: false
Servers your application calls
got
Yes by default (decompress: true)
Servers your application calls
Bare http / https modules
No — Node.js core does not decode Content-Encoding itself
Only reachable if your own code passes the body to zlib
Established from each project’s source code. body-parser added Brotli support in 2.0.0, so Express 4 apps on body-parser 1.x do not decode Brotli request bodies by default.
Note the asymmetry in the first row. undici decides how to decode from the response’sContent-Encoding header, not from what your request asked for. Sending accept-encoding: gzip narrows what an honest server returns; a malicious one can send Brotli anyway.
The other nine CVEs in the release
CVE-2026-111497 is the headline, but three of the remaining issues are rated High and two of them undermine TLS certificate checking — the control most applications assume is doing its job.
CVE
Severity
Lines
What it is
CVE-2026-105482
High
26, 24, 22
dns.resolve*() and dns.reverse() truncate names at a NUL byte, so attacker.example\0.trusted.example passes an allowlist check and then resolves attacker.example. An incomplete fix of CVE-2026-48930.
CVE-2026-105483
High
26, 24
TLS hostname check bypass when an IPv4 address is written in fullwidth digits; a regression after the fix for CVE-2026-48618.
CVE-2026-106069
High
26, 24, 22
A leading-zero IPv4 literal such as 0127.0.0.1 is checked as octal (87.0.0.1) but connected to as decimal on some platforms, so the certificate is validated against the wrong IP.
CVE-2026-105481
Medium
26, 24, 22
A keep-alive HTTP client can accept a response that arrived before its request was sent. An incomplete fix of CVE-2026-48931.
CVE-2026-106072
Medium
26, 24, 22
Non-canonical IPv4 spellings (0x7f.0.0.1, 2130706433, 127.1) let a certificate with only a DNS SAN authenticate an IP endpoint.
CVE-2026-106074
Medium
26, 24, 22
randomUUID() and randomInt() entropy caches get baked into startup snapshots and single-executable apps, so values repeat across launches.
CVE-2026-106070
Low
26
zlib.ZipFile returns attacker bytes after a failed ZIP header check — a scanner/extractor disagreement.
CVE-2026-106071
Low
26
Wildcard --allow-fs-* permission rules over-grant, so /etc/passwd.* also allows /etc/passwd.
CVE-2026-106073
Low
26, 24, 22
fs.realpathSync() skips the permission model’s read check. An incomplete fix of CVE-2026-21715.
Severity as rated by the Node.js project. Four of the nine are labelled incomplete fixes or regressions of earlier CVEs.
The bundled-dependency updates carry their own security content: c-ares 1.34.9 is a security release with eleven advisories (one High), OpenSSL 3.5.9 fixes the High-severity CVE-2026-84782 in DTLS, and the undici updates fix several High-severity issues, including CVE-2026-109383 — function options such as checkServerIdentity being silently dropped when an Agent cloned its options.
Affected and fixed versions
Release line
Status
Upgrade to
26.x
Current — becomes LTS on 28 October 2026
26.12.1 or later
24.x “Krypton”
Long-term support
24.22.1 or later
22.x “Jod”
Maintenance LTS, supported to 30 April 2027
22.23.4 or later
20.x and older
End-of-life (20.x ended 30 April 2026)
No fix — move to 22.x or 24.x
Source: Node.js release notes and release schedule. The project states that “End-of-Life versions are always affected when a security release occurs.”
How to find every vulnerable Node.js runtime
Upgrading is the easy part. The work is finding every copy, because Node.js rarely lives in one place: operating-system packages, version managers on developer and build machines, container base images, CI runners, serverless runtimes and desktop apps built on Electron each carry their own.
On each host
# The node on PATH — patched means v22.23.4+, v24.22.1+ or v26.12.1+
node --version
# Every node binary on the machine, not just the first one on PATH
which -a node nodejs
sudo find / -xdev \( -name node -o -name nodejs \) -type f -perm -u+x 2>/dev/null \
| while read -r b; do printf '%s\t' "$b"; "$b" --version 2>/dev/null || echo '?'; done
# Version managers on developer and build machines
ls ~/.nvm/versions/node/ 2>/dev/null
volta list all 2>/dev/null; fnm list 2>/dev/null; asdf list nodejs 2>/dev/null
# Operating-system packages (Debian's nodejs uses the system Brotli library)
dpkg -l nodejs libbrotli1 2>/dev/null | awk '/^ii/{print $2, $3}'
rpm -q nodejs brotli 2>/dev/null
# Python services carry the same CVE through the brotli package
pip show brotli brotlicffi 2>/dev/null | grep -E '^(Name|Version)'
Containers and dependencies
# Node.js inside each running container
docker ps --format '{{.ID}} {{.Image}}' | while read -r id img; do
printf '%s %s ' "$id" "$img"; docker exec "$id" node --version 2>/dev/null || echo 'no node'
done
# Images in a Kubernetes cluster that look like Node.js workloads
kubectl get pods -A -o jsonpath='{range .items[*]}{.metadata.namespace}/{.metadata.name}{"\t"}{.spec.containers[*].image}{"\n"}{end}' | grep -i node
# Which of your services decode Brotli request bodies automatically
npm ls express body-parser axios got 2>/dev/null
The process.versions trap
It is tempting to check the bundled library version with process.versions.brotli. Do not rely on it. The Node.js fix was a cherry-picked commit, and the version header was not changed — so patched 24.22.1 still reports Brotli 1.2.0, exactly like vulnerable 24.22.0. On Debian, the value reflects the system library and stays the same after the security update too. Decide on process.version, and on Debian on the libbrotli1 package version.
Debian, Ubuntu, Red Hat and container images
Debian builds nodejs against the system Brotli library, so the fix ships in the brotli source package: 1.1.0-2+deb13u1 for trixie (DSA-6582-1) and 1.0.9-2+deb12u1 for bookworm (DLA-4855-1, Debian LTS), both published 23 October. Restart long-running Node processes afterwards — a running process keeps the old library mapped.
Ubuntu lists brotli and nodejs as needs-triage at medium priority, with no Ubuntu Security Notice yet as of 23 October.
Red Hat rates the CVE Important; fixes for the RHEL brotli packages and the nodejs:24 and nodejs:26 modules were still pending on 23 October.
Official Docker images — the node:22, node:24/lts and node:26/current tags were rebuilt on 21 October. A container picks up the fix only when you rebuild your image and redeploy it; restarting a container does nothing.
If you cannot patch today
The Node.js project documents no workaround. The measures below reduce exposure while an upgrade is scheduled; none of them replaces it.
On Express 5, set inflate: false on body parsers for routes that never need compressed request bodies. The parser then rejects any non-identity Content-Encoding with a 415.
Reject or strip Content-Encoding: br on inbound requests at your reverse proxy or WAF.
Restrict which hosts your services fetch from. Red Hat’s guidance is to “limit exposure of services that decompress Brotli from untrusted peers”.
Treat an explicit accept-encoding header on outbound calls as partial protection only, for the reason given above.
Exploitation status, as of 23 October 2026
CVE-2026-111497 is not in CISA’s Known Exploited Vulnerabilities catalogue, and CISA’s assessment records exploitation as “none”. Red Hat states that “there is no public, reliable remote code execution proof-of-concept”.
On 22 October the security firm Imperva reported that it had achieved code execution in a lab, using the official Node.js 24.22.0 linux-arm64 image, through both fetch() responses and Express 5 request bodies. By Imperva’s own account the technique was unreliable and took multiple attempts, and it did not publish its code. Several GitHub repositories claim proofs of concept; according to press reports, the early ones crashed the process rather than running code.
That picture rarely lasts. The upstream fix has been public since February, the bug class is well understood, and the attack surface is any service that decompresses data it did not produce. Plan as though a reliable exploit will appear.
How SemperWise helps
Every runtime, not just the one on PATH
The optional Sentinel agent reports the installed software on each machine it watches, and SemperWise One grades every Node.js package against the fixed releases. A host on 22.x, 24.x or 26.x below the fix is raised as a finding that names CVE-2026-111497; one on an end-of-life line is raised as unsupported. Node.js inside container images and version-manager folders is reached from the build side, below.
Backports are not false alarms
A credentialed patch audit reads the distribution’s own advisories. A Debian host fixed through DSA-6582-1 is reported as fixed, even though its `nodejs` package version never changed — the case version-only scanners get wrong in both directions.
Your dependencies, in your repositories
Secure code review matches your lockfiles against current vulnerability intelligence, so the services that auto-decode Brotli — Express 5, body-parser 2.x, axios, got, Python’s brotli — are identified by name rather than by guesswork.
Closed when it is actually fixed
Findings are re-checked against every new inventory. One closes when the version moves past the fix, and reopens if an old image is redeployed — which is how most “patched” runtimes come back.
Known-exploited goes to the top
Every finding carries its CISA KEV status and EPSS exploit probability. If CVE-2026-111497 is added to the catalogue, it escalates automatically instead of waiting for someone to read the news.
Is my application vulnerable if it never calls zlib?
It can be. The built-in fetch() decompresses Brotli responses automatically, Express 5 decompresses Brotli request bodies, and axios and got decompress responses by default. If your service uses any of these on Node.js 22.x, 24.x or 26.x below 22.23.4, 24.22.1 or 26.12.1, treat it as exposed. A service that only uses the bare http module and never passes data to zlib does not reach the vulnerable decoder.
Does process.versions.brotli tell me whether I am patched?
No. The fix was a cherry-picked commit that did not change the bundled library’s version string, so patched 24.22.1 and vulnerable 24.22.0 both report Brotli 1.2.0. Check process.version instead, and on Debian check the libbrotli1 package version, because Debian’s Node.js uses the system Brotli library.
We still run Node.js 20. What should we do?
Node.js 20 reached end-of-life on 30 April 2026 and receives no fix for this or any later vulnerability. Upgrade to 22.23.4 or 24.22.1. If an upgrade is genuinely blocked, the Node.js end-of-life page names HeroDevs, NodeSource and TuxCare as providers of commercial security fixes for retired lines — a bridge, not a destination.
Is CVE-2026-111497 being exploited?
As of 23 October 2026 it is not in CISA’s Known Exploited Vulnerabilities catalogue, and no official source reports exploitation in the wild. Imperva has reported unreliable lab code execution on arm64 without publishing its code. Because the upstream fix has been public since February 2026, a reliable exploit should be expected rather than hoped against.
Do Electron desktop apps need updating?
Electron embeds its own copy of Node.js, which node --version cannot see. As of 23 October no Electron release note mentions CVE-2026-111497, so whether Electron is affected is unconfirmed; Electron did ship the eMudhra certificate mitigation on 20 October. Watch each vendor’s release notes and update desktop apps as fixes appear.
Reads vendor advisories, CVE records, distribution trackers and government alerts the day they publish, and turns them into the version tables, detection steps and fix guidance our assessment teams work from. Every version number and quote is checked against its original source.