Engineering research / security

Node.js Security Releases of 21 October 2026: Non-Canonical IP Hosts in TLS Checks, NUL Bytes in DNS Names, and Repeated Random Values in Snapshots

What the Node.js 26.12.1, 24.22.1 and 22.23.4 security releases change: TLS hostname checks that now reject non-canonical IP spellings, DNS resolver calls that reject NUL bytes, random caches cleared in startup snapshots, a keep-alive socket guard, and how to triage and test a fleet.

By Sandeep Belgavi · Published 2026-10-25 · Prepared 2026-10-22

Recent development · Practical technical guide · 22 October 2026

What the Node.js 26.12.1, 24.22.1 and 22.23.4 security releases change: TLS hostname checks that now reject non-canonical IP spellings, DNS resolver calls that reject NUL bytes, random caches cleared in startup snapshots, a keep-alive socket guard, and how to triage and test a fleet.

What was released

On 21 October 2026 the Node.js project released 26.12.1, 24.22.1 and 22.23.4 to fix ten vulnerabilities, four rated high, plus dependency updates: c-ares, undici, root certificates after the eMudhra intermediate CA compromise, OpenSSL on 24.x and 22.x, and SQLite on 22.x. The announcement notes that end-of-life lines are always affected when a security release occurs. This guide concentrates on the fixes that change behaviour your code can observe, because those are the ones that break tests after an upgrade and the ones worth a regression test of your own.

CVESeverityAreaLines affected
CVE-2026-106069HighTLS hostname check bypass when the host loosely resolves to an IP address26, 24, 22
CVE-2026-105483HighTLS hostname check bypass with an IP written in fullwidth digits26, 24
CVE-2026-105482Highdns.resolve truncates names containing a NUL character26, 24, 22
CVE-2026-111497HighBrotli decoder out-of-bounds write26, 24, 22
CVE-2026-105481MediumKeep-alive socket processes a response written before the request was sent26, 24, 22
CVE-2026-106072MediumNon-canonical IPv4 hostnames let DNS SANs authenticate IP endpoints26, 24, 22
CVE-2026-106074MediumrandomUUID and randomInt caches saved into startup snapshots and single executable applications26, 24, 22
CVE-2026-106070, -106071, -106073LowZIP header handling and two Permission Model issuesmostly 26

TLS: a host that is an IP address only after normalisation

tls.checkServerIdentity decides whether a certificate matches the host a client connected to. Hosts such as 127.1, 0x7f.0.0.1 or 127.0.0.1 written with fullwidth digits are not valid IP literals for net.isIP, but after IDNA normalisation, and in the eyes of many platform resolvers, they are IP addresses. The fix in commit ad788ccd07 says the problem directly: platform resolvers can interpret the original host as a different address than the one the identity check validated. The patched code no longer tries to match such hosts at all. If the original host is not an IP literal but its normalised form is, the check fails with Host is a non-canonical IP address.

The table shows what the probe printed with the previous 22.x release and the patched one on Windows. Each row calls tls.checkServerIdentity(host, cert) with a synthetic certificate object.

HostCertificate SANNode 22.23.3Node 22.23.4
127.1IP Address:127.0.0.1AcceptedRejected: non-canonical IP
127.1DNS:127.1RejectedRejected: non-canonical IP
0x7f.0.0.1DNS:0x7f.0.0.1RejectedRejected: non-canonical IP
127.0.0.1 in fullwidth digitsIP Address:127.0.0.1AcceptedRejected: non-canonical IP
127.0.0.1IP Address:127.0.0.1AcceptedAccepted
api.example.comDNS:api.example.comAcceptedAccepted

Two consequences follow. A client that connected to an IP endpoint with a shorthand spelling, which used to match the certificate’s IP SAN, now fails after the upgrade; fix the configuration to use the canonical address. And the 22.x line was not affected by CVE-2026-105483, the fullwidth-digit case, which the announcement lists only for 26.x and 24.x; the 22.23.3 rows above are consistent with that.

DNS: NUL bytes in names

