Building the AI-native SOC for the 99% of companies who can't justify a $500K stack.
All security advisoriesSecurity advisory

Node.js October 2026 Security Releases

On 21 October 2026 Node.js fixed ten vulnerabilities across every supported release line, four of them rated high. The most serious is a memory corruption bug in the bundled Brotli decoder that Node.js says permits remote code execution, and it is reachable through fetch and common body parsers, not only direct zlib calls. Upgrade to 22.23.4, 24.22.1 or 26.12.1. Node.js 20 and older receive no fix.

Status
Fixed releases and official Docker images are available. CVE records, CVSS scores and technical write-ups are not yet published.
Affects
Node.js 22.x, 24.x and 26.x, and every end-of-life line
Severity
High, as rated by Node.js. No CVSS scores published yet.
Published

Reflects what was publicly known on the last updated date. Every fact links to a primary source at the end of the page.

On this page
  1. Are you affected?
  2. What to do
  3. Find exposure
  4. Detect attempts
  5. Fixed versions
  6. The vulnerabilities
  7. Dependency updates
  8. Timeline
  9. Questions
  10. Sources

Are you affected?

If you run Node.js anywhere, yes. The rows below set the order to work in.

  • You run Node.js 22, 24 or 26

    Servers, CLIs, build tools, serverless functions

    Update needed

    Upgrade to 22.23.4, 24.22.1 or 26.12.1. All three lines carry at least one high-severity fix. Fixed versions

  • You decompress Brotli you do not control

    fetch to third-party servers, Express 5 APIs, proxies

    Act now

    Upgrade these services first. This is the bug Node.js says permits remote code execution, and common libraries reach it without any explicit zlib call in your code. CVE-2026-111497

  • You run Node.js 20 or older

    Including 21, 23 and 25

    Act now

    No fix is coming. Node.js 20 reached end of life on 30 April 2026. Treat these systems as carrying known, unfixed vulnerabilities and migrate to Node.js 22 or 24. Details

  • You ship Node.js inside images or binaries

    Docker images, single executable applications, startup snapshots

    Update needed

    Upgrading the host is not enough. Rebuild every image and binary on the fixed version, because each one carries its own copy of the runtime.

  • You rely on the Permission Model

    Processes started with --permission

    Low risk

    Two low-severity bypasses are fixed. Node.js documents the Permission Model as a safeguard for trusted code, not a sandbox for untrusted code.

What to do

This is an upgrade, not a configuration change. Node.js has published no workarounds for any of the ten CVEs.

  1. Inventory every Node.js runtime you run

    Hosts, CI runners, base images, serverless functions and binaries built from Node.js. The next section has commands for each. On any single machine, this prints the runtime and its bundled OpenSSL, c-ares and Undici versions.
    Shell
    node -p "[process.version, 'openssl ' + process.versions.openssl,
      'c-ares ' + process.versions.ares, 'undici ' + process.versions.undici].join('\n')"
  2. Upgrade services that handle untrusted Brotli first

    Then everything else, to 22.23.4, 24.22.1 or 26.12.1 or later in the same line. See fixed versions.
  3. Rebuild container images on a fresh base

    The official node images were updated on 21 October. An image built before then keeps the old runtime until you rebuild it on a fresh pull of the base.
    Shell
    docker run --rm node:22 node --version   # expect v22.23.4 or later
    docker run --rm node:24 node --version   # expect v24.22.1 or later
    docker run --rm node:26 node --version   # expect v26.12.1 or later
  4. Rebuild single executables and startup snapshots

    A single executable application embeds its own copy of Node.js, so it stays vulnerable until it is rebuilt. If it used a startup snapshot, also rotate any secrets it generated with randomUUID or randomInt. See CVE-2026-106074.
  5. Turn on the post-upgrade detections

    The fixed runtime raises distinct errors when it rejects the inputs these bugs relied on. Logging them turns the patch into a sensor. Detect attempts
  6. Schedule the move off end-of-life lines

    Anything on Node.js 20 or older stays exposed to every issue on this page. Track it as a known, unfixed vulnerability until it is migrated.

Find exposure

Version first, then reachability. A service on a fixed runtime is done; a service on an old runtime is urgent in proportion to which of the code paths below it uses.

Inventory runtimes

Hosts and CI runners. Version managers and system packages often leave more than one node on a machine. List them all.

Shell
# Every node binary on PATH, with its version
for n in $(which -a node); do printf '%s %s\n' "$n" "$("$n" --version)"; done

Containers. The runtime inside an image is fixed at build time. Ask each image directly rather than trusting its tag.

