SECURITYCRITICALCVE-2026-100721

vm2 NodeVM External-Module Resolver Authorization Bypass (CVE-2026-100721)

vm2 before 3.12.2 lets sandboxed guest code escape via a prefix-match flaw in the NodeVM external-module resolver, enabling host code execution.

Dylan H.

Security Team

September 27, 2026
5 min read
vm2 NodeVM External-Module Resolver Authorization Bypass (CVE-2026-100721)

Critical severity

Rated critical. Prioritise patching — see the remediation guidance below.

Affected Products

  • vm2 before 3.12.2

Executive Summary

A critical authorization bypass (CVE-2026-100721) has been disclosed in vm2, the Node.js sandboxing library used by embedders to run untrusted JavaScript with restricted access to the host. The flaw sits in NodeVM's external-module resolver and allows sandboxed guest code to escape the sandbox and execute arbitrary code in the host process — even when the embedder explicitly allowlisted only a single, specific module.

CVSS Score: 9.0 (Critical) Fixed in: vm2 3.12.2

vm2 has a long history of sandbox-escape disclosures, and this CVE was published alongside several related vm2 advisories (CVE-2026-100722, CVE-2026-100723, CVE-2026-43999, CVE-2026-44007) in the same batch — underscoring that vm2's sandbox boundary should not be treated as sufficient for isolating genuinely untrusted code.


Vulnerability Overview

AttributeValue
CVE IDCVE-2026-100721
CVSS Score9.0 (Critical)
TypeIncorrect Authorization / Sandbox Escape
ComponentNodeVM external-module resolver (lib/resolver-compat.js)
Attack VectorLocal (sandboxed code execution context)
Privileges RequiredAbility to run guest code inside a NodeVM instance
User InteractionNone
Affected Versionsvm2 before 3.12.2
Fixed Versionvm2 3.12.2
AdvisoryGHSA-5h3f-q97h-ccvc

Technical Details

Root Cause

When an embedder configures require.external with a custom resolver (using context: 'host'), LegacyResolver.customResolve in lib/resolver-compat.js records the resolved module directory in this.externals as new RegExp('^' + escapeRegExp(resolvedPath)) — a prefix match with no path-separator or end-of-string boundary.

Attack Chain

1. Embedder allowlists a single external module, e.g. "foo", resolved
   to a path like /app/node_modules/foo
 
2. Guest code inside the NodeVM calls require("foo") — this is
   legitimately allowed and passes isPathAllowedForModule
 
3. Guest code then calls require() again, but targets the absolute
   path of a sibling directory that merely shares the allowlisted
   path as a string prefix, e.g. /app/node_modules/foo2/index.js
 
4. Because the allowlist check is a bare prefix match (no trailing
   separator required), "/app/node_modules/foo2" passes the same
   isPathAllowedForModule test as "/app/node_modules/foo"
 
5. The non-allowlisted sibling module is loaded through hostRequire()
   BEFORE its exports are wrapped with vm.readonly protections
 
6. The sibling's top-level code executes directly in the host
   process — full sandbox escape and arbitrary code execution

No special crafting of the target module's contents is required beyond it simply existing as a sibling directory — something an attacker with any ability to influence installed dependencies, or who already knows the host's node_modules layout, can arrange or exploit opportunistically.

Why CVSS 9.0?

A sandbox whose entire purpose is to contain untrusted code is defeated with a single crafted require() call, no additional primitives, and no user interaction — resulting in full code execution in the host context that the sandbox was meant to protect.


CVEIssueCVSS
CVE-2026-100721External-module resolver prefix-match bypass (this advisory)9.0
CVE-2026-100722Improper host-side Promise rejection handling6.8
CVE-2026-100723Buffer backing-store ownership leak via zlib.deflateSync7.5
CVE-2026-43999 (GHSA-947f-4v7f-x2v8)module builtin allowlist bypass exposes Module._load(), enabling RCE via child_process—
CVE-2026-44007 (GHSA-8hg8-63c5-gwmx)require('vm2') succeeds inside a nested NodeVM regardless of require: false, reaching child_process—

Applications relying on vm2 for isolation should treat this as a coordinated wave of sandbox-defeat techniques, not an isolated bug — patch to 3.12.2 to address all of the above.


Impact Assessment

Impact AreaDescription
Sandbox EscapeGuest code gains arbitrary code execution in the host Node.js process
Supply Chain RiskAny product using vm2 to run third-party plugins, user scripts, or untrusted templates is exposed
Data ExposureHost-level code execution grants access to environment variables, file system, and network from the sandboxed context
Chained RiskCombined with the related CVEs in this batch, multiple independent escape paths exist even in restrictive configurations

Recommendations

Immediate Actions

  1. Upgrade to vm2 3.12.2 or later immediately across all services embedding it.
  2. Audit require.external allowlist configurations — do not assume a directory-prefix allowlist is sufficient even after patching; validate with explicit path-boundary checks in your own embedding code where possible.
  3. Inventory every service using vm2 for sandboxing untrusted code (plugins, user-submitted scripts, template engines, CI/CD job runners).

Longer-Term

  • Reconsider vm2 for high-assurance isolation. Given the volume and severity of sandbox-escape CVEs disclosed against vm2, treat it as unsuitable for isolating genuinely hostile code; prefer OS-level isolation (containers, microVMs, separate processes with restricted privileges) for untrusted workloads.
  • Monitor for anomalous child-process spawning or unexpected file/network access originating from sandboxed execution contexts.

References