CVE-2026-92946
vm2 before 3.11.7 Remote Code Execution via require.external
vm2 before 3.11.7 contains a remote code execution vulnerability when require.external is enabled without an explicit require.root that excludes node_modules. Sandboxed code can require vm2's own package, instantiate an unrestricted NodeVM instance, and execute arbitrary host OS commands via child_process.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- High
- Integrity
- High
- Availability
- High
Exploit likelihood
What each source reports
CVE List Score used
10.0
vm2 before 3.11.7 Remote Code Execution via require.external
vm2 before 3.11.7 contains a remote code execution vulnerability when require.external is enabled without an explicit require.root that excludes node_modules. Sandboxed code can require vm2's own package, instantiate an unrestricted NodeVM instance, and execute arbitrary host OS commands via child_process.
- Record
CVE-2026-92946- CVSS 4.0
- 10.0 Critical
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H - CVSS 3.1
- 10.0 Critical
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H - Weakness
- CWE-913
- Updated
- 2026-11-02
EUVDEUVD-2026-81595
10.0
vm2 before 3.11.7 contains a remote code execution vulnerability when require.external is enabled without an explicit require.root that excludes node_modules. Sandboxed code can require vm2's own package, instantiate an unrestricted NodeVM instance, and execute arbitrary host OS commands via child_process.
- Record
EUVD-2026-81595- CVSS 4.0
- 10.0 Critical
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-29x6-9qh5-83gg
10.0
Duplicate Advisory: NodeVM `require.external` without an explicit `require.root` grants unrestricted host filesystem access and full RCE
Duplicate Advisory
This advisory has been withdrawn because it is a duplicate of GHSA-j3hm-6rg5-mchv. This link is maintained to preserve external references.
Original Description
vm2 before 3.11.7 contains a remote code execution vulnerability when require.external is enabled without an explicit require.root that excludes node_modules. Sandboxed code can require vm2's own package, instantiate an unrestricted NodeVM instance, and execute arbitrary host OS commands via child_process.
- Record
GHSA-29x6-9qh5-83gg- CVSS 4.0
- 10.0 Critical
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/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 - CVSS 3.1
- 10.0 Critical
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H - Weakness
- CWE-913
- Updated
- 2026-11-02
GitHub AdvisoriesGHSA-j3hm-6rg5-mchv
10.0
vm2: NodeVM `require.external` without an explicit `require.root` grants unrestricted host filesystem access and full RCE
Summary
NodeVM's require.external option lets sandboxed code require() local files and npm packages. When require.external is enabled and require.root is not explicitly set to a path that excludes node_modules, two defaults combine to fully defeat the sandbox:
require.rootdefaults to unrestricted — "if omitted every path is allowed."require.contextdefaults to"host"— files loaded this way run through the real Node.jsrequire(), not inside any vm2 sandbox.
Sandboxed code can therefore require() a relative or absolute path to vm2's own installed package (node_modules/vm2), obtain the real, unwrapped NodeVM/VM classes, construct a brand-new unrestricted nested NodeVM instance, and execute arbitrary host OS commands via child_process.
This is exploitable using vm2's own documented "Quick Examples" configuration in README.md:
const vm = new NodeVM({
require: {
external: true,
root: './',
},
});
root: './' reads as a safety restriction but, in any ordinary npm project layout, ./node_modules/vm2 sits inside that same directory tree — so the restriction does not exclude vm2 itself. An application built by following the README's quick-start guide is affected by default.
Affected Versions
All vm2 versions where lib/resolver-compat.js's makeResolverFromLegacyOptions predates this report — confirmed present as of the current main (post-3.11.5, including all fixes through GHSA-8hg8-63c5-gwmx / Category 25 and GHSA-cp6g-6699-wx9c / Category 24). Neither of those prior fixes covers this code path (see Root Cause).
Details / Root Cause
lib/resolver-compat.js:
const {
builtin: builtinOpt,
mock: mockOpt,
external: externalOpt,
root: rootPaths,
resolve: customResolver,
customRequire: hostRequire = defaultRequire,
context = 'host', // <-- defaults to 'host'
strict = true,
fs: fsOpt = DEFAULT_FS,
} = options;
...
if (!externalOpt) return new Resolver(fsOpt, [], builtins);
...
let checkedRootPaths;
if (rootPaths !== undefined) {
// root is only canonicalized/validated if the embedder explicitly provided one.
...
}
and CustomResolver.isPathAllowed:
isPathAllowed(filename) {
if (this.rootPaths === undefined) return true; // <-- unrestricted when root is omitted
...
}
and CustomResolver.loadJS:
loadJS(vm, mod, filename) {
if (this.pathContext(filename, 'js') !== 'host') return super.loadJS(vm, mod, filename);
const m = this.hostRequire(filename); // <-- real host require(), when context === 'host' (the default)
mod.exports = vm.readonly(m);
}
CustomResolver (the resolver used whenever require.external is a bare true, i.e. not a scoped array/object) has no external-module-name allowlist at all — it gates purely on isPathAllowed, which is a no-op when root isn't set. Combined with context defaulting to 'host', any absolute or root-relative path sandboxed code names is loaded and executed via the real, unsandboxed Node.js require().
This is a distinct code path from the two already-fixed advisories that produce a similar end state:
- Category 24 / GHSA-cp6g-6699-wx9c (
require.rootsymlink bypass) assumesrootis configured and attacks the symlink boundary via a TOCTOU betweenpath.resolve()and the native loader's symlink-following.git log -- lib/resolver-compat.jsshows its fix (realpathcanonicalization) is the only history that file has — nothing from thenestingfix touches it, and the fix does nothing whenrootis never set in the first place, since there is no boundary to attack via symlink. - Category 25 / GHSA-8hg8-63c5-gwmx (
nesting: truebypass) closes a different delivery mechanism entirely — theNESTING_OVERRIDE-injectedvm2builtin, gated by a constructor-time check on thenestingoption. That check is never consulted by this path: an embedder with `nesting:…
- Record
GHSA-j3hm-6rg5-mchv- CVSS 3.1
- 10.0 Critical
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H - Weakness
- CWE-913
- Updated
- 2026-11-02
NVD
10.0
vm2 before 3.11.7 contains a remote code execution vulnerability when require.external is enabled without an explicit require.root that excludes node_modules. Sandboxed code can require vm2's own package, instantiate an unrestricted NodeVM instance, and execute arbitrary host OS commands via child_process.
- Record
CVE-2026-92946- CVSS 4.0
- 10.0 Critical
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/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 - CVSS 3.1
- 10.0 Critical
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H - Weakness
- CWE-913
- Updated
- 2026-11-02
OSV
10.0
vm2 before 3.11.7 Remote Code Execution via require.external
vm2 before 3.11.7 contains a remote code execution vulnerability when require.external is enabled without an explicit require.root that excludes node_modules. Sandboxed code can require vm2's own package, instantiate an unrestricted NodeVM instance, and execute arbitrary host OS commands via child_process.
- Record
CVE-2026-92946- CVSS 4.0
- 10.0 Critical
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H - Weakness
- CWE-913
- Updated
- 2026-11-02
Show 5 moreShow less
OSVGHSA-29x6-9qh5-83gg
10.0
Duplicate Advisory: NodeVM `require.external` without an explicit `require.root` grants unrestricted host filesystem access and full RCE
Duplicate Advisory
This advisory has been withdrawn because it is a duplicate of GHSA-j3hm-6rg5-mchv. This link is maintained to preserve external references.
Original Description
vm2 before 3.11.7 contains a remote code execution vulnerability when require.external is enabled without an explicit require.root that excludes node_modules. Sandboxed code can require vm2's own package, instantiate an unrestricted NodeVM instance, and execute arbitrary host OS commands via child_process.
- Record
GHSA-29x6-9qh5-83gg- CVSS 4.0
- 10.0 Critical
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/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 - CVSS 3.1
- 10.0 Critical
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H - Weakness
- CWE-913
- Updated
- 2026-11-02
OSVGHSA-j3hm-6rg5-mchv
10.0
vm2: NodeVM `require.external` without an explicit `require.root` grants unrestricted host filesystem access and full RCE
Summary
NodeVM's require.external option lets sandboxed code require() local files and npm packages. When require.external is enabled and require.root is not explicitly set to a path that excludes node_modules, two defaults combine to fully defeat the sandbox:
require.rootdefaults to unrestricted — "if omitted every path is allowed."require.contextdefaults to"host"— files loaded this way run through the real Node.jsrequire(), not inside any vm2 sandbox.
Sandboxed code can therefore require() a relative or absolute path to vm2's own installed package (node_modules/vm2), obtain the real, unwrapped NodeVM/VM classes, construct a brand-new unrestricted nested NodeVM instance, and execute arbitrary host OS commands via child_process.
This is exploitable using vm2's own documented "Quick Examples" configuration in README.md:
const vm = new NodeVM({
require: {
external: true,
root: './',
},
});
root: './' reads as a safety restriction but, in any ordinary npm project layout, ./node_modules/vm2 sits inside that same directory tree — so the restriction does not exclude vm2 itself. An application built by following the README's quick-start guide is affected by default.
Affected Versions
All vm2 versions where lib/resolver-compat.js's makeResolverFromLegacyOptions predates this report — confirmed present as of the current main (post-3.11.5, including all fixes through GHSA-8hg8-63c5-gwmx / Category 25 and GHSA-cp6g-6699-wx9c / Category 24). Neither of those prior fixes covers this code path (see Root Cause).
Details / Root Cause
lib/resolver-compat.js:
const {
builtin: builtinOpt,
mock: mockOpt,
external: externalOpt,
root: rootPaths,
resolve: customResolver,
customRequire: hostRequire = defaultRequire,
context = 'host', // <-- defaults to 'host'
strict = true,
fs: fsOpt = DEFAULT_FS,
} = options;
...
if (!externalOpt) return new Resolver(fsOpt, [], builtins);
...
let checkedRootPaths;
if (rootPaths !== undefined) {
// root is only canonicalized/validated if the embedder explicitly provided one.
...
}
and CustomResolver.isPathAllowed:
isPathAllowed(filename) {
if (this.rootPaths === undefined) return true; // <-- unrestricted when root is omitted
...
}
and CustomResolver.loadJS:
loadJS(vm, mod, filename) {
if (this.pathContext(filename, 'js') !== 'host') return super.loadJS(vm, mod, filename);
const m = this.hostRequire(filename); // <-- real host require(), when context === 'host' (the default)
mod.exports = vm.readonly(m);
}
CustomResolver (the resolver used whenever require.external is a bare true, i.e. not a scoped array/object) has no external-module-name allowlist at all — it gates purely on isPathAllowed, which is a no-op when root isn't set. Combined with context defaulting to 'host', any absolute or root-relative path sandboxed code names is loaded and executed via the real, unsandboxed Node.js require().
This is a distinct code path from the two already-fixed advisories that produce a similar end state:
- Category 24 / GHSA-cp6g-6699-wx9c (
require.rootsymlink bypass) assumesrootis configured and attacks the symlink boundary via a TOCTOU betweenpath.resolve()and the native loader's symlink-following.git log -- lib/resolver-compat.jsshows its fix (realpathcanonicalization) is the only history that file has — nothing from thenestingfix touches it, and the fix does nothing whenrootis never set in the first place, since there is no boundary to attack via symlink. - Category 25 / GHSA-8hg8-63c5-gwmx (
nesting: truebypass) closes a different delivery mechanism entirely — theNESTING_OVERRIDE-injectedvm2builtin, gated by a constructor-time check on thenestingoption. That check is never consulted by this path: an embedder with `nesting:…
- Record
GHSA-j3hm-6rg5-mchv- CVSS 3.1
- 10.0 Critical
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H - Weakness
- CWE-913
- Updated
- 2026-11-02
OSVCLSA-2026-1791278844 No score
TuxCare security update for vm2 (5 CVEs)
TuxCare rebuilt vm2 to address 5 CVEs. Fixed in 3.11.5-tuxcare.4.
- Record
CLSA-2026-1791278844- Updated
- 2026-11-02
OSVCLSA-2026-1791282867 No score
TuxCare security update for vm2 (3 CVEs)
TuxCare rebuilt vm2 to address 3 CVEs. Fixed in 3.10.0-tuxcare.4.
- Record
CLSA-2026-1791282867- Updated
- 2026-11-02
OSVROOT-APP-NPM-CVE-2026-92946 No score
CVE-2026-92946 in vm2 - Patched by Root
Root has patched CVE-2026-92946 in the vm2 package for Root:npm. Multiple fixed versions available.
- Record
ROOT-APP-NPM-CVE-2026-92946- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
vm2npm |
through 3.11.6 | 3.11.7 |
vm2TuxCare:npm From an advisory covering 5 CVEs |
3.11.5-tuxcare.0 up to 3.11.5-tuxcare.4, plus listed versions 3.11.53.11.5-tuxcare.13.11.5-tuxcare.23.11.5-tuxcare.3 |
3.11.5-tuxcare.4 |
vm2TuxCare:npm From an advisory covering 3 CVEs |
3.10.0-tuxcare.0 up to 3.10.0-tuxcare.4, plus listed versions 3.10.03.10.0-tuxcare.13.10.0-tuxcare.23.10.0-tuxcare.3 |
3.10.0-tuxcare.4 |
vm2npm |
before 3.11.7 | 3.11.7 |
vm2Root:npm |
before 3.11.6-aikido.1 | 3.11.6-aikido.1 |
@rootio/vm2Root:npm |
before 3.11.6-root.io.1 | 3.11.6-root.io.1 |
History
| Recorded | Changed | Sources |
|---|---|---|
| affected, facts | ||
| affected | ||
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-92946 is a critical vulnerability (CVSS 10.0) in Patriksimek Vm2, published September 17, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.78%, the 56th percentile. Fixed in vm2 3.11.7.
References
github.com/patriksimek/vm2/security/advisories/GHSA-j3hm-6rg5-mchv
vulncheck.com/advisories/vm2-before-3.11.7-remote-code-execution-via-require-external
nvd.nist.gov/vuln/detail/CVE-2026-92946
github.com/advisories/GHSA-29x6-9qh5-83gg
github.com/patriksimek/vm2/commit/903017c8a1eae9aba947ec854468b48155e79f86
github.com/patriksimek/vm2/releases/tag/v3.11.7
github.com/advisories/GHSA-j3hm-6rg5-mchv
github.com/CVEProject/cvelistV5/tree/main/cves/2026/92xxx/CVE-2026-92946.json
github.com/patriksimek/vm2
Left out here: the rest of 2 source descriptions. The JSON record has them.