Shell
# Node.js version inside each image used by a running container
docker ps --format '{{.Image}}' | sort -u | while read -r img; do
  printf '%s ' "$img"
  docker run --rm --entrypoint node "$img" -p process.version 2>/dev/null || echo "no node"
done

AWS Lambda. By default, Lambda applies managed runtime updates automatically, and the runtime version ARN appears in the INIT_START line of each function’s logs. Functions pinned to a runtime version, and functions deployed as container images, are not updated for you: container-based functions need a rebuild on the updated base image and a redeploy.

Shell
# Functions on a managed Node.js runtime
aws lambda list-functions \
  --query "Functions[?starts_with(Runtime, 'nodejs')].[FunctionName,Runtime]" \
  --output table

# Whether a function takes runtime updates automatically
aws lambda get-runtime-management-config --function-name <name>

Single executable applications. An SEA carries the runtime it was built with. Check the Node.js version in the pipeline that builds each binary, not on the machine that runs it.

Direct Undici dependencies. If your application installs the undici package itself, upgrading Node.js does not change it. Upgrade it to 6.29.1, 7.30.1 or 8.12.2. The npm package still decodes Brotli with the runtime’s zlib, so the runtime upgrade is needed either way.

Shell
# A direct undici dependency is not updated by upgrading Node.js
npm ls undici --all
pnpm why undici

Check which code paths you use

Use this to order the upgrade, not to skip it. Dependencies reach these APIs too, so a clean search of your own code does not prove a service is unexposed.

Where each vulnerability is reachable
BugReachable whenLook for
Brotli decoderBrotli input from a source you do not control: zlib Brotli calls, fetch responses with Content-Encoding: br, Express 5 or body-parser 2.x request bodies, proxies that decompress.brotliDecompress, createBrotliDecompress, fetch to user-supplied or third-party URLs, express@5, body-parser@2
DNS NUL truncationUser-supplied names checked as strings, then passed to a resolve method. dns.lookup is not affected.dns.resolve*, dns.reverse, Resolver, allowlists built on endsWith
TLS hostname bypassTLS connections to host strings an attacker can influence: webhooks, URL previews, SSRF-prone fetchers, proxies.tls.connect, https.request or fetch with a user-supplied host, custom checkServerIdentity
Keep-alive response smugglingHTTP clients with keep-alive talking to an untrusted server, or plain HTTP across an untrusted network.new http.Agent({ keepAlive: true })
Snapshot entropy reuseOnly single executable applications built with useSnapshot, or user-land startup snapshots.useSnapshot in SEA config, --build-snapshot
Permission Model bypassesOnly processes started with --permission.--permission, --allow-fs-read, --allow-fs-write
Code search
rg -n --type js --type ts \
  -e 'brotliDecompress|createBrotliDecompress' \
  -e 'dns\.(resolve|reverse)|new (dns\.)?Resolver' \
  -e 'checkServerIdentity|keepAlive:\s*true' \
  -e 'useSnapshot|build-snapshot|--permission' .

Detect attempts

No public exploit or indicators of compromise had been published as of 21 October 2026. These are starting points built from how the fixes behave, not signatures for a known campaign.

Brotli: crashes before the upgrade, errors after

On an unpatched runtime, an out-of-bounds write is most likely to surface as a Node.js process dying while it handles compressed input: a segmentation fault on a host, or exit code 139 in a container. Correlate unexplained crashes of services that accept or fetch Brotli with the requests they were handling at the time.

On a fixed runtime, the decoder rejects the malformed streams the bug relied on with the error code ERR__ERROR_FORMAT_BLOCK_SWITCH (errno -17), according to the fix’s regression test. Legitimate encoders do not produce these streams, so the code is worth an alert. Make sure the error, the client address and the request path reach your logs.

JavaScript
// Log rejected Brotli streams with enough context to investigate.
stream.on("error", (err) => {
  if (err.code === "ERR__ERROR_FORMAT_BLOCK_SWITCH") {
    logger.warn({ err, source: req.ip, path: req.url }, "rejected brotli stream");
  }
});

If your load balancer or WAF records request headers, also look at requests that arrive with Content-Encoding: br at endpoints that never expected compressed bodies.

TLS: non-canonical IP hosts

The two TLS hostname bypasses need a host string that only becomes an IP address after normalisation. On a fixed runtime these connections fail with ERR_TLS_CERT_ALTNAME_INVALID. Before upgrading, you can flag such hosts in outbound request logs or at the point where user-supplied URLs enter your application.

