GitHub Advisoryadvisory · npm
GHSA-qhwx-74w5-xhxq vm2: NodeVM builtin allowlist bypass via node:test.run() execArgv allows sandbox escape
CRITICAL
Official page ↗
- Aliases
- CVE-2026-92948
- Ecosystem
- npm
- Packages
- vm2
- Severity
- Critical
- CVSS vector
- CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H
- Weaknesses
- CWE-693
- Reviewed
- 2026-10-01
Affected packages
• npm vm2: >= 3.9.6, < 3.11.7 → fixed in 3.11.7
Description
Summary
On Node.js 24 and newer, `vm2` can expose the host `node:test` module to sandboxed `NodeVM` code when the embedder explicitly allows the `node:test` builtin. Sandbox code can reach that module through `require('node:node:test')` and call `run()` with attacker-controlled `execArgv`.
`node:test.run()` starts a separate Node process for process-isolated test execution and forwards the supplied `execArgv` values to that process. Supplying `--eval=` therefore executes arbitrary JavaScript in an unrestricted host Node process, outside the `NodeVM` sandbox.
The PoC confirms that direct sandbox imports of `fs`, `child_process`, `module`, and `process` remain denied before the spawned process imports host `fs` and writes a harmless marker.
Affected versions and environment
• Package: `vm2`
• Affected versions: `>=3.9.6, vm.readonly(hostRequire(key)));
```
4. In `lib/setup-node-sandbox.js`, `requireImpl()` strips one `node:` prefix before builtin lookup:
```js
if (localStringPrototypeStartsWith(filename, 'node:')) {
id = localStringPrototypeSlice(filename, 5);
nmod = loadBuiltinModule(id);
}
```
5. Consequently, sandbox code requesting `node:node:test` is normalized to the stored key `node:test` and receives a readonly proxy to the host module.
6. The readonly proxy does not make `node:test.run()` safe. Calls are forwarded to the host implementation, which accepts attacker-controlled `execArgv` for a newly spawned Node process.
7. `--eval=` runs outside vm2 and has normal host builtin access.
The doubled prefix is the reachability mechanism, but the security boundary failure is broader: the generic host-passthrough loader treats the `test` builtin family as safe even though its `run()` API can launch unrestricted Node processes.
Proof of concept
From the `poc` directory:
```bash
npm ci --ignore-scripts --no-audit --no-fund
node repro.js
```
Expected successful result on Node.js 24+ includes:
```json
{
"vm2Version": "3.11.5",
"nodeVersion": "v24.18.0",
"markerExists": true,
"childIsDistinctProcess": true,
"marker": {
"hostCodeExecution": true
}
}
```
The PoC writes only `host-rce-marker.json` in its own directory and does not invoke a shell, contact a network service, or access third-party data.
Impact
An attacker who is intentionally permitted to execute untrusted JavaScript in the affected `NodeVM` configuration can escape the sandbox and execute arbitrary JavaScript under the embedder's operating-system identity.
This provides the spawned process with the host user's filesystem, environment, network, and process-execution permissions. It can therefore result in complete confidentiality, integrity, and availability impact for the hosting service.
Suggested remediation
Treat the normalized `test` builtin family as dangerous before wildcard expansion and explicit builtin registration.
For example, add `test` to `DANGEROUS_BUILTINS` so the existing prefix and family checks reject both `node:test` and `node:test/reporters`:
```js
const DANGEROUS_BUILTINS = new Set([
// existing entries
'test'
]);
```
If test helpers must be exposed, provide a sandbox-local wrapper through `mock` or `override` that does not expose `run()`, process isolation, `execArgv`, or other host process controls.
Recommended regression cases:
• explicit `builtin: ['node:test']`
• wildcard builtin configurations
• `require('node:test')`
• `require('node:node:test')`
• `node:test/reporters` and prefixed variants
• direct low-level builtin registration
• attempts to pass `--eval`, `--require`, or `--import` through test-runner process options
vm2-node-test-ghsa-submission.zip
Related
Alias · CVE (1)
- CVE-2026-92948 vm2 versions >= 3.9.6 and <= 3.11.6 are affected by a NodeVM builtin allowlist bypass that permits a sandbox escape on Node.js 24 and newer… vulnerability
Weakness · CWE (1)
- CWE-693 Protection Mechanism Failure weakness
References
- https://github.com/patriksimek/vm2/security/advisories/GHSA-qhwx-74w5-xhxq web
- https://nvd.nist.gov/vuln/detail/CVE-2026-92948 advisory
- https://github.com/patriksimek/vm2/commit/415339f698f0d52d3c5ad358b12b79c8072d5b4b web
- https://github.com/patriksimek/vm2 package
- https://github.com/patriksimek/vm2/releases/tag/v3.11.7 web
- https://www.vulncheck.com/advisories/vm2-3.9.6-through-3.11.5-sandbox-escape-via-node-test web