Antwa CodeAntwaCode Blog

Notes12 min read

Node.js October 2026 Security Releases: 10 CVEs, a Brotli RCE, and the eMudhra CA Compromise

Node.js 22.23.4, 24.22.1, and 26.12.1 patch 10 runtime vulnerabilities — including a Brotli RCE, a trio of TLS verification bypasses, and the eMudhra CA compromise. Full breakdown, risk profiles, and how to update.

Read in Bahasa Indonesia →

nodejs/node repository Image: GitHub — the nodejs/node repository

Just one day after the PHP world was rocked by PHP2Shell, it's the Node.js ecosystem's turn to get a major security release. On October 21, 2026, the Node.js team shipped new versions for the three supported release lines — v22.23.4 (LTS "Jod"), v24.22.1 (LTS "Krypton"), and v26.12.1 (Current) — patching 10 vulnerabilities in the runtime while also handling a certificate authority compromise incident.

Four of the ten bugs are rated High, including one explicitly stated to allow remote code execution: an out-of-bounds write in the Brotli decoder. The rest cover three TLS hostname verification bypasses, a DNS bug, a peculiar issue in crypto.randomUUID(), and a few small holes in the Permission Model.

In this article we'll break them down one by one: what's broken, why it matters, and what you should do today.

At a Glance: Ten Holes at Once

Before diving into the details, here's a quick map. Node.js doesn't publish official CVSS scores for these issues; the severities below are the Node.js project's own ratings:

CVESeverityIssueAffected
CVE-2026-111497HighBrotli decoder OOB write → RCE22.x, 24.x, 26.x
CVE-2026-105482Highdns.resolve truncates names containing NUL characters22.x, 24.x, 26.x
CVE-2026-105483HighTLS hostname bypass when the IP is written with fullwidth digits24.x, 26.x
CVE-2026-106069HighTLS hostname bypass when a "loose" host resolves to an IP22.x, 24.x, 26.x
CVE-2026-105481MediumKeep-alive socket processes a response written before the request was sent22.x, 24.x, 26.x
CVE-2026-106072MediumBroken certificate validation for non-canonical IPv4 hostnames22.x, 24.x, 26.x
CVE-2026-106074MediumrandomUUID()/randomInt() repeat values in snapshot/SEA22.x, 24.x, 26.x
CVE-2026-106070LowZipFile returns attacker-controlled prefix bytes26.x
CVE-2026-106071LowPermission Model over-grant via wildcard radix lookup26.x
CVE-2026-106073LowPermission Model bypass via fs.realpathSync22.x, 24.x, 26.x

An important note from the advisory: end-of-life versions are always affected whenever a security release comes out, and they will not receive these fixes. If you're still on Node.js 20.x or older, no patch is waiting for you — the only way out is to move to a supported release line.

Highlight: Brotli RCE (CVE-2026-111497)

This is the one that gets your heart racing. Node.js's Brotli decoder has an out-of-bounds write that can be exploited for remote code execution. Brotli is a widely used compression format — from Content-Encoding: br in HTTP to zlib.createBrotliDecompress() to compressed static assets. That means compressed data coming from outside can end up in this broken decoder.

The technical details are deliberately kept thin by Node.js (as usual for advisories before everyone has had a chance to patch): the advisory doesn't say exactly which input triggers the bug, and it doesn't confirm whether the path is directly reachable in a default setup. What's clear:

  • The bug is in Node.js's bundled Brotli decoder (a bundled dependency).
  • It was found by Matteo Collina (mcollina) and fixed by Antoine du Hamel (aduh95) with a cherry-pick of an upstream commit by Evgenii Kliuchnikov in the Brotli repository.
  • It affects all three release lines: 22.x, 24.x, and 26.x.

Since this is an OOB write, the impact isn't just a crash — OOB writes are the class of bug that classically leads to execution control. Treat this as the most urgent of the ten, especially if your application processes archives, assets, or requests with Content-Encoding: br from users or untrusted upstreams.

Who's Most at Risk?

Really, anyone running Node.js 22.x/24.x/26.x should update — but if you want to prioritize, here's the risk profile from most urgent down:

🔴 High priority: anything processing compressed data from outside Proxies, API gateways, CDN origins, services accepting Content-Encoding: br, applications extracting ZIP archives, or image/asset pipelines — all of these touch the Brotli decoder (CVE-2026-111497) and the ZIP parser (CVE-2026-106070) directly. If incoming data comes from users or upstreams you don't fully trust, this is a direct path to the OOB write.

🔴 High priority: anything making TLS connections to IP endpoints Microservices calling each other over raw IPs, connections to IP-based databases, webhooks, or agents verifying the peer's certificate — this is exactly where the trio of TLS hostname bypasses plays out. A certificate issued for an attacker's domain can "represent" the IP endpoint you're connecting to.

🟡 Medium priority: anything relying on hostname validation Applications with SSRF protection, domain allowlists, or hostname-based security decisions need to pay attention to CVE-2026-105482 (NUL truncation) and audit their validation flows.

🟡 Medium priority: anything shipping SEA or startup snapshots CLI tools compiled into a single executable, desktop installers, or any application built with V8 snapshots — the randomUUID() bug (CVE-2026-106074) makes every copy of the executable produce the same "random" values. If your product ships to many users in this form, consider this a security incident, not just a bug.

🟢 Low priority but still update: anything using the Permission Model The --permission feature is still fairly young, and these two Low bugs are enough to break out of its isolation. If you use it as a real security layer (not just as an experiment), updating is mandatory.

And one profile that's often forgotten: applications wrapped in Electron or desktop frameworks. Electron ships its own Node.js runtime, which doesn't always stay in sync with system updates. Check the runtime version inside it, not just node -v in your terminal.

Three TLS Hostname Verification Bypasses — One Pattern, Three Ways

What's interesting about this release is three separate CVEs sharing the same attack pattern: an address is written in an unusual form, Node.js doesn't recognize it as an IP address, and the certificate check "falls back" to the DNS name — so a certificate issued for a domain can vouch for a connection to an IP endpoint. In short: an attacker with a valid certificate for their own domain can convince a client that a connection to a target IP is legitimate.

CVE-2026-105483 — IP with Fullwidth Digits (High, 24.x & 26.x)

When an IP address is written using fullwidth digits (Unicode variants of numbers, e.g. 127.0.0.1), TLS certificate hostname validation can be bypassed. Node.js doesn't recognize this form as an IP, so verification falls back to DNS SANs. Found by hasanfleyah, fixed by James Snell (jasnell) by correcting an IP normalization regression.

CVE-2026-106069 — "Loose" Host Resolving to an IP (High, 22.x, 24.x, 26.x)

The second bypass happens when a host resolves to an IP address in a loose way. The mechanism is the same: the address isn't recognized as an IP, the check falls back to DNS names, and a domain certificate can represent the IP endpoint. Found by anagnorisis2peripeteia, fixed by Matteo Collina.

CVE-2026-106072 — Non-Canonical IPv4 & DNS SANs (Medium, 22.x, 24.x, 26.x)