JavaScript
// Flags host strings that only become IPv4 addresses after normalisation,
// such as 0127.0.0.1, 0x7f.0.0.1, 127.1 or fullwidth digits.
const net = require("node:net");

function nonCanonicalIpHost(host) {
  if (/[\uFF0E\uFF10-\uFF19\u3002]/.test(host)) return true;
  return (
    net.isIP(host) === 0 &&
    /^(0x[0-9a-f]*|\d+)(\.(0x[0-9a-f]*|\d+)){0,3}\.?$/i.test(host)
  );
}

DNS: names containing NUL

A fixed runtime throws ERR_INVALID_ARG_VALUE when a resolve call receives a name containing a NUL character. In web logs, a NUL usually arrives encoded as %00 in a parameter that later becomes a hostname. Either is a strong sign of deliberate probing, since no valid DNS name contains one.

Fixed versions

Status as of 21 October 2026, from the Node.js release schedule. Any later release in the same line also contains the fixes.

Fixed Node.js version for each release line
Release lineStatusFixed in
Node.js 26Current, becomes LTS on 28 October 202626.12.1
Node.js 24 (Krypton)LTS, in maintenance since 20 October 202624.22.1
Node.js 22 (Jod)Maintenance LTS until 30 April 202722.23.4
Node.js 25, 23 and 21End of lifeNo fix
Node.js 20 (Iron) and olderEnd of life since 30 April 2026No fix

End-of-life lines

The Node.js security post states that end-of-life versions are always affected when a security release occurs. Node.js 20 reached end of life on 30 April 2026 and Node.js 25 on 1 June 2026. The Node.js project recommends that production applications use only Active LTS or Maintenance LTS releases.

The vulnerabilities

Ten CVEs, ordered by severity. Node.js has published only a title and rating for each, so the explanations below come from the fix commits and their tests, and say so where they go beyond what Node.js has stated.

Node.js October 2026 CVEs by release line
CVETitleSeverityAffected lines
CVE-2026-111497Brotli decoder out-of-bounds writeHigh22.x, 24.x, 26.x
CVE-2026-105482DNS resolve truncates names at a NUL characterHigh22.x, 24.x, 26.x
CVE-2026-106069TLS hostname bypass with non-canonical IP addressesHigh22.x, 24.x, 26.x
CVE-2026-105483TLS hostname bypass with fullwidth digitsHigh24.x, 26.x
CVE-2026-106072DNS names in certificates accepted for IP endpointsMedium22.x, 24.x, 26.x
CVE-2026-105481Keep-alive socket accepts a response sent too earlyMedium22.x, 24.x, 26.x
CVE-2026-106074Repeated random values in snapshots and SEAsMedium22.x, 24.x, 26.x
CVE-2026-106070ZipFile returns stray bytes after a failed header checkLow26.x
CVE-2026-106071Permission Model over-grants with wildcard pathsLow26.x
CVE-2026-106073Permission Model bypass through fs.realpathSyncLow22.x, 24.x, 26.x

High CVE-2026-111497: Brotli decoder out-of-bounds write

Affects 22.x, 24.x, 26.x. Fix: Node.js commit, google/brotli commit

The bug is in the Brotli library that Node.js bundles, not in Node.js’s own code. A crafted compressed stream that declares a single block type and then switches blocks was not rejected, and Node.js says the result is an out-of-bounds write that permits remote code execution. Node.js applied the upstream fix, which makes the decoder stop with an error on such streams.

Who is exposed: anything that decompresses Brotli data it does not control. zlib.brotliDecompress, its sync form and zlib.createBrotliDecompress all reach the decoder. So do two common libraries that call them for you: the built-in fetch decodes responses sent with Content-Encoding: br, and body-parser 2.x, the default in Express 5, decompresses br request bodies unless inflate is turned off. Node.js has not commented on either path; both use the patched decoder, so we treat them as exposed.

High CVE-2026-105482: DNS resolve truncates names at a NUL character

Affects 22.x, 24.x, 26.x. Fix: Node.js commit

The DNS resolver hands names to c-ares as C strings, which end at the first NUL character. A name such as example.com\0.trusted.corp passes a JavaScript check like endsWith(".trusted.corp"), but the query actually goes out for example.com. The fix commit uses this example.

Who is exposed: applications that pass user-supplied names to dns.resolve, any resolve* method, dns.reverse or a Resolver instance after checking them as strings, for example to enforce an allowlist. After the fix these calls throw ERR_INVALID_ARG_VALUE for names containing a NUL. The change does not touch dns.lookup.

High CVE-2026-106069: TLS hostname bypass with non-canonical IP addresses

