Skip to main content
Blog|
Latest news

The October 2026 Node.js security release: what the bugs do and why apps on Hostney need only a rebuild

|
Oct 23, 2026|13 min read
LATEST NEWSThe October 2026 Node.jssecurity release: what thebugs do and why apps onHostney need only a rebuildHOSTNEY™hostney.comOctober 23, 2026

Short answer: On October 21, 2026, Node.js released 26.12.1, 24.22.1 and 22.23.4 to fix ten vulnerabilities, four of them rated high. The most serious is an out-of-bounds write in Node’s built-in Brotli decompression, which the advisory says permits remote code execution. Most of the others affect apps that connect to addresses someone else can influence, or use features most apps never turn on. Node.js 20 and older get no fix. On Hostney, apps never depended on the server’s Node.js: each app carries its own Node.js inside its container, built from the official Node.js image, so the fix reaches an app with its next build and does not wait for an operating system update. The control panel now flags every app whose Node.js is behind a security release, and each app runs in its own container, as an unprivileged user with no Linux capabilities, where a bug in Node.js reaches that one app and nothing else.

What happened#

On October 21, the Node.js project published security releases for its three supported lines: 26.12.1, 24.22.1 and 22.23.4. They fix ten vulnerabilities, four rated high, three medium and three low. They also update components that Node.js bundles: Undici (the library behind Node’s built-in fetch() ), the c-ares DNS library, OpenSSL on the 24 and 22 lines, SQLite on 22, and the list of trusted certificate authorities, after the compromise of an intermediate certificate authority run by eMudhra.

Node.js 20 reached end of life on April 30, 2026, so it got nothing. The advisory says so directly: “End-of-Life versions are always affected when a security release occurs.” Every line older than 20 has been out of support even longer.

The advisory lists each vulnerability by title, severity and the release lines it affects, with little detail beyond that. The explanations below describe what each title says the bug does, and which apps that puts in reach.

What the bugs do#

PHP2Shell, the PHP vulnerability fixed the same day, sat in code that runs for every request to every PHP site. These bugs are different. Each one lives in a specific part of Node.js, and an app is exposed only if it uses that part on data someone else controls. For each bug, the question is whether your app does that.

The Brotli decoder (CVE-2026-111497, high)#

Brotli is a compression format that web servers use alongside gzip, and Node.js has a Brotli decoder built into its zlib module. The bug is an out-of-bounds write: a crafted compressed stream makes the decoder write past the end of the memory it set aside, and the advisory says this permits remote code execution. Someone who can hand an app such a stream may be able to run code inside the app’s Node.js process.

Apps often decompress Brotli without calling the decoder themselves. Node’s built-in fetch() unpacks Brotli-compressed responses automatically, and so do many HTTP client libraries. So an app that fetches URLs from servers it does not control is decompressing data an attacker can shape. That covers link previews, feed readers, scrapers, and webhooks or integrations pointed at addresses the app’s users enter. An app that accepts Brotli-compressed request bodies and unpacks them is exposed the same way. Of the ten bugs, this is the one to fix first.

Three ways around the certificate check (CVE-2026-105483, CVE-2026-106069, CVE-2026-106072)#

When Node.js opens an HTTPS or other TLS connection, it checks that the server’s certificate was issued for the name or address it connected to. How SSL certificates work explains why that check is the point of a certificate: without it, a valid certificate for any site would pass for every other site. All three bugs let a certificate pass the check for an address it was not issued for, by writing the address in an unusual way:

  • an IP address written with fullwidth digits, the wide forms of 0 to 9 used in East Asian text (high, Node.js 26 and 24 only)
  • a host name that Node.js loosely reads as an IP address (high)
  • an IPv4 address written in a non-canonical form, which lets a certificate for a DNS name authenticate a connection to an IP address (medium)

To use any of them, an attacker needs a say in the address the app connects to, plus a way to sit between the app and that address. The apps at risk are the ones that connect to addresses taken from user input or outside data. An app that talks only to its own database and a few well-known APIs, by their normal names, is a much harder target.

A DNS lookup for a shortened name (CVE-2026-105482, high)#

Node’s dns.resolve functions cut a name short at a NUL character, a zero byte. A request to resolve attacker.example\0.trusted.example could look up attacker.example instead. Some apps check a host name against an allow-list by its ending and then resolve it, as many protections against server-side request forgery do. Those apps can be sent somewhere they never meant to allow. The apps in reach are those that resolve host names taken from user input.

