Oct 2, 2026·6 min read·2 visits
A path traversal and module allowlist bypass flaw in vm2's legacy resolver allows sandboxed code to load un-allowlisted sibling packages that share a lexical prefix with allowlisted modules.
A module allowlist bypass vulnerability (CVE-2026-92945 / GHSA-7q3f-wx44-378m) was identified in the vm2 sandboxing library prior to version 3.11.7. This flaw permits unauthenticated or untrusted code running within the sandbox environment to bypass explicit module restrictions. When the transitive resolution option is disabled, the system fails to validate file system path boundaries, allowing prefix-sharing sibling directories to be resolved and loaded, thereby escaping intended sandbox restrictions.
The vm2 library is a widely utilized Node.js module designed to execute untrusted code in an isolated sandbox environment. It allows developers to restrict access to built-in modules and selectively whitelist specific external modules. This security boundary is essential for applications that execute user-defined scripts, such as extensibility platforms or serverless execution nodes.
A path validation vulnerability, tracked as CVE-2026-92945, exists in versions of vm2 prior to 3.11.7. This vulnerability permits an attacker executing code inside the sandbox to bypass module restriction rules. Under specific configurations, namely when a module is allowlisted with transitive dependency resolution disabled, the validation engine fails to enforce strict boundaries.
This failure allows sandboxed scripts to import un-allowlisted Node.js packages present on the filesystem if those packages share a lexical name prefix with an allowed module. The weakness is classified under CWE-22 (Improper Limitation of a Pathname to a Restricted Directory) and CWE-863 (Incorrect Authorization).
The core weakness is located within the legacy path resolution component of the library, specifically inside lib/resolver-compat.js in the LegacyResolver.isPathAllowedForModule(path, mod) method. This function evaluates whether a target import file path (path) is legally accessible from a currently loaded and authorized module context (mod).
When the sandboxing configurations designate an external module with transitive resolution disabled, vm2 attempts to block the loading of nested or untrusted dependencies. To accomplish this, the legacy resolver validates whether the file system path of the target import begins with the file system path of the authorized module. However, the validation checks prefix containment using character-level matches via the JavaScript String.prototype.startsWith() function.
Because the library evaluates the raw directory string without verifying segment boundaries, it fails to differentiate between subdirectories of an authorized package and distinct sibling directories that share a common naming prefix. For example, if the system allows imports from /app/node_modules/foo, the path /app/node_modules/foo2/index.js satisfies the prefix validation because the character sequence of the allowed path is a subset of the target path.
Furthermore, the secondary security check designed to block transitive modules relies on a regular expression /node_modules/ to identify nested dependency pathways. In prefix-bypass attacks, the remainder string extracted after slicing the allowed prefix (e.g., 2/index.js derived from /app/node_modules/foo2/index.js) does not contain the node_modules directory pattern. Consequently, the resolution is erroneously authenticated as a valid subpath of the allowlisted module, granting access to an unauthorized sibling package.
To understand the exact mechanics of the vulnerability, we can examine the implementation of LegacyResolver.isPathAllowedForModule in the vulnerable versions of vm2. The problematic code path uses an unsafe string-based comparison without confirming the directory separator boundaries.
// Vulnerable Implementation in lib/resolver-compat.js
if (path.startsWith(mod.path)) {
const rem = path.slice(mod.path.length);
if (!/(?:^|[\\/])node_modules(?:$|[\\/])/.test(rem)) return true;
}The patch implemented in version 3.11.7 fixes this logic by introducing strict path boundary validation checks. It requires that the candidate path is either a perfect match of the authorized module's path, or that a filesystem directory separator exists at the boundary marker, which isolates the directory context.
// Patched Implementation in lib/resolver-compat.js
const len = mod.path.length;
if (
path.startsWith(mod.path) &&
(path.length === len || (len > 0 && this.fs.isSeparator(mod.path[len - 1])) || this.fs.isSeparator(path[len]))
) {
const rem = path.slice(len);
if (!/(?:^|[\\\\/])node_modules(?:$|[\\\\/])/.test(rem)) return true;
}This updated logic employs three conditions to guarantee a secure match. The first condition path.length === len handles exact matches of the directory path. The second condition this.fs.isSeparator(mod.path[len - 1]) accommodates cases where the allowed module path string already ends with a trailing separator. The final check this.fs.isSeparator(path[len]) checks the character in the candidate path immediately following the prefix length, confirming that it is a valid directory delimiter.
Exploiting this vulnerability requires specific environment conditions. First, the host application must instantiate the sandbox with transitive dependency resolution disabled for specific allowlisted external packages. An attacker must also have the ability to supply sandboxed code that can execute inside this sandbox.
Furthermore, a prefix-sharing sibling package must exist on the local file system within the target's node module directory structure. For example, if the application has /app/node_modules/foo allowlisted, and a target dependency /app/node_modules/foo2 contains vulnerable code, this layout satisfies the physical requirements.
The attack is executed by invoking a relative import sequence through the authorized package. The sandboxed code invokes a mechanism in the allowlisted package to resolve a relative module path or directly attempts to trigger a loading sequence pointing to a parent sibling directory (e.g., ../foo2).
When the resolver processes the module loading command, the legacy path matching routine incorrectly identifies the target path as part of the allowlisted module foo. This permits the file /app/node_modules/foo2/index.js to execute within the sandbox environment, escaping the intended module allowlist restrictions and allowing execution of arbitrarily defined dependencies.
The security impact of CVE-2026-92945 is rated as Medium on the CVSS v3.1 scale (Base Score: 4.2), and Low on the CVSS v4.0 scale (Base Score: 2.3). The lower scoring is due to the complex attack requirements, which demand the existence of specific sibling packages on the local file system.
In environments where these prerequisites are satisfied, an attacker can escape module sandbox constraints. If a package with a similar name prefix exists on the local disk and exports sensitive functionalities, the sandboxed context can fully leverage those resources. This can result in unauthorized data exposure or escalation of privileges depending on what the sibling package can perform.
For serverless platforms or multi-tenant code-execution engines where dependencies are shared or can be populated by other users, this directory prefix matching bug poses a notable privilege escalation risk. By finding or installing a package with an intersecting prefix name, untrusted code can systematically access system resources.
The primary remediation path for this vulnerability is upgrading the vm2 dependency to version 3.11.7 or higher. This version contains the complete fix that integrates platform-independent filesystem separator checks, resolving the prefix-matching boundary flaw.
For environments where upgrading is not immediately feasible, developers can apply defensive configurations. One primary workaround is to avoid running the sandbox with transitive: false configurations unless path isolation is guaranteed. Alternatively, developers can restrict filesystem layout configurations to prevent sibling directories from sharing prefixes with critical allowlisted libraries.
Using static application security testing (SAST) tools can help identify if code relies on unsafe path parsing operations. Developers should avoid custom startsWith prefix logic for verifying authorization and instead utilize canonicalized paths and directory segment array comparisons to confirm directory containment.
CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:L/I:L/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
vm2 patriksimek | >= 0, < 3.11.7 | 3.11.7 |
| Attribute | Detail |
|---|---|
| Vulnerability ID | CVE-2026-92945 / GHSA-7q3f-wx44-378m |
| CWE ID | CWE-22 (Path Traversal), CWE-863 (Incorrect Authorization) |
| Attack Vector | Network (AV:N) |
| Attack Complexity | High (AC:H) |
| CVSS v3.1 Score | 4.2 (Medium) |
| EPSS Score | 0.00277 (18.25th percentile) |
| Exploit Status | Proof of Concept available |
| CISA KEV Status | Not listed |
The software uses external input to construct a pathname that should be within a restricted directory, but it does not properly neutralize elements within the pathname that can cause the pathname to resolve to a location outside of the restricted directory.
CVE-2026-92941 is a critical sandbox-escape and trust-manipulation vulnerability in the vm2 library (versions 3.11.3 to 3.11.6). This security flaw allows untrusted code executing within a NodeVM sandbox environment to compromise the global TLS trust store of the host Node.js process. By leveraging a design flaw where the host's native 'tls.setDefaultCACertificates' can be executed via a proxy wrapper, combined with a bridge unwrapping bypass in the 'url' module, an attacker can modify the process-wide default root Certificate Authorities. Consequently, all subsequent outbound TLS/HTTPS clients running on the host thread are forced to trust attacker-signed certificates, facilitating transparent Man-in-the-Middle (MitM) attacks. The vulnerability was resolved in version 3.11.7 of vm2 by introducing built-in member-level sanitization before applying read-only proxy wrappers.
A critical engine-level reachability failure in Node.js 26 running V8 14.6 allows attackers to escape the vm2 sandbox environment. When consecutive prototype properties are modified using sequential assignments, a V8 optimization bug fails to invalidate the PromiseThenLookupChain protector. By calling Promise.prototype.finally, the attacker bypasses the vm2 wrappers, hijacks the promise reaction using a custom constructor, triggers a calibrated stack overflow to capture a host-realm RangeError, and executes arbitrary shell commands on the host.
A critical sandbox escape vulnerability in the vm2 library allows sandboxed JavaScript code to bypass containment and execute arbitrary native code on the host process. This occurs when the host's builtin crypto module is exposed to the NodeVM environment. Although vm2 implements a read-only proxy layer to restrict direct modifications to host properties, it does not prevent invocation of host-level functions. By calling the crypto.setEngine API with a path to a malicious native library on disk, an attacker can trigger OpenSSL's dynamic module loader. The host's operating system loader immediately runs the dynamic library's initializers/constructors before verifying engine compatibility, leading to remote code execution in the context of the host process.
CVE-2026-92938 is a critical sandbox escape vulnerability in the vm2 library (versions 3.11.3 through 3.11.6) that allows arbitrary native code execution on the host when the node:sqlite built-in module is loaded inside a sandboxed NodeVM environment.
CVE-2026-92937 is a critical sandbox escape vulnerability in the `vm2` Node.js library. Due to a logical failure in checking direct invocation targets inside the Proxy bridge, an attacker can register Promise callbacks using `Function.prototype.call` or `Function.prototype.apply` indirection. This bypasses the error sanitization wrappers, delivering raw host error objects directly to sandboxed callbacks and allowing the attacker to escape the sandbox and execute arbitrary shell commands on the host.
CVE-2026-92935 is a critical sandbox escape and remote code execution vulnerability in the vm2 library. By supplying an array or exotic object to the require property of NodeVM while nesting is enabled, attackers can bypass security checks, load the host vm2 module, and run arbitrary shell commands on the hosting server.