{"id":"CVE-2026-92948","aliases":["GHSA-qhwx-74w5-xhxq"],"url":"https://o3.security/vulnerability/CVE-2026-92948","summary":"vm2 3.9.6 through 3.11.5 Sandbox Escape via node:test","details":"## Summary\n\nOn 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`.\n\n`node:test.run()` starts a separate Node process for process-isolated test execution and forwards the supplied `execArgv` values to that process. Supplying `--eval=<JavaScript>` therefore executes arbitrary JavaScript in an unrestricted host Node process, outside the `NodeVM` sandbox.\n\nThe 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.\n\n## Affected versions and environment\n\n- Package: `vm2`\n- Affected versions: `>=3.9.6, <=3.11.5`\n- Latest reproduced version: `3.11.5`\n- Reproduced runtime: Node.js `v24.18.0`\n- Exact path is not present on Node.js 22 because `module.builtinModules` does not expose the scheme-only `node:test` entry there\n- Configuration prerequisite:\n\n```js\nrequire: {\n  builtin: ['node:test'],\n  external: false\n}\n```\n\nThe lower version boundary was tested directly: `vm2@3.9.5` blocks `require('node:node:test')`, while `vm2@3.9.6` permits the exploit path. Representative releases through `3.11.5` were also reproduced.\n\n## Root cause\n\nThe issue is a combination of builtin admission, generic host passthrough, and prefix normalization:\n\n1. On Node.js 24+, `module.builtinModules` includes the scheme-only key `node:test`.\n2. `lib/builtin.js` builds `BUILTIN_MODULES` from that array. The family-based `DANGEROUS_BUILTINS` protection does not include `test`, so `node:test` remains eligible.\n3. When the embedder explicitly allows `node:test`, `addDefaultBuiltin()` stores it through the generic loader:\n\n```js\nbuiltins.set(key, special ? special : vm => vm.readonly(hostRequire(key)));\n```\n\n4. In `lib/setup-node-sandbox.js`, `requireImpl()` strips one `node:` prefix before builtin lookup:\n\n```js\nif (localStringPrototypeStartsWith(filename, 'node:')) {\n  id = localStringPrototypeSlice(filename, 5);\n  nmod = loadBuiltinModule(id);\n}\n```\n\n5. Consequently, sandbox code requesting `node:node:test` is normalized to the stored key `node:test` and receives a readonly proxy to the host module.\n6. 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.\n7. `--eval=<attacker JavaScript>` runs outside vm2 and has normal host builtin access.\n\nThe 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.\n\n## Proof of concept\n\nFrom the `poc` directory:\n\n```bash\nnpm ci --ignore-scripts --no-audit --no-fund\nnode repro.js\n```\n\nExpected successful result on Node.js 24+ includes:\n\n```json\n{\n  \"vm2Version\": \"3.11.5\",\n  \"nodeVersion\": \"v24.18.0\",\n  \"markerExists\": true,\n  \"childIsDistinctProcess\": true,\n  \"marker\": {\n    \"hostCodeExecution\": true\n  }\n}\n```\n\nThe 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.\n\n## Impact\n\nAn 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.\n\nThis 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.\n\n## Suggested remediation\n\nTreat the normalized `test` builtin family as dangerous before wildcard expansion and explicit builtin registration.\n\nFor example, add `test` to `DANGEROUS_BUILTINS` so the existing prefix and family checks reject both `node:test` and `node:test/reporters`:\n\n```js\nconst DANGEROUS_BUILTINS = new Set([\n  // existing entries\n  'test'\n]);\n```\n\nIf 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.\n\nRecommended regression cases:\n\n- explicit `builtin: ['node:test']`\n- wildcard builtin configurations\n- `require('node:test')`\n- `require('node:node:test')`\n- `node:test/reporters` and prefixed variants\n- direct low-level builtin registration\n- attempts to pass `--eval`, `--require`, or `--import` through test-runner process options\n[vm2-node-test-ghsa-submission.zip](https://github.com/user-attachments/files/29930119/vm2-node-test-ghsa-submission.zip)","published":"2026-09-17T13:46:08.814Z","modified":"2026-10-28T02:30:49.422028456Z","cvss":null,"epss":null,"cisaKev":null,"exploitsKnown":null,"affectedPackages":[{"ecosystem":"npm","name":"vm2","fixedVersion":"3.11.7"}],"fix":{"url":"https://github.com/patriksimek/vm2/commit/415339f698f0d52d3c5ad358b12b79c8072d5b4b","label":"patriksimek/vm2@415339f"},"references":[{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/92xxx/CVE-2026-92948.json"},{"type":"ADVISORY","url":"https://github.com/patriksimek/vm2/security/advisories/GHSA-qhwx-74w5-xhxq"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-92948"},{"type":"ADVISORY","url":"https://www.vulncheck.com/advisories/vm2-3.9.6-through-3.11.5-sandbox-escape-via-node-test"},{"type":"WEB","url":"https://github.com/patriksimek/vm2/commit/415339f698f0d52d3c5ad358b12b79c8072d5b4b"},{"type":"PACKAGE","url":"https://github.com/patriksimek/vm2"},{"type":"WEB","url":"https://github.com/patriksimek/vm2/releases/tag/v3.11.7"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-10-28T02:30:49.422028456Z"}}