A reused connection that answers early (CVE-2026-105481, medium)#

Node’s HTTP client keeps a connection to a server open and reuses it for the next request, which is what keep-alive means. The bug let a reused connection hand the next request a response the server had written before that request was even sent. A server can therefore line up an answer in advance for whatever the app asks next on that connection. The apps in reach are those that call servers they do not fully trust, and how much harm a pre-written answer can do depends on what the app does with it.

Random values that repeat (CVE-2026-106074, medium)#

crypto.randomUUID() and crypto.randomInt() keep a small cache of random data so they stay fast. If an app was built into a startup snapshot ( --build-snapshot ) or packaged as a single executable application, that cache was saved into the file. Every process started from the file then produced the same “random” values: the same UUIDs and the same numbers, run after run. Apps started the usual way, with node server.js or npm start , are not affected.

The three low-severity bugs#

Two are in the Permission Model, Node’s opt-in way to limit what a process may read and write. They affect only apps started with --permission : CVE-2026-106073 on every line, and CVE-2026-106071 on 26 only. The third is in Node 26’s ZIP reader, which could return bytes an attacker placed in front of a ZIP header after that header had failed validation (CVE-2026-106070, 26 only).

Which versions are fixed#

Node.js lineFixed inStatus
2626.12.1Fixed. Becomes the newest LTS line on October 28, 2026
2424.22.1Fixed. Supported until April 2028
2222.23.4Fixed. Supported until April 2027
20 and oldernoneEnd of life, no fix

Every later release in those lines includes the fixes. A Node.js line past its end of life is in the same position as old PHP: every new security release adds bugs that will never be fixed in it, as we covered in the risks of running end-of-life PHP.

What we did on Hostney#

Your app's Node.js is not the server's#

Each app deployed from Git runs in its own container, built from the official Node.js image for the version picked in the app’s build settings, with that Node.js inside the image. The app does not use any Node.js installed on the server, so an update on the server changes nothing inside an app’s container, and an app’s Node.js never waits for one. Deploying Node, Python and Ruby apps on Hostney explains how builds and app containers work.

Apps get the fix with their next build#

Each app’s container image is built on the official image for its Node.js version, node:<version>-alpine from Docker Hub. Those images were rebuilt with the fixed releases within hours. By the early hours of October 22, UTC, node:24-alpine carried 24.22.1 and node:22-alpine carried 22.23.4. Every build on Hostney pulls that image fresh instead of reusing a copy kept on the server, so any build started since then runs the fixed release of its version. There was no operating system package to wait for, and nothing we had to roll out first. For comparison, Rocky Linux 9’s own Node.js packages were still at 22.23.2 and 24.21.0 on October 23, two days after the release, and neither has the fixes.

What does not happen is a build on its own. A running app keeps the Node.js of its last build until it is built again. We do not rebuild apps without their owners: a build installs the app’s dependencies again from its repository and replaces the running container, so the timing stays with whoever owns the app. Instead, the control panel now tells you which apps need a build.

The control panel shows which apps are behind#

Every build now records the exact Node.js version inside the app’s container, and for apps that were already running we read it from their current images. When that version is behind a Node.js security release, or its line has reached end of life, the app’s page shows a banner that says what to do. Usually that means building the app again, and sometimes picking a newer version in its Settings tab first. The app’s Vulnerabilities tab lists its Node.js version, the newest release of that line and the known CVEs fixed since. The verdict comes from the Node.js project’s own list of releases, so the next security release will flag affected apps the same way.

For accounts that have the dependency vulnerability email turned on, the email now includes apps running an outdated Node.js. Ellie, the assistant in the control panel, gives the same answer when you ask about an app’s vulnerabilities.

Node.js 26 in the builder, and 20 kept with a warning#

The version list in an app’s build settings now offers Node.js 26, 24, 22 and 20. Node.js 24 is marked as recommended and is the default for new apps. Node.js 20 stays in the list, the same way older PHP versions stay in the PHP manager, so an app that still needs it keeps building. Whenever 20 is picked, the settings form warns that it gets no security fixes.

What keeps a bug contained#

An app’s code is copied into its image at build time, so its container holds none of the account’s other files, no other app and nothing of any other customer’s. The Node.js process runs as the image’s unprivileged node user, with every Linux capability dropped, setuid programs unable to raise its privileges, and its own memory, CPU and process limits inside the account’s resources. How we isolate websites with containers covers the model behind this. Remote code execution through the Brotli bug would be serious on any platform. On ours, it would start inside one app’s container, with that app’s code and environment variables in reach and nothing beyond them.

