Go back

Every Endpoint Now Runs Node: How AI Tools Multiplied an Unmanaged Runtime

Daniel Alima
Daniel Alima
15 Nov 2026
10 min read
Threat Research

Node.js is now an endpoint runtime

Two years ago, Node.js on a corporate laptop meant a web developer’s toolchain. Today it is a prerequisite for the AI tools everyone wants. The major coding agents install through npm, their MCP servers launch through npx, and the desktop AI apps and editors around them ship Node inside their own binaries.

The result is not one Node.js per endpoint but several, installed by different tools, living in different folders and updated by different mechanisms, or by none. Security programs still treat Node as a developer’s problem. My argument is that AI tools have made it an endpoint problem, and that the number of copies matters more than any single vulnerability in them.

Where the copies come from

A single developer laptop running a couple of AI tools can easily carry four or five independent Node runtimes. Each arrives through a different door.

SourceWhere it livesHow it gets updatedSeen by software inventory?
System install for npm-based agent CLIsProgram FilesManually, by whoever installed itUsually
Version managers (nvm-windows, fnm, Volta)The user profileBy the developer, if at allRarely
MCP servers and tools launched with npxnpm cache, run on whichever node is first on PATHThe package refreshes; the runtime never doesNo
Electron-based AI apps and editorsEmbedded in the app’s own binaryWhen the app auto-updatesAs the app, not as Node
Single-file agent binaries (Bun or Node single-executable builds)Embedded in the executableOnly when the vendor rebuildsNo

Only the first row looks like software IT manages. The rest are invisible to an inventory that reads the installed-programs list, and they are exactly where AI tooling has been adding runtimes.

Why patching misses them

Even when a Node vulnerability is announced and the fix is ready, most of those copies stay vulnerable. Four habits keep them that way:

  • Prerequisites read as floors, not ceilings. Agent tooling commonly documents “Node 18 or newer”. That tells a developer an old runtime is fine, even though Node 18 and Node 20 are end of life and receive no security fixes at all.
  • Updating the agent does not update the runtime. npm update -g refreshes the agent package, not the node.exe underneath it.
  • Embedded runtimes depend on the vendor. An Electron app or a single-file agent binary is patched only when its vendor rebuilds and ships, and few release notes say which Node version a build contains.
  • The working copy wins. When an agent and its MCP servers run fine, nobody has a reason to touch the version manager that installed their runtime months ago.

Node.js now ships a security release every few months, and each one is supposed to end with every copy updated. On an AI-era endpoint, that means first finding copies nobody knew were there.

The agent decides where Node connects

An outdated runtime on a laptop used to be low risk, because something had to feed it hostile input. A web developer’s local Node mostly talked to localhost and the npm registry. An agent’s Node talks to whatever the agent decides to reach.

And the agent decides based on what it reads: a link in a README, an issue comment, a docs page, a result returned by an MCP tool. That makes prompt injection a delivery mechanism for runtime bugs. An attacker does not have to trick the user into visiting a server. They only have to put text where the agent will read it, then answer the request that follows.

This revives a whole class of Node vulnerabilities on the endpoint: flaws in the HTTP client, TLS verification, decompression and parsing code that every one of those copies carries. The browser has a sandbox, site isolation and aggressive auto-update to absorb such bugs. A Node process started by an agent has the user’s privileges and, often, a months-old runtime.

How direct the path is varies by product. Some agents route web fetches through the vendor’s cloud, which keeps that parsing off the laptop. Locally running MCP servers almost always make their calls from the endpoint itself.

npx turns Node into a launcher

The runtime is also how new code arrives. The standard way to extend an agent is a configuration line such as npx -y some-server@latest. Each time the agent starts, that line downloads a package from the public registry, unpinned, and executes it on the local Node with the user’s full privileges.

No installer runs and no software approval process sees it. The configuration file that triggers it often lives inside a project repository rather than with IT. A hijacked package, or a project config a developer cloned without reading, becomes code execution the next time the agent launches.

These MCP servers often also listen on a local HTTP port. Binding to localhost narrows that exposure but does not remove it: any local process can reach the port, and the MCP specification itself warns local HTTP servers about DNS rebinding.

A perfect place to land

A compromised agent runtime has unusually good reach. It runs as the user, so it can read the agent’s model API token plus the git credentials, cloud CLI profiles, registry tokens and SSH keys in that user’s profile. MCP servers add their own SaaS tokens on top.

It is also hard to see. The rule that fires when winword.exe launches PowerShell has been relaxed for node.exe on developer machines, because node.exe launching shells is the agent doing its job. The same goes for broad file reads and outbound connections to dozens of new domains. Memory heuristics struggle too: V8’s JIT allocates executable memory that no image on disk backs, so execution from unbacked regions, a staple shellcode signal, is noisy in node.exe and often exempted.