The third case: an IPv4 hostname in a non-canonical form (such as 2130706433 or 0x7f.1 — yes, the kind that gives you a headache if you've ever played with weird IPs) makes certificate validation improper, allowing DNS SANs to authenticate an IP endpoint. Found by br41ny, fixed by Matteo Collina.

The Fix

All of these paths are closed with the same approach: TLS now rejects non-canonical IP addresses and fixes the IP normalization regression. If your application makes TLS connections to IP-based endpoints — internal services, databases, webhooks, microservice-to-microservice connections — make sure the runtime is updated, because that's exactly where this class of bypass is dangerous.

Side Story: The eMudhra CA Compromise

This release also includes an emergency measure that isn't a Node.js bug at all, but is just as important: handling of the eMudhra certificate authority compromise.

The timeline (quoted from Mozilla's report):

  • Saturday, October 17, 2026 — Mozilla received a report that the private keys for 8 eMudhra intermediate certificates had been compromised.
  • eMudhra stated that the affected intermediates came from an April 2025 key generation ceremony, and the investigation is still ongoing.
  • The impact: an attacker holding those keys could issue fake HTTPS certificates for any site they don't own.

Node.js's response (and Mozilla's):

  • All of these security releases now reject TLS chains containing the blocked public keys.
  • The bundled root certificates were updated to NSS 3.131.1 — Mozilla's trust store release that revokes the root that signed the affected intermediates.

The lesson: TLS security isn't just about bugs in our verification code. The chain of trust above it — root CAs, intermediate CAs — can collapse at any moment, and a runtime must be quick enough to revoke its trust. This is why regular updates matter even when your own code hasn't changed.

dns.resolve and NUL Characters (CVE-2026-105482)

Another High bug: dns.resolve truncates names containing NUL characters (\0). A classic C-string problem sneaking into the JavaScript world. The impact varies by application: a hostname that should be rejected gets "shortened" into a different, valid name — potential for broken security logic, e.g. an allowlist check on safe.example.com\0.evil.com that gets truncated to safe.example.com.

Node.js now validates all resolve inputs for null bytes before processing them. Found by hasanfleyah, fixed by James Snell.

If your application validates hostnames for security decisions (SSRF protection, allowlists, CORS), it's time for an audit: never rely on hostname normalization performed after validation.

A Unique Bug: randomUUID()/randomInt() Repeating in Snapshots (CVE-2026-106074)

This is probably the most "unique" bug in the release. Node.js applications built with user-land startup snapshots or Single Executable Applications (SEA) can end up producing the same random values in every process.

Here's the story: crypto.randomUUID() and crypto.randomInt() keep an entropy cache for performance. Well, that cache gets "saved" into the snapshot at build time. Every process started from that snapshot begins its life with the same cache — so the "random" values it emits repeat exactly in every process.

Imagine the impact:

  • Password reset tokens generated at startup are always the same.
  • Session IDs or API keys generated with randomUUID() can be predicted by an attacker running the same build.
  • Random values for cryptography that turn out not to be random — a security developer's worst nightmare.

The fix (by Filip Skokan / panva, on a report by gesposito): the entropy cache is now cleared (reset) between snapshots. But the runtime fix alone is not enough for you — snapshots/SEAs you've already built with the old runtime still carry the broken cache and must be rebuilt with the new Node.js version. This is the operational detail that's easy to miss!

The Rest: HTTP Keep-alive, ZIP, and the Permission Model

CVE-2026-105481 — Keep-alive Socket (Medium, all lines)

A keep-alive socket can process a response written before its request was sent. In a broken connection reuse scenario, data can get swapped between requests — the classic, dangerous "response mix-up" problem in proxies or persistent connections. Found by hasanfleyah, fixed by James Snell by making the agent data guard work without I/O.

CVE-2026-106070 — ZIP Prefix Bytes (Low, 26.x)

ZipFile can return attacker-controlled prefix bytes after ZIP header validation fails. Instead of failing cleanly, the parser leaves leftover attacker data in the output. Found by startimeash, fixed by Matteo Collina by avoiding caching of invalid ZIP local headers.

CVE-2026-106071 & CVE-2026-106073 — Permission Model (Low)

Two holes in Node.js's Permission Model (the --permission feature):

  • CVE-2026-106071 (26.x): filesystem over-grant via wildcard radix lookup — permissions can open up wider than requested. Found by malikhamzaiqbal, fixed by James Snell.
  • CVE-2026-106073 (all lines): Permission Model bypass via fs.realpathSync. Found by 0xoroot, fixed by RafaelGSS.

Even though they're "Low", if you rely on the Permission Model as a security isolation layer (e.g. limiting filesystem access for third-party applications), these two bugs are enough to break through its boundaries. Updating is mandatory.

Bundled Dependency Updates That Come Along

These security releases also update bundled dependencies that have public vulns of their own:

  • OpenSSL 3.5.9 (24.x, 22.x) — security fixes in the TLS/crypto library.
  • c-ares 1.34.9 (all lines) — the async DNS resolver library.
  • SQLite 3.53.4 (22.x) — the bundled embedded database.
  • Undici (all lines — 6.29.1 for 22.x, 7.30.1 for 24.x) — Node.js's bundled HTTP client.
  • Root certificates NSS 3.131.1 (all lines) — including the eMudhra trust revocation.
  • Timezone data 2026e (22.x) and a V8 backport.
  • There's also hardening of X.509 conversion and formatting from Filip Skokan.

If you think "Node.js is just a runtime, its dependencies are safe because they're battle-tested" — remember that OpenSSL, c-ares, and Undici each have their own CVE histories. Updating the runtime brings all of these fixes at once.

How to Update and an Operational Checklist

Check the Running Version

node -v
# v22.23.3 or older? Time to update.
 
# Don't forget to check what's running in production, not just locally
# (containers, serverless, etc.)

Update with nvm

nvm install 22.23.4    # or 24.22.1 / 26.12.1
nvm alias default 22.23.4
nvm use 22.23.4

Update with a Package Manager

# Debian/Ubuntu (NodeSource)
sudo apt update && sudo apt install nodejs
 
# Or via fnm/asdf/mise to match your workflow

Docker

# Make sure the base image is updated too
FROM node:22.23.4-slim

Then rebuild the image — updating packages inside an old container doesn't help if the base image is still old.

Post-Update Checklist

  1. Restart everything — updated packages have no effect until the running processes are restarted or replaced.
  2. Rebuild SEA / startup snapshots — mandatory to eliminate the randomUUID() bug (CVE-2026-106074). Old snapshots keep producing repeating random values even on the new runtime.
  3. Inventory your runtimes — don't forget the hidden ones: derivative Docker images, serverless functions, embedded Node.js in desktop apps (Electron has its own runtime!), CI runners, and process supervisors.
  4. Verify — check node -v in the processes that are actually running after the restart.
  5. Check for EOL versions — if you still have Node.js 20.x or older anywhere in your infrastructure, set a migration schedule. EOL versions are always affected but never get patches.

Conclusion

This October 2026 security release reminds us of several things at once:

  1. An RCE is waiting in a compression decoder. Compressed data from third parties is no small matter — the Brotli OOB write (CVE-2026-111497) is the number one reason to update today.
  2. TLS verification is more fragile than we think. Three bypasses at once with the same pattern — "a weird shape isn't recognized as an IP" — is a reminder that network address parsing is a minefield for security bugs.
  3. Security is a chain, not a single point. The eMudhra CA compromise proves that even a bug-free runtime needs regular updates to revoke trust that's no longer valid.
  4. "Random" isn't necessarily random. The randomUUID() snapshot bug is a reminder that execution context (snapshots, SEA, workers) can change the behavior of cryptographic APIs — and old build artifacts must be rebuilt too.
  5. An update is a package deal with restart, rebuild, and verification. A half update = no update.

If you're running Node.js 22.x, 24.x, or 26.x, your schedule today is simple: update to 22.23.4 / 24.22.1 / 26.12.1, restart all processes, rebuild snapshots/SEAs, and verify. Ten bugs with four Highs — including one RCE — is not a release worth postponing.

Quick FAQ

I only run an internal application, nothing is publicly accessible. Do I still have to update? Yes. First, "internal" doesn't mean safe — the TLS hostname bypasses and the DNS bug are actually most dangerous on inter-service connections that are assumed to be trusted. Second, the Brotli bug doesn't care whether the data comes from the public or from a colleague on the next team uploading a file. The update itself is cheap; delaying it is what's expensive.

Is it enough to update Node.js on my laptop/CI? No. What needs updating is the runtime that runs the application in production: servers, container images, serverless functions, and all their derivatives. A fresh local runtime doesn't protect a container still using an old base image.

I'm on Node.js 20.x (EOL). Am I safe since it's not mentioned? Quite the opposite. The Node.js advisory explicitly states "end-of-life versions are always affected when a security release occurs" — meaning your version stays vulnerable but will never get a patch. Migrate to 22.x or 24.x (both LTS) as soon as possible.

How long should this update actually take? For most teams: a few minutes to a few hours. Minor/patch LTS updates rarely involve breaking changes. What takes longest is usually coordinating restarts and rebuilding images — not the update itself.

How bad is this randomUUID() bug if my application doesn't use SEA? If your application runs from regular source (or a regular container) without startup snapshots/SEA, the bug's path isn't triggered. But still update — you're still hit by the other nine bugs.

Is this related to yesterday's PHP2Shell? There's no technical connection — different ecosystems, different root causes. But both were released on the same day (October 21, 2026) and teach the same lesson: bugs can lie dormant in the runtimes and libraries you trust, and the only complete answer is still to update.

References

More posts