# vm2: NodeVM nesting guard accepts array-shaped require and permits host RCE

vm2 NodeVM's hasRealRequireConfig guard treats `require: []` as valid config, letting sandboxed code load host vm2 and spawn an unrestricted inner NodeVM for host RCE.

- Payout: Research
- Severity: critical
- Program: vm2
- Found by: lexdotdev
- Disclosed: 2026-10-01
- CVE: CVE-2026-92935
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-8hr7-r645-pc6w
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-8hr7-r645-pc6w
- Tags: area:oss domain:oss impact:rce kind:research platform:ghsa sev:critical tech:nodejs technique:incomplete-fix technique:source-review vuln:access-control vuln:rce vuln:sandbox-escape

**Target:** NodeVM sandbox constructor (npm vm2 library)
**Root cause:** `hasRealRequireConfig` uses `typeof requireOpts === 'object' && requireOpts !== null`, which is true for arrays, so `require: []` passes the guard meant to reject `nesting` without explicit require config. `makeResolverFromLegacyOptions()` then destructures the array to undefined fields and returns a resolver containing only NESTING_OVERRIDE.vm2.
**Steps:**
1. Host creates `new NodeVM({nesting: true, require: []})` and runs attacker JS.
2. Attacker code calls `require('vm2')` inside the sandbox, which the nesting loader resolves to the host VM constructors.
3. Attacker builds `new NodeVM({require: {builtin: ['child_process']}})` inside the sandbox, selecting its own builtin allowlist.
4. Inner VM runs `require('child_process').execSync('id')`, executing as the host process.
**How found:** Reporter noticed the shape check `typeof requireOpts === 'object' && requireOpts !== null` fails open for arrays, then traced `makeResolverFromLegacyOptions()` destructuring an array to undefined fields and merging NESTING_OVERRIDE, yielding a vm2-only resolver.
**Exploitation:** Requires a downstream app enabling `nesting` with array-shaped `require`. Attacker-supplied JS loads host vm2 via the nesting loader, creates an inner NodeVM with `builtin: ['child_process']`, and executes arbitrary host commands; outer builtin restrictions do not constrain the inner VM.
**Escalation:** Outer sandbox's builtin restrictions are bypassed by creating a fresh inner NodeVM whose require config is attacker-chosen inside the sandbox.
**Insight:** When a guard validates config by `typeof x === 'object'`, test array and other exotic shapes: destructuring an array yields undefined fields, silently producing a degenerate resolver that skips intended restrictions.

