The Node.js project published security releases on October 21, 2026: v22.23.4, v24.22.1 and v26.12.1. They fix ten CVEs across the three supported lines. Four rate high. The same releases update c-ares and Undici on all three lines, OpenSSL on 22.x and 24.x, SQLite on 22.x, and the bundled CA certificates in response to the eMudhra intermediate CA compromise.
The high-severity four
| CVE | Area | Bug | Lines |
|---|---|---|---|
| CVE-2026-111497 | node:zlib | Brotli decoder writes past an 8-byte buffer | 22, 24, 26 |
| CVE-2026-105482 | dns | dns.resolve*() and dns.reverse() truncate names at a NUL byte | 22, 24, 26 |
| CVE-2026-106069 | tls | leading-zero IPv4 hosts get checked against a different address | 22, 24, 26 |
| CVE-2026-105483 | tls | fullwidth digits in an IPv4 host skip the IP SAN check | 24, 26 |
Brotli: CVE-2026-111497
The Brotli decoder bundled with Node.js treated an invalid block switch as a request for more input instead of a format error. The streaming decoder kept appending bytes to an eight-byte internal buffer and ran past its end. Anyone who can hand your process Brotli data that you decompress with node:zlib can corrupt memory. The advisory lists a crash, disclosure of process memory, or remote code execution as outcomes.
The pattern to look for in your code:
import zlib from "node:zlib";
// Decompressing uploads or webhook bodies that arrive as br
app.post("/ingest", (req, res) => {
if (req.headers["content-encoding"] === "br") {
req.pipe(zlib.createBrotliDecompress()).pipe(sink);
}
});
A body-parsing middleware that inflates Content-Encoding: br requests through node:zlib puts you in the same spot, even if your own code never names Brotli. Researchers from OpenAI's Codex Security team reported the bug, and Node.js core maintainers mcollina and aduh95 wrote the fix.
TLS and DNS
Three of the bugs let a hostname string say one thing to the certificate check and another to the resolver.
- CVE-2026-106069.
tls.checkServerIdentity()follows WHATWG URL rules, so it reads0127.0.0.1as octal and checks the certificate against87.0.0.1. The macOS resolver reads the same string as decimal and connects to127.0.0.1. - CVE-2026-105483. A host written with fullwidth digits, such as
127.0.0.1, connects to an IPv4 address without a matching IP SAN in the certificate. The project calls it a regression from the fix for CVE-2026-48618, and it affects 24.x and 26.x. - CVE-2026-105482.
dns.resolve*()passes the name to c-ares as a C string, soattacker.example\0.trusted.exampleresolvesattacker.exampleand returns no error. An earlier fix covereddns.lookup()and missed these functions.
A medium-rated sibling, CVE-2026-106072, lets a certificate with a numeric DNS SAN authenticate an IP endpoint when the client uses spellings such as 0x7f.0.0.1 or 127.1 on a resolver that accepts them, glibc among them.
All four matter most when your server connects to hosts that users choose: webhooks, link previews, SSRF filters that validate a hostname before a request.
Snapshots and single executables
CVE-2026-106074, rated medium, hits a narrower group. If you build a startup snapshot with --build-snapshot or a single executable application with useSnapshot, Node.js saved the entropy caches for crypto.randomUUID(), crypto.randomUUIDv7() and crypto.randomInt() into the snapshot. Every process started from it returned the same values until the cache refilled: up to 127 UUIDs and about 1,000 randomInt() results.
If you ship a SEA or snapshot, rebuild it on the patched Node.js. Then look for IDs minted near process start that collided across instances.
The rest
| CVE | Severity | Summary |
|---|---|---|
| CVE-2026-105481 | Medium | HTTP Agent keep-alive socket accepts a response sent before the request |
| CVE-2026-106070 | Low | zlib.ZipFile returns attacker bytes after failed header checks (26.x) |
| CVE-2026-106071 | Low | --allow-fs-read=/etc/passwd.* also grants /etc/passwd (26.x) |
| CVE-2026-106073 | Low | fs.realpathSync() skips Permission Model read checks |
Upgrade
node -v
# nvm
nvm install 24.22.1 && nvm alias default 24.22.1
# Docker: pin the patched version in your FROM line, then rebuild
docker build --pull -t my-service .
The advisory reminds you that end-of-life versions get no fixes and count as affected. If a service still runs a line outside 22, 24 and 26, these CVEs stay open on it.
This week
- Move every service to 22.23.4, 24.22.1 or 26.12.1. Start with services that decompress request bodies or connect to hosts your users pick.
- Grep for
createBrotliDecompress,brotliDecompressand body parsers withbrenabled, so you know where CVE-2026-111497 reached you. - Rebuild any single executable application or startup snapshot.
- If you validate hosts in an SSRF filter, test it with
0127.0.0.1,0x7f.0.0.1,127.1and fullwidth digits after the upgrade.
Volodymyr Chornous

