SAN FRANCISCO — July 6, 2026. In the eighteen months since April 2024, the Node.js project has shipped security releases addressing more than twenty distinct CVEs in the runtime itself — not in npm packages, not in transitive dependencies, but in the interpreter, the HTTP parser, the permission model, and the bundled undici HTTP client that ships inside every Node.js installation. For an ecosystem that often treats "supply chain risk" as synonymous with "malicious npm package," that number is a reminder that the runtime underneath the dependency tree is itself an attack surface — and one that most vulnerability scanners still under-cover.
This roundup walks through the two most consequential waves of Node.js runtime vulnerabilities since early 2024, explains why runtime CVEs behave differently from package CVEs in a typical application, and lays out what security and platform teams should actually do about it — beyond the reflexive "upgrade Node."
The Numbers: A Runtime Under Sustained Scrutiny
Node.js ships coordinated security releases on a rolling basis, batching multiple CVEs per release rather than patching one at a time. Since April 2024, the project has published security releases in April 2024, September 2024, November 2024, and multiple rounds through 2025, spanning the then-current 18.x, 20.x, 21.x, 22.x, and 23.x release lines. The pattern across these releases is consistent: HTTP parsing edge cases, permission-model bypasses, and flaws in undici (the built-in fetch() implementation) account for the large majority of disclosed issues, with a smaller but recurring set of platform-specific bugs — particularly on Windows — rounding out the list.
That concentration matters. It tells us where the Node.js core team's own attack surface is thinnest, and it tells defenders where to focus reachability analysis: HTTP request handling, outbound fetch calls, child-process invocation, and any code path that touches the experimental permission model (--permission).
Case Study: The April 2024 Wave
The clearest illustration of "runtime CVE" risk is the batch of fixes Node.js shipped on April 3, 2024, across the 18.x, 20.x, and 21.x lines. Five CVEs landed together:
- CVE-2024-27980 — A command injection flaw in
child_process.spawn()andspawnSync()on Windows. When an application invoked a.bator.cmdfile through these APIs with theshelloption, insufficiently strict argument validation allowed an attacker-controlled argument to break out and execute arbitrary shell commands. This one drew the most press attention because it was trivially exploitable in any Windows service that shelled out to batch scripts with user-influenced arguments — a common pattern in CI runners, build tooling, and Windows-hosted Node services. - CVE-2024-27982 — An HTTP request smuggling issue caused by lenient parsing of chunked-encoding extensions in Node's HTTP server, letting a malicious client construct a request that front-end and back-end servers would interpret differently.
- CVE-2024-27983 — A denial-of-service condition in the HTTP server triggered by an unbounded number of connections left in a particular internal state, allowing an attacker to exhaust server resources with a modest request volume.
- CVE-2024-21892 — An improper bypass of the experimental permission model, undermining the guarantees teams relied on when using
--permissionto sandbox file-system or network access for untrusted code. - CVE-2024-22025 — A resource-exhaustion denial-of-service vector in
undici'sfetch()implementation.
Individually, none of these read as catastrophic. Together, they describe a runtime where the HTTP stack, the process-spawning APIs, and the sandboxing primitives all had exploitable gaps in the same six-month window — and where a single unpatched production fleet was plausibly exposed to smuggling, injection, and DoS simultaneously.
2025: Permission Model and Path Handling Take Center Stage
Through 2025, the center of gravity shifted toward two categories: continued hardening of the permission model (still marked experimental, still shipping bypasses) and path-handling flaws on Windows, where drive-relative paths and reserved device names (COM1, CON, NUL, and similar) repeatedly proved capable of defeating path-traversal protections that application code assumed were airtight. Node's own security team has been candid that the permission model — introduced to let developers restrict a process's filesystem, network, and child-process capabilities — is not yet a hard security boundary, and 2025's disclosures reinforced that message: several fixes closed gaps where a sandboxed script could still reach file paths or spawn processes the permission flags were meant to block.
The practical takeaway for security teams is blunt: if your threat model currently treats --permission as a trust boundary between untrusted code and the host, that assumption has needed revisiting more than once in the past two years. Treat it as defense-in-depth, not isolation.
Why Runtime CVEs Are Different From Package CVEs
Most vulnerability management programs are tuned to answer "is this npm package version affected?" — a question SBOM diffing and lockfile scanning answer well. Runtime CVEs break that model in three ways.
First, the runtime is often invisible to dependency scanners. A Node.js binary isn't a node_modules entry; it's the container base image, the Lambda runtime, the version pinned in a .nvmrc that half the fleet has silently drifted away from. Teams that are diligent about npm audit and lockfile hygiene frequently have no equivalent process for tracking which Node.js minor version is actually running in each service.
Second, exploitability is highly path-dependent. CVE-2024-27980 only matters if your code actually calls child_process.spawn with shell: true against a .bat/.cmd target on Windows. CVE-2024-27982's smuggling risk only materializes if your Node service sits behind a front-end proxy that parses chunked encoding differently. A CVSS score tells you the theoretical ceiling; it says nothing about whether your call graph ever reaches the vulnerable code path. This is precisely where blanket "upgrade everything" mandates waste engineering time on services that were never exposed, while genuinely reachable instances get lost in the noise.
Third, the fix isn't always a version bump you control. Runtime upgrades often mean rebuilding and redeploying every container image pinned to an affected Node version — a fleet-wide operation, not a single package-lock.json commit, and one that competes for the same deployment windows as every other release.
The EOL Problem Nobody Wants to Own
Layered on top of all of this is lifecycle risk. Node.js 18 reached end-of-life in April 2025; Node.js 16 reached EOL back in September 2023. Every CVE disclosed after a line's EOL date is one that line will never receive a patch for — yet EOL runtimes persist in production far longer than anyone plans, embedded in base images that haven't been rebuilt, in vendor appliances nobody controls, and in "legacy but stable" services that fell off the upgrade roadmap. A fleet running even a handful of EOL Node.js instances is accumulating unpatchable exposure with every subsequent security release, whether or not any given CVE happens to be reachable.
How Safeguard Helps
Safeguard treats the Node.js runtime itself as a first-class asset in its software supply chain graph, not a blind spot between the OS layer and the application layer. Through SBOM generation and ingestion, Safeguard identifies the exact Node.js version — down to patch level — running across every service, container image, and CI job, surfacing EOL and vulnerable runtimes that dependency-only scanners miss entirely. Reachability analysis, powered by Griffin AI, then traces whether your application's actual code paths invoke the vulnerable APIs behind a given CVE — such as child_process.spawn with a .bat target, or an exposed HTTP server hitting the affected chunked-parsing logic — so teams can distinguish genuinely exploitable exposure from theoretical CVSS scores and triage accordingly. Where a fix requires more than a runtime bump, Safeguard's auto-fix PRs propose the concrete remediation — updated base image tags, dependency pins, or configuration changes — directly against the affected repository. The result is a runtime vulnerability program that prioritizes by actual reachability and blast radius, not just CVE count, and that closes the gap between "Node.js shipped a patch" and "our fleet is actually protected."