What you should do#

If your apps are on Hostney#

Open each app in the control panel. If it shows the Node.js banner:

  1. If the banner says the app’s Node.js line has reached end of life, open the app’s Settings tab, pick Node.js 24 and save. If it says the build settings ask for an exact version, pick the major version instead, so every build takes the newest release of that line.
  2. Go to the Deployments tab and click Build now, or push a commit if auto-deploy is on.

Moving from Node.js 20 to 24 is a major version change. Most apps build and run without changes, but read the build log and check the site afterwards. If you want to test first, use the dependency check in the next section. Static sites need nothing: Node.js only built their files, and visitors get those files without any Node.js process involved.

If you host somewhere else#

Find every Node.js you run. node -v shows the one on your PATH, but one server can have several: the operating system’s package, versions installed with nvm, and one inside every container image. For containers, check each running one with docker exec <container> node -v .

Update to 22.23.4, 24.22.1, 26.12.1 or later. If your Node.js comes from your operating system’s repositories, check what they offer first. On Rocky Linux 9, the packages were still at 22.23.2 and 24.21.0 on October 23. The official builds from nodejs.org had the fixed releases on release day, and NodeSource’s packages had them by October 22. On Red Hat based systems, NodeSource’s package will not install over the distribution’s, so remove the distribution’s nodejs packages first. Before you do, check where your global npm packages, such as pm2, are installed ( npm root -g ), so you can reinstall any the removal takes with it.

Restart every Node.js process. A new binary on disk does nothing for a process started before the update. This lists every running process together with the Node.js binary it runs. A path ending in (deleted) marks a process that is still running the old one:

for p in /proc/[0-9]*; do
  exe=$(readlink "$p/exe" 2>/dev/null)
  case "$exe" in *node*) echo "${p#/proc/} $exe" ;; esac
done

Run it as root to see every user’s processes. With pm2, pm2 update restarts pm2’s own daemon along with the apps it runs. Each user who runs pm2 has to do this under their own account.

Rebuild your container images. An image keeps the Node.js of the day it was built. Rebuild with docker build --pull (or podman build --pull ) so the base image is fetched again, then replace the running containers. Without --pull , the build reuses whatever copy of the base image is already on the machine.

Test your dependencies before leaving Node.js 20. Most packages work across major versions, and the ones that do not are usually native add-ons. You can check without installing anything: unpack the official build into a folder of its own, load every dependency with both the old and the new Node.js, and compare the results.

cd /opt
curl -fsSLO https://nodejs.org/dist/v24.22.1/node-v24.22.1-linux-x64.tar.xz
curl -fsSL https://nodejs.org/dist/v24.22.1/SHASUMS256.txt | grep 'linux-x64.tar.xz$' | sha256sum -c -
tar -xJf node-v24.22.1-linux-x64.tar.xz

cd /path/to/your/app
check() {
  for dep in $(node -p "Object.keys(require('./package.json').dependencies || {}).join(' ')"); do
    "$1" -e "require('$dep')" >/dev/null 2>&1 && echo "ok   $dep" || echo "FAIL $dep"
  done
}
check node > current.txt
check /opt/node-v24.22.1-linux-x64/bin/node > new.txt
diff current.txt new.txt

No output from diff means every dependency loads the same way on both versions. A package that goes from ok to FAIL is the one to look at before you switch. Nothing here touches the Node.js your apps run on or their node_modules .

If you think an app was attacked#

Look for processes, files and outgoing connections you cannot explain. Then change every secret the app holds: database passwords, API keys and tokens in its environment variables. On Hostney, change them in the app’s environment variables in the control panel, then build the app again so it starts with the new values. How to check a website for malware covers what to look for among a site’s files.

Summary#

The October 2026 Node.js security release fixes ten vulnerabilities in Node.js 26, 24 and 22. The one to act on first is a Brotli decoder bug that can lead to remote code execution in any app that decompresses Brotli data it does not control. That includes apps whose fetch() calls reach servers they do not control. Three certificate-check bugs and a DNS bug affect apps that connect to addresses users can influence, and the rest need features most apps do not use. Node.js 20 and older get no fix. On Hostney, each app carries its own Node.js in its container, built from the official image, so the fix reaches an app with its next build and never waited for an operating system package. The control panel flags every app whose Node.js is behind. If one of yours is flagged, build it again, and move it off Node.js 20 if it still runs there.

Related articles