The c-ares resolver receives names as C strings, so a name such as example.com\0.trusted.corp was silently cut at the NUL byte and the query went out for example.com. Code that validated the full string, for example by checking that it ends with a trusted suffix, then resolved a different name. Commit 6b78ea171d makes the resolver methods reject such names with ERR_INVALID_ARG_VALUE. In the probe, dns.resolve4 accepted the name on 22.23.3 and threw that error on 22.23.4. If your code builds host names from user input, validate them before resolving whatever the Node version, and test that a NUL byte is rejected.

Snapshots: the same random values in every process

crypto.randomUUID() and crypto.randomInt() serve values from a cache filled from the system random source. When an application is built into a user-land startup snapshot or a single executable application after these functions have been called, the unused cache entries were saved in the snapshot, and every process started from it replayed the same values. Commit acffa610db clears the caches after user serialisation callbacks and again before user deserialisation callbacks. If you ship snapshots or single executable applications, rebuild them with the patched release; upgrading only the runtime that builds them is the step that matters.

Keep-alive sockets and the rest

The HTTP agent already guarded idle keep-alive sockets against data that arrives while they sit in the pool. Commit 0683ee5f7d adds a direct check, without consuming bytes, for data waiting in the operating system’s socket buffer before an idle socket is handed to a new request; a socket with pending data is destroyed and replaced. The scenario is a server, or something between client and server, writing a response before the request was sent, which the old guard could miss between event-loop polls. The Brotli fix is a dependency patch exposed as an out-of-bounds write in the decoder; anything that decompresses Brotli from untrusted sources, including HTTP clients that accept br encoding, should treat the update as urgent.

Triage for a fleet

  1. Inventory Node binaries, not only package manifests: container base images, CI runners, Electron apps and single executable applications each carry their own runtime.
  2. Patch internet-facing services and anything that decompresses untrusted Brotli first, then TLS clients that connect to IP endpoints, then build hosts that produce snapshots.
  3. Before rolling out, search configuration for IP endpoints written in shorthand or with leading zeros or hex; they will now fail certificate checks.
  4. Rebuild snapshots and single executable applications after the build host is patched.
  5. Move services off end-of-life lines; the announcement says they are affected by every security release.

Regression tests worth keeping

JavaScript test file; the expectations match what Node 22.23.4 printed in the probe.

// node --test tls-identity.test.js
const test = require('node:test');
const assert = require('node:assert');
const tls = require('node:tls');
const dns = require('node:dns');

test('non-canonical IP hosts are rejected', () => {
  const err = tls.checkServerIdentity('127.1', { subjectaltname: 'IP Address:127.0.0.1', subject: {} });
  assert.ok(err, 'expected a rejection on a patched runtime');
});

test('canonical IP hosts still match IP SANs', () => {
  assert.strictEqual(tls.checkServerIdentity('127.0.0.1', { subjectaltname: 'IP Address:127.0.0.1', subject: {} }), undefined);
});

test('resolver rejects NUL bytes', () => {
  assert.throws(() => dns.resolve4('example.com\u0000.trusted.corp', () => {}), { code: 'ERR_INVALID_ARG_VALUE' });
});

Run it in CI against the runtime image you ship. It fails on an unpatched image and passes on a patched one, which turns the upgrade into something a pipeline can check.

Failure modes

  • Upgrading application dependencies and leaving the Node runtime in the base image unchanged.
  • Treating the TLS change as a regression and pinning the old runtime instead of fixing shorthand IP endpoints.
  • Rebuilding the application but not the startup snapshot, so the repeated random values remain.
  • Assuming 22.x and 24.x are affected identically; the announcement lists CVE-2026-105483 for 26.x and 24.x only, and three low-severity issues mostly for 26.x.

Sources and scope

Checked against these sources on 22 October 2026. Event: Node.js security releases 26.12.1, 24.22.1 and 22.23.4, 21 October 2026; recheck it before acting. Examples and figures are illustrative, not measurements.

Patch runtimes in base images first, fix IP endpoints written in shorthand before upgrading, rebuild startup snapshots and single executable applications, and keep the TLS and DNS regression tests.