DEV Community

Cover image for Node.js October 2026 Security Release: What to Patch Now
devTalk
devTalk

Posted on Originally published at dev-talk.com

Node.js October 2026 Security Release: What to Patch Now

On Wednesday, October 21, 2026, the Node.js project shipped security releases for three lines: 26.x, 24.x and 22.x. The advisory fixes 10 vulnerabilities, 4 rated High, 3 Medium and 3 Low. One of the High ones, a memory-corruption bug in the bundled Brotli decoder, is described as allowing remote code execution, and that is the reason to patch this week rather than at the next maintenance window.

The short version

Release line Upgrade to at least
Node.js 26.x v26.12.1
Node.js 24.x v24.22.1
Node.js 22.x v22.23.4

The advisory lists no workarounds for any of the 10 issues, so upgrading is the fix. It also says end-of-life versions are always affected when a security release ships. Node.js 20 and earlier are end of life and get no patch, so on those the only route is moving to a supported line.

Check what you are running

# on a server or CI runner
node --version

# inside a container image
docker run --rm your-image:tag node --version

# are you on Express 5 / body-parser 2?
npm ls express body-parser

# do you call the Brotli API directly?
grep -rn "brotliDecompress\|createBrotliDecompress" src/
Enter fullscreen mode Exit fullscreen mode

An image pinned to an old tag stays vulnerable until you rebuild it. After upgrading, restart every Node.js process, containers included, so the patched runtime is actually loaded. If you use your distribution's Node.js package, also check your distro's advisory: the Aikido write-up notes that packaged builds may use the system Brotli library.

# nvm example
nvm install 24
nvm alias default 24
node --version     # expect v24.22.1 or newer

# Docker: rebuild from a fresh base image
docker build --pull -t my-app .
Enter fullscreen mode Exit fullscreen mode

All 10 vulnerabilities

CVE Severity Lines What it is
CVE-2026-111497 High 26, 24, 22 Brotli decoder out-of-bounds write in node:zlib
CVE-2026-105482 High 26, 24, 22 dns.resolve*() truncates a hostname at a NUL character
CVE-2026-105483 High 26, 24 tls.checkServerIdentity() fails to treat an IP written with fullwidth digits as an IP
CVE-2026-106069 High 26, 24, 22 TLS hostname check bypass when a host "loosely" resolves to an IP address
CVE-2026-105481 Medium 26, 24, 22 Keep-alive socket treats data that arrived early as the response to the next request
CVE-2026-106072 Medium 26, 24, 22 DNS names in a certificate can authenticate non-canonical IPv4 spellings
CVE-2026-106074 Medium 26, 24, 22 crypto.randomUUID() and randomInt() caches saved into snapshots and single executable apps
CVE-2026-106070 Low 26 only zlib.ZipFile returns attacker-controlled bytes after a failed header validation
CVE-2026-106071 Low 26 only Permission Model wildcard grant allows more than intended
CVE-2026-106073 Low 26, 24, 22 Permission Model bypass through fs.realpathSync()

The release also updates c-ares, undici, the CA certificate bundle (the advisory lists "eMudhra Intermediate CA Compromise"), OpenSSL on 24.x and 22.x, and SQLite on 22.x.

The Brotli bug is the urgent one

CVE-2026-111497 is an out-of-bounds write in the Brotli decoder that Node.js bundles. According to the advisory, an invalid block switch was treated as "need more input" instead of a hard format error, so the streaming decoder kept appending data to a small internal buffer. Malicious Brotli data passed to node:zlib decompression can cause a crash, disclose process memory, or lead to remote code execution.

The question that decides your exposure is whether untrusted Brotli data ever reaches your process. I checked the common paths on Node.js 22.22.0, using harmless data:

  • fetch()decodes Brotli responses on its own. A test server answering with Content-Encoding: br came back as plain text. So any server your code fetches from, including a user-supplied webhook or URL, can send your app Brotli data.
  • A barehttpserver does not decode request bodies. My test server saw the raw compressed bytes.
  • Express 5 and body-parser 2 do. An Express 5.3.0 app using express.json() (with body-parser 2.4.0, which has a br case in its body reader) parsed a Brotli-compressed JSON body without any extra setup. So any client can feed Brotli data to such an app. That matches what Aikido reports.

In short: if you run Express 5 or call fetch() against servers you do not control, assume you are exposed.

How bad is it in practice?