Affects 22.x, 24.x, 26.x. Fix: Node.js commit

Before checking a server’s certificate, Node.js normalised the hostname. Strings such as 0127.0.0.1, 0x7f.0.0.1 or 127.1 could be checked against the certificate as one address while the operating system’s resolver connected to another. The fix’s test pairs 0127.0.0.1, which the resolver reads as octal and connects to 87.0.0.1, with a certificate check against 127.0.0.1.

Who is exposed: applications that open TLS connections to host strings an attacker can influence, such as webhook senders, URL previewers and proxies. Based on the fix’s tests, the attacker also needs a certificate the client trusts whose DNS name is the IP address written normally. Connections to fixed, ordinary hostnames are not affected. After the fix, tls.checkServerIdentity rejects these hosts with ERR_TLS_CERT_ALTNAME_INVALID.

High CVE-2026-105483: TLS hostname bypass with fullwidth digits

Affects 24.x, 26.x. Fix: Node.js commit

A related regression in the same hostname check. A host written with fullwidth digits, such as 127.0.0.1, was normalised into an ordinary IP address and could then match a certificate issued for that address. The fix rejects any host that only becomes an IP address after normalisation. Node.js 22 does not have the regression.

Node.js rates this high, but the fix commit itself describes it as a low-severity bug that is not easy to exploit. We show the rating from the security release post.

Medium CVE-2026-106072: DNS names in certificates accepted for IP endpoints

Affects 22.x, 24.x, 26.x. Fix: Same commit as CVE-2026-106069

A second report of the same underlying problem, fixed by the same change. A certificate whose DNS name reads 127.0.0.1 could authenticate a connection made to a non-canonical form of the address, such as 0177.0.0.1. Upgrading for CVE-2026-106069 fixes this too.

Medium CVE-2026-105481: Keep-alive socket accepts a response sent too early

Affects 22.x, 24.x, 26.x. Fix: Node.js commit

When the HTTP client reuses a kept-alive connection, a server could write a response onto the idle socket before the next request was sent, and Node.js would treat it as the answer to that request. An earlier guard only caught this when the event loop had polled the socket. The fix asks the operating system whether unread data is waiting before reusing a socket, and opens a new connection if it is.

Who is exposed: clients using an http.Agent with keepAlive enabled that talk to a server which is malicious or compromised, or to a plain HTTP server through an attacker on the network path.

Medium CVE-2026-106074: Repeated random values in snapshots and SEAs

Affects 22.x, 24.x, 26.x. Fix: Node.js commit

crypto.randomUUID() and crypto.randomInt() keep a small cache of random values. When an application built a startup snapshot, the unused part of that cache was saved into it, so every process started from the snapshot produced the same first values. The fix clears the caches when a snapshot is written and when it is loaded.

Who is exposed: only applications built as single executable applications with useSnapshot enabled, or that use user-land startup snapshots. Ordinary applications are not. Node.js has not published rotation guidance. We recommend rebuilding affected binaries on a fixed version and rotating any tokens, nonces or other secrets they generated with these functions, since those values may be predictable.

Low CVE-2026-106070: ZipFile returns stray bytes after a failed header check

Affects 26.x. Fix: Node.js commit

zlib.ZipFile is an experimental API added in 26.8.0 for reading ZIP archives. When a ZIP entry failed header validation, a later read of that entry could return bytes from the wrong position in the file. Only 26.x has this API.

Low CVE-2026-106071: Permission Model over-grants with wildcard paths

Affects 26.x. Fix: Node.js commit

With the Permission Model enabled, a wildcard path in --allow-fs-read or --allow-fs-write could grant access to more paths than it should. Node.js has not described which paths were over-granted.

Low CVE-2026-106073: Permission Model bypass through fs.realpathSync

Affects 22.x, 24.x, 26.x. Fix: Node.js commit

fs.realpathSync() skipped the Permission Model check that its siblings, such as fs.promises.realpath() and fs.existsSync(), already performed, so it could be used on paths the process had not been granted.

On both Permission Model issues: the Node.js documentation describes the Permission Model as a seat belt that stops trusted code from accidentally touching files it was not granted, and states that it does not protect against malicious code. Do not rely on it to contain untrusted code, patched or not.

Dependency updates

The releases also update libraries that Node.js bundles. You get these by upgrading Node.js; there is nothing to install separately.

