Node.js Security Releases: Check If Your Version Is Patched
The October 21, 2026 Node.js security release covers every supported line at once: 22.23.4, 24.22.1 and 26.12.1. Four of its ten CVEs are rated high, and the one that stands out is an out-of-bounds write in the bundled Brotli decoder that, according to the project, can lead to remote code execution. The question for your team is whether the Node.js you run in production is older than the fix. Below you get the fixed version for each line, a short script that answers the question against nodejs.org, and a way to hear about the next security release without refreshing the Node.js blog.
1. What the October 21 Node.js Security Release Fixed
The official announcement lists ten CVEs: four rated high, three medium, three low. It also bundles dependency updates: c-ares, the CA certificate store (to handle the eMudhra intermediate CA compromise) and Undici on all three lines, OpenSSL on 24.x and 22.x, SQLite on 22.x.
The Brotli bug, CVE-2026-111497, is the one with "remote code execution" in its title. The project's description is short: an invalid block switch in malformed input was treated as a temporary need for more data instead of a format error, so the streaming decoder kept appending bytes to an eight-byte internal buffer. Anyone who can feed Brotli data to code that decompresses it with node:zlib can corrupt process memory. A crash is the mild outcome; the announcement also lists memory disclosure and code execution.
The other three high-severity issues are less dramatic and easier to underestimate. One is an incomplete fix: names containing a NUL byte were still truncated by dns.resolve*() and dns.reverse(), so code that checks a hostname against an allowlist and then resolves it could get records for a different name. Two are TLS hostname-verification bypasses triggered by unusual IPv4 spellings, such as fullwidth digits or a leading zero.
Fixed versions: 22.23.4, 24.22.1 and 26.12.1. The announcement lists 22.x, 24.x and 26.x as affected by CVE-2026-111497: upgrade to at least the fixed version of your line. The 20.x line is end-of-life and has no fix.
Where each line stands, from the Node.js release schedule:
| Line | Status on October 25, 2026 | Fixed in |
|---|---|---|
| 26.x | Current, scheduled to become LTS on October 28, 2026 | 26.12.1 |
| 24.x | Maintenance LTS since October 20, 2026 | 24.22.1 |
| 22.x | Maintenance LTS until April 30, 2027 | 22.23.4 |
| 20.x | End-of-life since April 30, 2026 | No fix |
2. Your Laptop Is Not Where Node.js Runs
Running node -v in your terminal answers the wrong question. The version that matters is the one inside the production container, on the CI runner that builds your release artifacts, and in whatever runtime your serverless provider picked. Those three can disagree with each other, and none of them is guaranteed to update because nodejs.org published a blog post. A managed serverless runtime may get the patch on the provider's schedule, which is not yours either.
A Docker image built from node:22 three weeks ago contains whatever 22.x was current when it was built. The tag has moved since; your image has not. Same for a CI cache that restored an old toolchain. Print the version at runtime (a log line at startup, or a health endpoint that returns process.version) so you can check the place that counts.
3. Let nodejs.org Tell You What You Missed
Comparing version numbers by hand against a blog post works once. The machine-readable source is nodejs.org/dist/index.json, which lists every release with its date and a security field that is true for security releases. The question "did I miss a fix?" becomes: is there a security release in my major line that is newer than my version?
// check-node-security.mjs
// Usage: node check-node-security.mjs [version] (default: the running Node)
const current = process.argv[2] ?? process.version;
const [major, minor, patch] = current.replace(/^v/, "").split(".").map(Number);
const res = await fetch("https://nodejs.org/dist/index.json");
const releases = await res.json();
const newer = (v) => {
const [a, b, c] = v.slice(1).split(".").map(Number);
return a === major && (b > minor || (b === minor && c > patch));
};
const missed = releases.filter((r) => r.security && newer(r.version));The rest prints the result and sets the exit code, so a CI step fails when something is missing:
if (missed.length === 0) {
console.log(`${current}: no security release missed in v${major}.x`);
} else {
console.log(`${current} misses: ${missed.map((r) => `${r.version} (${r.date})`).join(", ")}`);
process.exitCode = 1;
}It needs no dependencies, only the global fetch. We ran it on October 25 in the sandbox where this article was written:
$ node -v v22.22.0 $ node check-node-security.mjs v22.22.0 misses: v22.23.4 (2026-10-20), v22.23.2 (2026-07-28), v22.23.0 (2026-06-17), v22.22.2 (2026-03-24) $ node check-node-security.mjs v24.22.1 v24.22.1: no security release missed in v24.x
Four missed security releases on a machine nobody thought of as outdated. Note that the index dates 22.23.4 to October 20 while the announcement is dated the 21st, so don't match releases to announcements by date. Also, the script trusts the version string you pass: a partial value like 22 makes it print a false "no security release missed" and exit with 0, so feed it a full major.minor.patch. An .nvmrc containing 22 or lts/* has the same problem.
4. An End-of-Life Line Looks Clean to That Script
Run the script against v20.20.2 and it reports nothing missed, which is misleading. 20.20.2, from March 24, 2026, is the last 20.x release with the security flag, and no 20.x release came after it, because the line reached end-of-life on April 30. The October announcement is explicit that end-of-life versions are always affected when a security release occurs. The index has no "unsupported" field, so the script can't see this on its own.
The release schedule can. The nodejs/Release repository publishes the same dates as JSON, with an end field per major line:
const schedule = await (
await fetch("https://raw.githubusercontent.com/nodejs/Release/main/schedule.json")
).json();
const end = schedule[`v${major}`]?.end;
if (end && new Date(end) < new Date()) {
console.log(`v${major}.x reached end-of-life on ${end}: it gets no security fixes`);
process.exitCode = 1;
}Appended to the first script, a run with v20.20.2 adds the line "v20.x reached end-of-life on 2026-04-30: it gets no security fixes" and exits with 1. For 22.23.4 the extra check adds nothing. One limit we can't script away: the schedule says when the project stops patching, not when anyone else does. The Node.js site points to commercial support through the OpenJS Ecosystem Sustainability Program for lines past maintenance, and a check built on nodejs.org data knows nothing about patches shipped that way.
5. Run the Check Every Monday in CI
If your repository pins the production version in .nvmrc as a full version (say 22.23.4), a scheduled GitHub Actions workflow can check that pin once a week and go red when it falls behind:
# .github/workflows/node-security.yml
name: node-security
on:
schedule:
- cron: "0 7 * * 1"
workflow_dispatch:
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- run: node scripts/check-node-security.mjs "$(cat .nvmrc)"This uses the runner's preinstalled Node.js only to execute the script; the version being judged is the one in .nvmrc. One detail from the Actions docs: notifications for scheduled workflows go to the user who last modified the cron line, so a red run may land in the inbox of someone who left the team. The same page notes that in a public repository scheduled workflows are disabled after 60 days without repository activity A repository that only exists to run this check is that kind of repository.
If you install Node.js in CI with actions/setup-node and a loose range like node-version: 22, read the check-latest section of its docs. With check-latest at its default of false, the action first looks in the runner cache for a version that satisfies the range, so the build can run on a cached 22.x that predates a fix. Setting it to true makes it download the newest match, at the cost of a slower setup.
6. Hearing About the Next Security Release
The weekly job should catch a stale pin within seven days. To hear on the day, the Node.js project offers two channels of its own: the nodejs-sec mailing list, which the announcement calls low-volume and announcement-only, and the vulnerability RSS feed, which carries the same posts. If you already read feeds, our guide to GitHub release RSS feeds covers how to keep a handful of them manageable.
The third channel is the GitHub releases of nodejs/node, but it reports only one of the releases. GitHub marks a single release per repository as "Latest": by default the most recent full release, unless the publisher overrides it. The REST API docs describe /releases/latest as returning the latest published full release, never a draft or a pre-release. On October 21 Node.js published three releases, and on the releases page only v26.12.1 carries the Latest badge. 24.22.1 and 22.23.4 sit right below it without one.
Any tool that follows the Latest release reports v26.12.1 and nothing else. GitWatchman is one of them, so a user following nodejs/node got a notification for v26.12.1 only. The notes of that release open with "This is a security release." and tag each fix with its CVE, but they don't mention 22.x or 24.x. If you run 22.x, read that notification as "a security release just shipped, run the script", not as an upgrade instruction.
Timing matters too. GitWatchman checks repositories every 2 hours, not in real time. Slack, Discord and Telegram messages go out right after the check; email defaults to a daily digest for new accounts, and you can switch it to Instant in your notification preferences, which is what you want for a runtime you deploy. For the libraries on top of Node.js, the same reasoning applies one level up, as covered in our piece on dependency security.
Frequently Asked Questions
Which Node.js versions fix CVE-2026-111497?
Node.js 22.23.4, 24.22.1 and 26.12.1, released on October 21, 2026. The announcement lists the 22.x, 24.x and 26.x lines as affected. Node.js 20.x reached end-of-life on April 30, 2026 and got no fix: the project states that end-of-life versions are always affected when a security release ships, so the only way out for 20.x is a supported line.
How do I check if my Node.js version is missing security fixes?
Compare the output of node -v with the releases of your major line that nodejs.org/dist/index.json marks with security: true. If any of them is newer than your version, you missed at least one security release. The script in this article does that comparison and exits with code 1, so it can fail a CI job.
Is Node.js 20 still getting security updates?
No. Node.js 20 reached end-of-life on April 30, 2026, according to the Node.js release schedule, and its last security release was 20.20.2 in March 2026. The October 21, 2026 fixes shipped only for 22.x, 24.x and 26.x. The Node.js site points to commercial support through OpenJS Ecosystem Sustainability Program partners for lines past maintenance.
How do I get notified of Node.js security releases?
Subscribe to the nodejs-sec mailing list, which the project describes as low-volume and announcement-only, or add the vulnerability feed at nodejs.org/en/feed/vulnerability.xml to a feed reader. A GitHub release monitor on nodejs/node works too, with one catch: when several lines ship on the same day, only one release carries the Latest badge.
Know the day nodejs/node ships a release
Follow up to 5 GitHub repositories for free during the beta. GitWatchman sends new releases by email, Slack, Discord or Telegram, or to your personal RSS feed.
Start tracking for free