Public reports disagree on how easily it can be exploited, so I will not over-claim either way. The Hacker News says a public proof of concept appeared on October 22 and reached code execution only against a deliberately weakened test build, with a crash and a limited memory read on a standard build. Aikido reports later code execution against an unmodified Node.js 24.22.0 image on Linux arm64, not on every attempt, and says 24.22.1 rejects the same input. Both say no exploitation in the wild had been reported as of October 22, and Qualys notes the CVE records did not yet have CVSS scores. A memory-safety flaw in a runtime that handles untrusted network input, with a public proof of concept and a routine version bump as the fix, is a good reason to patch now.

A stopgap if you cannot upgrade today

It does not replace the upgrade, and it does not cover Brotli responses your app fetches from other servers. But for an Express 5 API you can refuse Brotli request bodies before any body parser runs:

app.use((req, res, next) => {
  if (/\bbr\b/i.test(req.headers['content-encoding'] ?? '')) {
    return res.status(415).send('Unsupported Content-Encoding');
  }
  next();
});

app.use(express.json()); // must come after the check
Enter fullscreen mode Exit fullscreen mode

I ran this with Express 5.3.0: a plain JSON body and a gzip body returned 200, and a Brotli body returned 415. The same rule can also live at your proxy or load balancer.

The TLS hostname bugs: real, but narrower

Three of the High and Medium issues (CVE-2026-105483, CVE-2026-106069, CVE-2026-106072) are in how tls.checkServerIdentity() decides that a certificate matches the host you connected to. The advisory describes them as IP-address edge cases: an IP written in an unusual way (fullwidth digits, leading zeros, hex or a bare integer) gets matched against DNS names in the certificate instead of its IP entries. For CVE-2026-106072 the advisory says exploitation needs a numeric DNS name in a certificate from a CA the client trusts, plus a resolver that accepts those spellings, such as glibc.

The leading-zero case rests on a real parsing difference. WHATWG URL parsing reads 0127 as octal, and some operating system resolvers read it as decimal:

new URL('http://0127.0.0.1/').hostname   // '87.0.0.1'  (checked on Node 22.22.0)
Enter fullscreen mode Exit fullscreen mode

So the certificate can be validated against a different address than the one the socket connects to. The practical takeaway: if your services connect to other services by IP address, or take hostnames or URLs from users, these matter. If you only connect to normal DNS names, your exposure is much smaller, but the patch is still worth taking.

The DNS bug: validate, then resolve

CVE-2026-105482 means dns.resolve*() and dns.reverse() cut a name off at an embedded NUL byte, because the name is handed to c-ares as a C string. The advisory's example: attacker.example\0.trusted.example resolves attacker.example. The risky pattern is checking a hostname against an allowlist and then resolving it, which is common in SSRF protection code. The advisory calls it an incomplete fix of CVE-2026-48930, which covered only dns.lookup() and the net/tls host options. The fix is the upgrade. As defence in depth (my suggestion, not from the advisory), reject names containing a NUL byte before you validate them:

if (hostname.includes('\0')) throw new Error('invalid hostname');
Enter fullscreen mode Exit fullscreen mode

The rest, and who can skip them

  • Keep-alive socket (CVE-2026-105481): unsolicited data that arrived on an idle reused socket can be parsed as the response to your next request. It is described as an incomplete fix of CVE-2026-48931.
  • randomUUID()/randomInt()(CVE-2026-106074): only matters if you build user-land startup snapshots (--build-snapshot) or single executable apps with useSnapshot. Every process started from the snapshot returns the same values until the cache refills, so IDs can collide or be predicted.
  • Permission Model (CVE-2026-106071, CVE-2026-106073): only matters if you run with --permission and --allow-fs-read or --allow-fs-write. 106071 is on 26.x only.
  • zlib.ZipFile(CVE-2026-106070): 26.x only.

Frequently asked questions

Is this being exploited?

As of October 22, the sources I read reported no exploitation in the wild. A public proof of concept exists, so do not treat that as permission to wait.

I don't use Brotli. Am I safe?

Not necessarily. Brotli decoding can happen without you calling it: through fetch() responses, or through Express 5 request body parsing, as shown above. And the other nine fixes apply regardless.

Can I stay on Node.js 20?

It is end of life and will not receive these fixes. Plan the move to a supported line.

References


Originally published on DEV Talk.

Top comments (0)