- The Mess: vm2 has been hit by a fresh wave of sandbox escapes published on September 17, including a CVSS 10.0 flaw that lets sandboxed JavaScript manipulate host-side prototypes and reach code execution outside the VM boundary. Multiple other escapes were disclosed at the same time.
- The Damage: Applications using vm2 to execute attacker-controlled JavaScript can lose the isolation boundary entirely, handing malicious code access to the privileges of the Node.js host process.
- The Fix: Upgrade to the latest patched vm2 release and audit the Node.js runtime and sandbox configuration; older vm2 versions have accumulated a long chain of independent escape techniques.
vm2 is supposed to do one job: take JavaScript you don’t trust and run it somewhere you can safely ignore.
That promise has become increasingly difficult to defend.
On September 17, 2026, another batch of vm2 vulnerabilities appeared in public vulnerability databases. Among them are CVE-2026-92955, CVE-2026-92956, CVE-2026-92948, CVE-2026-92950 and CVE-2026-92934.
They don’t all use the same primitive.
That’s the interesting part.
The bugs attack different pieces of the boundary: prototype handling, Promise internals, Node builtins, exception sanitization and module resolution. The common result is the same: code that was supposed to remain inside vm2 can reach the Node.js host.
CVE-2026-92955: the CVSS 10 escape
This is the ugly one.
CVE-2026-92955 affects vm2 versions before 3.11.8 and carries a CVSS 4.0 score of 10.0.
The vulnerability exists in NodeVM.
Sandboxed code can reach the host __proto__ getter/setter through console._stdout and console._stderr. From there, the attacker can manipulate EventEmitter.prototype.emit and trigger process events with the host process context.
That breaks the security boundary and allows arbitrary code execution in the host Node.js process.
This is exactly the type of primitive a sandbox is supposed to prevent.
The attacker isn’t supposed to get a host prototype.
The attacker isn’t supposed to alter host-side event machinery.
And the sandbox certainly isn’t supposed to provide a path to the host process context.
CVE-2026-92956: Node.js 26 makes the Promise problem worse
Then there is CVE-2026-92956.
This one is particularly nasty because the vulnerable configuration can be surprisingly ordinary.
The issue affects vm2 3.10.1 through 3.11.6 when running on Node.js 26.
A default:
new VM()
was demonstrated as vulnerable.
The attack abuses the interaction between WebAssembly.compileStreaming() / instantiateStreaming(), host-realm Promise objects and Promise.prototype.finally().
By manipulating Symbol.species, sandbox code can cause a host error object to cross the boundary without the sanitization vm2 expects.
From there the attacker can walk the constructor chain to the host Function constructor and recover the host process object.
The result is arbitrary code execution with the privileges of the Node.js host.
There is an important detail here.
The vm2 security report says the escape was reproduced on Node.js 26.0.0 through 26.7.0, while Node.js 22, 24 and 25 did not reproduce the specific issue in the tested configuration. It was also reproduced on Linux and Windows.
So this isn’t simply “old vm2 is broken.”
It is a nasty interaction between a sandbox implementation and a newer JavaScript runtime.
CVE-2026-92948: node:test becomes the escape hatch
CVE-2026-92948 attacks NodeVM’s builtin allowlist.
On Node.js 24 and newer, node:test appears in Node’s builtin module inventory in a form that vm2’s dangerous-builtin filtering did not properly cover.
The resolver then creates an interesting double-prefix situation.
A sandbox request for:
node:node:test
can resolve to the configured node:test host module.
That means sandbox code receives access to the real host implementation.
The particularly nasty part is node:test.run().
The host implementation can spawn another Node process for process-isolated test execution and accepts attacker-controlled execArgv values.
That creates a route from sandboxed JavaScript to an unrestricted host Node process.
The bug affects vm2 versions in the 3.9.6–3.11.6 range according to the published advisory, with 3.11.7 listed as the fix.
CVE-2026-92934: AggregateError strikes again
vm2 has already had problems sanitizing host-side error objects.
CVE-2026-92934 shows why this class of bug is so difficult to eliminate.
The flaw involves AggregateError objects and the cycle-detection logic used by vm2’s handleException().
A specially constructed error structure can cause vm2 to revisit a host-wrapped object without applying the expected sanitization. That exposes a host proxy to sandbox code.
The result is another sandbox escape leading to host RCE.
The vulnerability affects vm2 before 3.11.8 and has a CVSS 4.0 score of 9.5.
This isn’t an ordinary parser bug.
It’s a failure in the machinery that decides which objects are safe to move across the VM boundary.
The bigger problem: this keeps happening
The current wave isn’t isolated.
The vm2 repository’s security page lists a long sequence of critical sandbox problems throughout 2026, including:
- host Promise rejection escapes
node:sqlitenative-code execution- dangerous builtin exposure
- prototype manipulation
- Promise species attacks
AggregateErrorsanitization bypasseschild_processexposure through builtin configuration- NodeVM nesting bypasses
The maintainers continue to publish fixes, but the sheer number of independent escape primitives is the story here.
One earlier example is GHSA-6w8r-xxw2-g3hx, where vm2 3.11.3–3.11.6 could expose Node’s node:sqlite to sandboxed code. A native SQLite extension supplied with the untrusted plugin could then be loaded into the host Node process, producing arbitrary native code execution. That issue was fixed in 3.11.7.
Another issue, GHSA-m5w8-4gq2-6f8x, allowed os and dns to remain reachable under builtin: ['*']. The latter was particularly ugly because dns.setServers() could modify DNS resolution for the entire host process.
This is not one broken function.
It’s an increasingly large attack surface surrounding the concept of “safe JavaScript.”
Why sandbox escapes are worse than normal RCE
With a normal remote-code-execution vulnerability, the attacker needs to reach a vulnerable server-side function.
With a sandbox escape, the application has often already decided to execute the attacker’s code.
The sandbox is supposed to be the second line of defense.
For example:
Untrusted JavaScript
↓
vm2
↓
isolated VM
↓
Node.js
↓
Host
The entire security model assumes the arrow from the VM back to the host does not exist.
A successful escape changes that to:
Untrusted JavaScript
↓
vm2
↓
sandbox escape
↓
host Function/process
↓
filesystem / network / secrets / child processes
At that point vm2 isn’t providing isolation.
It’s providing a false sense of isolation.
Who should care?
The obvious targets are platforms that deliberately execute untrusted JavaScript.
That includes:
- plugin systems
- workflow engines
- automation platforms
- server-side scripting systems
- multi-tenant developer platforms
- code execution services
- notebook infrastructure
- build systems
- applications executing user-provided JavaScript
If an application accepts JavaScript from an untrusted user and feeds it into vm2, the vm2 version becomes part of the application’s security boundary.
That makes these vulnerabilities substantially more important than a normal dependency update.
What to patch
The immediate action is to upgrade vm2 to the latest release containing the fixes.
The current security material identifies 3.11.8 as the patched version for the newest CVE-2026-92934 and CVE-2026-92955 issues.
Administrators should not assume that moving from one recent vm2 version to another automatically covers every historical escape.
The project has shipped multiple security releases during 2026, with different vulnerabilities fixed in 3.11.0, 3.11.4, 3.11.6, 3.11.7 and 3.11.8.
Check the actual installed dependency:
npm ls vm2
Then inspect the dependency tree.
A direct dependency is easy.
A transitive dependency hidden three levels down is where things get interesting.
If patching is impossible
Don’t pretend configuration is equivalent to a security patch.
Restrict which code can enter the sandbox.
Remove unnecessary Node builtins.
Avoid wildcard configurations such as:
builtin: ['*']
and review every application that exposes host objects or functions to sandboxed code.
The newest vulnerabilities demonstrate why.
One bug attacks Promise handling.
Another attacks builtin resolution.
Another attacks prototype access.
Another attacks exception sanitization.
There is no single magic configuration switch that fixes the entire class.
And if the workload genuinely requires strong isolation for hostile JavaScript, a process or container boundary is fundamentally different from trying to perfectly emulate one inside the same Node.js process.
Bugstoday’s take
vm2 isn’t suffering from one embarrassing bug.
It’s suffering from a recurring architectural problem: a JavaScript library is trying to convince hostile JavaScript that it lives somewhere it doesn’t.
Every new V8 optimization, Node builtin, Promise behavior, prototype edge case or error object becomes another place where that illusion can break.
The September 17 disclosures make the point brutally clear.
CVE-2026-92955 reaches CVSS 10.0. CVE-2026-92956 can hit the default new VM() configuration on Node.js 26. CVE-2026-92948 abuses a Node builtin. CVE-2026-92934 breaks error sanitization.
That’s not a patch Tuesday annoyance.
If vm2 is your security boundary, check the version today.
Today’s Bugs. Tomorrow’s Breaches.
Technical Sources
- GitHub — vm2 Security Advisories
- GitHub — GHSA-88hf-g992-jg85 / CVE-2026-92955
- GitHub — GHSA-27g9-p43v-cw3v / CVE-2026-92956
- GitHub — GHSA-6w8r-xxw2-g3hx
- CVE-2026-92934 / GHSA-x965-fc75-jpqh
- CVE-2026-92948
- vm2 GitHub repository and security documentation




