A critical sandbox escape vulnerability has been disclosed in the widely used Node.js sandboxing library vm2, tracked as CVE-2026-47210 (GHSA-6j2x-vhqr-qr7q), carrying a maximum CVSS v3.1 base score of 10.0 (Critical).
The flaw allows an attacker to break out of the VM2 sandbox and execute arbitrary code directly in the host process, a complete security boundary failure affecting any application relying on VM2 for untrusted code isolation.
VM2 is a widely adopted open-source Node.js library used to run untrusted JavaScript code in a secure, isolated sandbox environment.
Developers and enterprises use it to safely execute user-submitted or dynamically generated scripts without exposing the host runtime.
It is one of the most downloaded sandboxing solutions in the npm ecosystem, making any critical bypass in its security model a high-impact supply chain concern.
This is not VM2’s first encounter with sandbox escapes. Over the past year alone, the library has been hit by a cascade of critical vulnerabilities.
Including CVE-2026-22709 (CVSS 9.8) via Promise callback bypass, CVE-2026-26956 (CVSS 9.8) through WebAssembly JSTag manipulation, and a batch of twelve critical flaws disclosed in May 2026, with CVSS scores reaching a perfect 10.0. CVE-2026-47210 represents the latest and arguably most technically sophisticated entry in this growing list.
The vulnerability was discovered by security researcher RealHurrison and published by vm2 maintainer patriksimek via GitHub Security Advisory GHSA-6j2x-vhqr-qr7q.
It specifically targets Node.js 26 (node26), which exposes the experimental WebAssembly JavaScript Promise Integration (JSPI) API including WebAssembly.promising() and WebAssembly.Suspending().
JSPI allows WebAssembly functions to interoperate with JavaScript Promises asynchronously. The problem is that Promises created through WebAssembly.promising() do not behave like ordinary sandboxed Promises inside vm2.
When such a JSPI-backed Promise undergoes rejection, it produces a host-originated TypeError, an error object that originates from the host JavaScript realm, not the sandboxed realm.
Normally, vm2’s Promise-species hardening is supposed to ensure that Promise chains remain within the sandboxed localPromise machinery.
However, this hardening does not apply to the .finally() path. By contrast, the .then() and .catch() Handlers still correctly route through VM2’s expected local Promise machinery, making this a uniquely targeted gap.
The attack chain exploits this gap as follows:
- Attacker crafts a minimal WebAssembly binary and instantiates it with a
WebAssembly.Suspending-wrapped import that triggers a rejection. WebAssembly.promising()wraps the WASM export, producing a JSPI-backed Promise.- A custom class
Fis defined with an overriddenSymbol.speciesgetter returning itself. - The
constructorproperty of the JSPI-backed Promise is overridden to point to classF. - When
.finally()is called on the Promise, the rejection object a host-originated TypeError reaches the attacker’s species constructor logic. - Inside that constructor,
e.constructor.constructor(whereeis the host error) resolves to the host’sFunctionconstructor, not the sandboxed one. - The attacker calls
e.constructor.constructor('return process')()to obtain the hostprocessobject, then chains tochild_process.execSync()achieving arbitrary OS command execution.
The published Proof-of-Concept (PoC) demonstrates this by executing touch pwned in the host filesystem, confirming full sandbox escape on a node:26-bookworm environment.
CVE-2026-47210 receives the highest possible risk classification:
| Metric | Value |
|---|---|
| Attack Vector | Network |
| Attack Complexity | Low |
| Privileges Required | None |
| User Interaction | None |
| Confidentiality Impact | High |
| Integrity Impact | High |
| Availability Impact | High |
| CVSS v3.1 Score | 10.0 (Critical) |
The CWE classification is CWE-913 (Improper Control of Dynamically-Managed Code Resources). The product fails to properly restrict attacker-controlled code from accessing host-level execution resources.
The practical blast radius of this vulnerability is broad. Any application that accepts or processes untrusted JavaScript through vm2 as a security boundary is potentially fully compromised. Specifically at risk are:
- SaaS platforms offering user-defined scripting or automation using Node.js backend execution environments
- Code execution services, sandboxed REPL environments, and serverless function runners built on vm2
- CI/CD pipelines and plugin systems that sandbox third-party code using vm2
- Node.js applications running on Node 26, where JSPI is natively exposed
Successful exploitation can result in arbitrary command execution, unrestricted read/write access to the file system, theft of secrets, API tokens, credentials, and environment variables, and complete compromise of the host service.
Mitigation
The vulnerability affects vm2 versions ≤ 3.11.3 and has been patched in vm2 version 3.11.4. Security teams should take the following actions immediately:
- Upgrade to vm2 3.11.4 via
npm install vm2@3.11.4 - Verify the installed version using
npm ls vm2to detect transitive dependencies - Audit all indirect dependencies in your
package-lock.jsonoryarn.lockfor pinned older vm2 versions - Restrict or eliminate Node 26 + JSPI exposure in production environments where vm2 is still in use
- Consider migrating to alternative sandboxing solutions given vm2’s persistent history of critical escapes
Given the unpatched volume of prior VM2 CVEs in 2026 alone, organizations should evaluate whether continued reliance on VM2 is architecturally sound, regardless of this specific patch.
FAQ
Q1: What Node.js version is required to exploit CVE-2026-47210?
The exploit specifically targets Node.js 26 (node26), where the experimental WebAssembly JSPI API (WebAssembly.promising / WebAssembly.Suspending) is natively exposed.
Q2: Does upgrading to vm2 3.11.4 fully mitigate the JSPI .finally() sandbox escape?
Yes, vm2 3.11.4 is the official patched release; users on any version ≤ 3.11.3 remain vulnerable and should upgrade immediately.
Q3: Why doesn’t the same species bypass work through .then() or .catch()?
Unlike .finally(), the .then() and .catch() paths still correctly route through vm2’s localPromise machinery, which applies expected species hardening and prevents host-realm object exposure.
Q4: Is there a public Proof-of-Concept (PoC) available for CVE-2026-47210?
Yes, a working PoC is publicly available in the GitHub Security Advisory GHSA-6j2x-vhqr-qr7q, demonstrating OS command execution via child_process.execSync() on a node:26-bookworm environment.
Site: thecybrdef.com
For more insights and updates, follow us on Google News, Twitter, and LinkedIn.