With several copies of Node on the machine, each from a different source, even a clean baseline of “which node.exe is normal” is hard to build. What remains is context: did that shell or connection match a tool call the agent actually made? Answering that means joining agent telemetry, such as hooks and tool-call logs, with OS telemetry. Few stacks do it today, and agent-side hooks are uneven across vendors and platforms.

Case in point: one CVE, many copies

The past three weeks show how this plays out. In October, Node.js patched Node2Shell (CVE-2026-111497), a memory-corruption bug in the Brotli decoder it bundles that can lead to remote code execution, and CISA has since confirmed exploitation. One of its paths is Node’s built-in fetch(), which decompresses Brotli responses automatically, so it is a client-side bug as much as a server one.

The interesting question for an endpoint team was never the bug itself. It was how many vulnerable copies of Node each laptop held, and how many of them anyone could find. Coverage and tooling focused on servers listening on the network; the agent runtimes, version-manager copies and embedded builds on laptops went largely uncounted.

And the cycle is already repeating. This week Node.js pre-announced another security release for November 11, rated HIGH on every supported line. Whatever it fixes, every copy in that table needs finding and patching again.

How to close the gap

The patching problem has two layers: the Node runtimes AI tools put on the endpoint, and the packages those runtimes download and run. They fail for different reasons, but one program handles both: find, reduce, patch, contain.

Find

  • Record every runtime as it runs. Capture the path and file version of every node.exe and bun.exe at process start. This catches short-lived npx copies a periodic scan misses, and shows which copies actually execute.
  • Build a bill of materials (SBOM) for the agent environment. List global packages for each runtime, the npx cache, and the MCP servers in each agent’s configuration, together with each one’s full dependency tree, not just the top-level package. npx-launched servers have no project manifest, but each cache folder has its own package files, so standard scanners can run against them. Then track change: record the resolved version and a hash of each package’s installed files, and compare on every scan. A version that moved on its own points to an unpinned @latest; files that changed while the version stayed the same point to tampering after install. npm audit signatures adds a check that packages match what the registry signed.

Reduce

  • Standardize on one managed runtime. Deploy a single Node LTS through normal software deployment and put it first on PATH so agents and npx use it. Retire per-user version managers.
  • Route every install through one registry. Point all users’ npm configuration at an internal registry proxy that blocks deprecated and known-vulnerable versions and holds newly published ones for a few days.
  • Allowlist and pin MCP servers. Approve one server per service, at an exact version, and prefer the service owner’s own server. No @latest.

Patch

  • Patch the runtime on a deadline. Node.js pre-announces security releases about a week in advance. Stage the update during that week, then push it within a fixed window.
  • Enforce a minimum version. Official Windows builds are signed, so an application control rule with a minimum file version blocks outdated copies wherever they live.
  • Review pins on a schedule. A pinned package stops changing, and so do its vulnerabilities. Review the allowlist regularly and whenever a relevant advisory appears.
  • Make vendors own embedded runtimes. Require every agent CLI and Electron app to disclose its bundled Node version and how quickly it ships upstream security fixes.

Contain

  • Disable install scripts. Set ignore-scripts=true globally, with exceptions for packages that compile native code.
  • Limit what agents and MCP servers can reach. Scope file access and credentials to the task, and route agent traffic through an inspecting proxy. Node’s permission flags add a layer, but they are not a security boundary.
  • Detect by process lineage. Alert when an agent’s node.exe or an MCP server spawns, loads or connects to something no tool call explains. That includes unsigned .node addons loaded from package caches.

One number shows whether all of this works: for each Node security release, the time until no vulnerable runtime executes anywhere in the fleet.

The second front door

To be clear about the limits: this is an argument about exposure, not a report of a campaign. I have seen no public evidence yet of attackers targeting AI agents through Node runtime bugs, and turning a client-side memory flaw into reliable code execution on a hardened Windows machine is hard.

But the conditions are in place. AI tools have put several unmanaged Node runtimes on every endpoint that uses them, given those runtimes the user’s credentials, and handed the choice of where they connect to an agent that can be steered by text. Endpoint programs spent a decade learning to treat the browser as the front door. Node is now a second one, and it deserves the same inventory, patching and monitoring discipline before the first incident report forces the issue.

Sources

About the author

Daniel Alima

Daniel Alima is the Security Research Team Leader at Morphisec, where he leads advanced threat research focused on malware analysis, vulnerability discovery, ransomware, AI security, and emerging attack techniques. He regularly publishes technical research and threat intelligence, helping organizations better understand sophisticated cyber threats and strengthen their defenses against modern adversaries. Daniel is an active contributor to the cybersecurity community, sharing insights on vulnerability research, threat hunting, endpoint security, and the evolving risks associated with AI-powered development tools.

Stay up-to-date

Get the latest resources, news, and threat research delivered to your inbox.

Gartnerยฎ Emerging Tech: Top Use Cases Driving Preemptive Cybersecurity Growth