Bundled dependency versions in each fixed release
Library22.23.424.22.126.12.1What it fixes
c-ares (DNS resolver)1.34.91.34.91.34.9Eleven upstream advisories, mostly use-after-free bugs when lookups are cancelled or servers change during a callback, and one where a reply from a different server than the one queried could be accepted. c-ares 1.34.9
OpenSSL3.5.93.5.9UnchangedCVE-2026-84782 (high), a DTLS retransmission bug that can leak heap memory or crash, plus low-severity fixes. Node.js exposes TLS over TCP, not DTLS, so we expect few applications to reach the high-severity path. OpenSSL vulnerabilities
Undici (fetch)6.29.17.30.18.12.2Includes CVE-2026-109383 (high), where function-valued TLS options such as a custom checkServerIdentity were silently dropped by Agent, CVE-2026-87018 (high), a crash through TLS 1.2 renegotiation, and several denial-of-service fixes. 7.x and 8.x also fix a cache poisoning bug, CVE-2026-109829. Undici 6.29.1, Undici 8.12.2
SQLite3.53.4UnchangedUnchangedBrings 22.x up to the SQLite version already in 24.x. Node.js lists it as a dependency update and names no CVE.
Root certificatesNSS 3.131.1NSS 3.131.1NSS 3.131.1Rejects chains containing the eight compromised eMudhra intermediates (#66871) and removes three emSign roots (#66877). See our eMudhra advisory.

Timeline

Dates are UTC. Upstream dependency releases are included because the Node.js fixes depend on them.

  1. 2026-09-29

    2026-09-29

    OpenSSL releases 3.5.9, fixing CVE-2026-84782.

    OpenSSL vulnerabilities

  2. 2026-10-15

    2026-10-15

    Undici releases 6.29.1, 7.30.1 and 8.12.2.

    Undici releases

  3. 2026-10-19

    2026-10-19

    c-ares publishes security release 1.34.9.

    c-ares 1.34.9

  4. 2026-10-20

    2026-10-20

    Node.js merges the eMudhra intermediate blocklist and the NSS 3.131.1 root store update.

    nodejs/node #66871

  5. 2026-10-21

    2026-10-21

    Node.js releases 22.23.4, 24.22.1 and 26.12.1 and publishes the security release post.

    Node.js security release post

  6. 2026-10-21

    Official node images for all three versions are pushed to Docker Hub.

    nodejs/docker-node

Questions

Which Node.js version should I upgrade to?

Node.js 22.23.4, 24.22.1 or 26.12.1, or any later release in the same line. These are the first releases that contain the October 2026 fixes. Rebuild container images and single executable applications as well, because each carries its own copy of the runtime.

Is Node.js 20 affected by the October 2026 vulnerabilities?

Yes, and it receives no fix. Node.js 20 reached end of life on 30 April 2026, and the Node.js project states that end-of-life versions are always affected when a security release occurs. Move to Node.js 22 or 24.

What is the most serious vulnerability in this Node.js release?

CVE-2026-111497, an out-of-bounds write in the Brotli decoder bundled with Node.js, which Node.js rates high and says permits remote code execution. Any code that decompresses Brotli data from a source it does not control can reach it.

Do fetch and Express reach the Brotli bug?

Node.js has not said, but both use the affected decoder. The built-in fetch, provided by Undici, decodes responses sent with Content-Encoding: br using zlib.createBrotliDecompress. body-parser 2.x, the default in Express 5, decompresses br request bodies by default. Treat fetch calls to servers you do not trust, and Express 5 endpoints that accept request bodies, as exposed until you upgrade.

How can I tell if someone tried to exploit the Brotli bug?

On an unpatched runtime, a successful trigger is likely to look like a Node.js process crashing while handling compressed input. After upgrading, the fixed decoder rejects these streams with the error code ERR__ERROR_FORMAT_BLOCK_SWITCH (errno -17). Alert on that code in application logs and keep the request that caused it. No public exploit or indicators of compromise had been published as of 21 October 2026.

Do I need to change my code after upgrading?

Usually not. The release notes list no breaking changes. Connections to hosts written as non-canonical IP addresses, such as 0127.0.0.1, now fail certificate checks, and DNS resolve calls with a NUL character in the name now throw. Single executable applications and startup snapshots must be rebuilt with the fixed version.

Are the official Node.js Docker images updated?

Yes. The node images on Docker Hub were published for 22.23.4, 24.22.1 and 26.12.1 on 21 October 2026. Rebuild your images on a fresh pull so they pick up the new base.

Does this release include the eMudhra certificate fix?

Yes. All three releases reject certificate chains that contain the eight compromised eMudhra intermediates and update the bundled root store to NSS 3.131.1, which removes three emSign roots.

Sources