Oct 2, 2026·7 min read·4 visits
A flaw in type-checking inside vm2 allows attackers to bypass sandbox nesting guards using array-shaped require options, gaining access to host Node.js modules and achieving full remote code execution.
CVE-2026-92935 is a critical sandbox escape and remote code execution vulnerability in the vm2 library. By supplying an array or exotic object to the require property of NodeVM while nesting is enabled, attackers can bypass security checks, load the host vm2 module, and run arbitrary shell commands on the hosting server.
The vm2 library is a widely used sandbox utility designed to run untrusted JavaScript code inside a secure Node.js environment. The NodeVM class provides simulated module-loading capabilities via a mock require function. To support advanced execution scenarios, NodeVM offers a nesting configuration option that exposes the parent host's vm2 instance to sandboxed contexts.
Exposing the host vm2 code to the sandbox is dangerous because host code has the authority to load core Node.js modules. If sandboxed code can access the host vm2 module, it can programmatically spawn a nested virtual machine with unrestricted capabilities. An attacker can use this secondary nested virtual machine to load core modules like child_process and execute arbitrary shell commands directly on the host operating system.
To prevent unauthorized sandbox escape, the developers previously introduced a nesting guard to verify that the require options were configured using a real resolver or valid configuration object. CVE-2026-92935 represents a critical design bypass of this mitigation. By providing exotic object types that spoof the configuration validator, an attacker can restore permissive access to the host virtual machine, leading to complete sandbox escape and remote code execution.
The root cause of this vulnerability lies in an incorrect type comparison check combined with JavaScript's permissive type evaluation logic. The original defense mechanism introduced to patch GHSA-m4wx-m65x-ghrr assessed whether the user-supplied requireOpts config was a valid object. It attempted to validate the configuration using the condition typeof requireOpts === 'object' && requireOpts !== null to confirm that a formal configuration structure had been established.
In JavaScript, however, the typeof operator evaluates to the string 'object' for several structures beyond standard, plain configuration objects. This includes arrays, dates, regular expressions, maps, sets, and boxed primitives. Consequently, when an application instantiates a NodeVM with require configured as an empty array [], the guard evaluates the setup as a valid, high-privilege configuration object.
Once the invalid array object passes this validation step, it propagates to the internal makeResolverFromLegacyOptions function. The function attempts to perform destructuring on the properties of the legacy configuration object, seeking variables like builtin, external, and mock. Because arrays do not possess these properties, the variables destructure to undefined, which defaults the environment to exposing NESTING_OVERRIDE containing the host vm2 module.
The patch resolved the bypass by defining a strict type validation helper called isPlainConfigObject in lib/resolver-compat.js. This predicate enforces that the configuration parameter must be a direct descendant of the base Object.prototype or a null-prototyped object. This design successfully filters out arrays, boxed objects, and foreign instances.
// PATCH ANALYSIS: lib/resolver-compat.js
const objectGetPrototypeOf = Object.getPrototypeOf;
const objectPrototype = Object.prototype;
const arrayIsArray = Array.isArray;
function isPlainConfigObject(value) {
if (value === null || typeof value !== 'object') return false;
// Array.isArray detects arrays and handles proxies around arrays securely
if (arrayIsArray(value)) return false;
// Validates that the prototype is either Object.prototype or null
const proto = objectGetPrototypeOf(value);
return proto === objectPrototype || proto === null;
}This validator is integrated into two critical points of the execution path to establish a defense-in-depth scheme. The first layer operates directly inside the NodeVM constructor within lib/nodevm.js to ensure the application throws an explicit VMError immediately during configuration parsing.
// PATCH ANALYSIS: lib/nodevm.js
// Old logic:
// const hasRealRequireConfig = requireOpts instanceof Resolver || (typeof requireOpts === 'object' && requireOpts !== null);
// New logic enforcing the strict predicate:
const hasRealRequireConfig =
requireOpts instanceof Resolver
|| isPlainConfigObject(requireOpts);
if (nesting && !hasRealRequireConfig) {
throw new VMError(
'NodeVM `nesting` requires an explicit `require` config object.'
);
}As a secondary safety layer, the internal resolver generator in lib/resolver-compat.js performs a subsequent evaluation. If the validator detects that the options object is not a plain configuration, it strips the privileged NESTING_OVERRIDE from the resolved options. This design guarantees that even if a future bypass occurs at the constructor validation step, the resolver fails closed and avoids exposing the host vm2 instance.
Exploitation of CVE-2026-92935 requires specific environmental conditions. The hosting application must initialize NodeVM with both nesting: true and an array-based or otherwise exotic require value, such as require: []. The attacker must also possess the capability to submit arbitrary JavaScript code to be executed within the context of the initialized outer virtual machine.
Once inside the vulnerable context, the attacker's payload executes within a sandbox where require('vm2') resolves directly to the host's vm2 implementation. The payload then instantiates an inner NodeVM using this imported host instance. This nested instance is constructed with an explicit configuration allowing the import of the host's native child_process library.
Because the nested instance is instantiated using the host's execution parameters rather than the outer sandbox parameters, it operates without the original restrictions. The payload executes the system command via child_process.execSync inside the nested runtime and returns the result back through the outer runtime. This process bypasses all sandbox boundaries, allowing the attacker to interact with the underlying host operating system.
// Conceptual Exploit Proof of Concept
const { NodeVM } = require('vm2');
// Vulnerable server-side setup hosting the sandbox
const outerSandbox = new NodeVM({
nesting: true,
require: []
});
// Attack payload designed to escape the outer sandbox
const payload = `
const { NodeVM } = require('vm2');
// Establish an unrestricted inner VM allowing 'child_process'
const innerSandbox = new NodeVM({
require: { builtin: ['child_process'] }
});
// Execute arbitrary commands on the host machine
module.exports = innerSandbox.run("module.exports = require('child_process').execSync('id').toString()");
`;
const output = outerSandbox.run(payload);
console.log(output);Successful exploitation of this vulnerability has severe consequences for the security of the host environment. Because sandboxes are generally deployed to execute untrusted user input safely, escaping the sandbox grants the attacker the exact system privileges of the running Node.js process. An attacker can read, modify, or delete files, establish reverse shells, and perform lateral movement across the network.
CVSS v4.0 evaluates this flaw at a base score of 9.5, reflecting a critical severity rating. The vector string highlights that the vulnerability can be exploited over the network without authentication, requires low operational complexity, and has a high impact on confidentiality, integrity, and availability. This profile makes it a high-priority target for systems processing untrusted scripts.
Furthermore, the vm2 library is officially deprecated and is no longer maintained by its authors. As a result, finding and resolving sandbox escape vectors in legacy installations of this library is critical, as no future official updates or patches will be produced to fix newly identified issues.
The primary remediation strategy is upgrading the library to version 3.11.7. This version includes the strict prototype validation checks that successfully prevent exotic objects from mimicking valid configuration files. This update blocks the bypass and restores the integrity of the nesting security boundary.
# Example dependency fix in package.json
"dependencies": {
"vm2": "^3.11.7"
}For systems where immediate updating is not viable, developers should implement input sanitation to intercept initialization arguments. Alternatively, applications can manually inspect configuration properties before instantiating the NodeVM object to reject arrays or custom objects. Incorporating runtime inspection can block attempts to load the vm2 module from within the sandboxed code.
Because the project has been deprecated, the long-term remediation recommendation is migrating to modern containerization or isolation technologies. Utilizing lightweight secure runtimes, virtual machines, or isolated execution frameworks like WebAssembly provides stronger hardware-level or operating-system-level isolation boundaries. These options are much less susceptible to prototype and type-evaluation bypasses than JavaScript-level sandboxing libraries.
CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H| Product | Affected Versions | Fixed Version |
|---|---|---|
vm2 patriksimek | >= 3.11.4, <= 3.11.6 | 3.11.7 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-697 (Incorrect Comparison) |
| Attack Vector | Network |
| CVSS v4.0 Score | 9.5 (Critical) |
| EPSS Score | 0.00673 |
| Impact | Sandbox Escape & Remote Code Execution |
| Exploit Status | Proof-of-Concept Available |
| KEV Status | Not Listed |
The application performs an incorrect comparison that permits unexpected types of input to bypass validation checks.
CVE-2026-92938 is a critical sandbox escape vulnerability in the vm2 library (versions 3.11.3 through 3.11.6) that allows arbitrary native code execution on the host when the node:sqlite built-in module is loaded inside a sandboxed NodeVM environment.
CVE-2026-92937 is a critical sandbox escape vulnerability in the `vm2` Node.js library. Due to a logical failure in checking direct invocation targets inside the Proxy bridge, an attacker can register Promise callbacks using `Function.prototype.call` or `Function.prototype.apply` indirection. This bypasses the error sanitization wrappers, delivering raw host error objects directly to sandboxed callbacks and allowing the attacker to escape the sandbox and execute arbitrary shell commands on the host.
CVE-2026-92949 is a sandbox bypass vulnerability in the vm2 library affecting versions 3.9.6 through 3.11.6. The flaw exists due to a breakdown in the ReadOnlyHandler proxy boundary, allowing sandboxed scripts to obtain direct references to wrapped property setters on frozen host-bound objects, ultimately leading to unauthorized state modification in the host environment. This security failure violates the read-only contract enforced by the sandbox for frozen/readonly objects, though it does not by itself allow a full execution-level realm escape. Due to systemic and structural design difficulties in securing a shared-runtime JavaScript sandbox, the vm2 library has been officially deprecated.
A vulnerability in the NodeVM component of the vm2 sandbox package through version 3.11.6 allows sandboxed code to bypass security policies restricting access to built-in modules. When a wildcard require policy is configured with negative deny entries using the 'node:' prefix (e.g., '-node:child_process'), the parser fails to recognize the exemption due to exact string comparison. As a result, the unmitigated module is registered, allowing sandboxed code to import the host child_process module and execute arbitrary system commands.
CVE-2026-92958 is a high-severity sandbox escape and denylist bypass vulnerability within the NodeVM subsystem of the vm2 sandboxing library. When configuring wildcards with negative deny entries, exact-string matches fail to block subpaths like fs/promises. Sandboxed code can import these subpaths to bypass isolation and execute arbitrary filesystem operations on the host.
An incorrect authorization and directory traversal vulnerability in the vm2 library before version 3.11.7 allows remote attackers to bypass the sandbox's external package allowlist. This flaw permits sandboxed code to resolve and execute arbitrary packages available on the host filesystem under host privileges, leading to unauthenticated sandbox escape and host code execution.