Oct 2, 2026·9 min read·4 visits
A sandbox escape in vm2 (versions 3.11.3 to 3.11.6) allows untrusted code to access and hook the host process-wide http/https globalAgent. This enables unauthenticated attackers to steal sensitive headers (like Bearer tokens) and hijack active sockets during unrelated host-realm requests.
A critical vulnerability in the Node.js sandbox library vm2 allows sandboxed code to retrieve the process-wide http.globalAgent and https.globalAgent singletons. By attaching event listeners to these host-realm emitters, sandboxed code can intercept host-realm network requests, capturing sensitive Authorization headers and hijacking active TLSSocket streams.
CVE-2026-92940 describes a critical security vulnerability within the popular Node.js sandboxing library vm2 affecting versions 3.11.3 through 3.11.6. The core library is designed to isolate untrusted JavaScript execution from the host operating environment and runtime. However, when a NodeVM is configured to allow built-in network modules, a structural gap in how those modules are wrapped allows the sandbox to access sensitive host objects.
This vulnerability falls under the category of host-authority built-in members surviving the read-only wrap, cataloged as CWE-668 (Exposure of Resource to Wrong Sphere). When the sandbox loads either the http or https built-in Node.js modules, the loader wraps the module in a read-only proxy. This wrapping mechanism fails to intercept references to the process-wide globalAgent instance.
Because the globalAgent object is a shared thread-wide EventEmitter that manages the connection pool for all outgoing connections in the Node.js process, its exposure is critical. Any sandboxed code allowed to import the http or https modules can capture this singleton. Consequently, the sandboxed code can register event listeners to tap into connection events executed by the host process.
The resulting impact is a highly severe escape vector where untrusted sandboxed code intercepts plaintext communication and active transport-layer security (TLS) sockets. An attacker running code inside the sandbox can monitor all host-initiated HTTPS requests. This monitoring allows the collection of sensitive request headers, such as authentication tokens, cookies, and target destination details, without disrupting host operations.
The root cause of CVE-2026-92940 lies in the architectural design of vm2's bridge layer (lib/bridge.js) and builtin module loader (lib/builtin.js). To permit the restricted use of native Node.js core modules, vm2 uses a read-only wrapper function (vm.readonly()) that wraps host-realm objects before delivering them to the sandboxed runtime. This wrapper intercepts object property mutations (assignments) to prevent the sandbox from modifying host objects, but it allows property reads and method calls.
While property assignment is effectively blocked by the read-only proxy, invoking a method on the wrapped object bypasses assignment barriers because the method invocation is forwarded directly to the underlying host object. Because http.globalAgent and https.globalAgent are process-wide singletons inheriting from EventEmitter, calling methods like .on() or .addListener() directly registers callback functions in the host realm. These callbacks execute with full host authority when events are emitted.
When a host-realm HTTPS request completes, the connection socket is returned to the pool, triggering the 'free' event on the global agent. The globalAgent then fires the event and passes the active TLSSocket along with the original request options object to all registered listeners. Because the sandbox successfully attached its listener to the shared host singleton, it receives these arguments directly, crossing the security boundary without triggering proxy exceptions.
A compounding factor is the default behavior of parameterless or standard calls to https.request() or https.get() within the host environment. Under normal circumstances, if a request does not explicitly supply an agent parameter, Node.js implicitly defaults to using the shared https.globalAgent. This structural architecture ensures that almost all host-initiated outbound traffic utilizing default configurations is routed through the very singleton the sandbox can monitor.
Prior to the patch implemented in version 3.11.7, the builtin module registration logic in lib/builtin.js registered core modules directly using the readonly wrapper without member-level sanitization. The vulnerable path evaluated the module import as follows:
// Vulnerable path in lib/builtin.js prior to v3.11.7
builtins.set(key, special ? special : vm => vm.readonly(hostRequire(key)));This allowed the sandbox to retrieve the direct reference of hostRequire('https'), exposing the shared https.globalAgent without neutralization.
The remediation introduced in commit aa146a77f859325e079f3bfbfe6d8309af483daa implements member-level neutralization via a newly introduced sanitizeBuiltinMembers function. This function intercepts the http and https modules during loading and replaces the real, process-wide globalAgent with a fresh, sandboxed-dedicated Agent instance that has no exposure to host-realm activities.
// Neutralization mechanism in lib/builtin.js
function makeHttpAgentSanitizer(agentKey) {
return function sanitizeHttpModule(mod) {
if (typeof mod.Agent !== 'function' || !mod[agentKey]) return mod;
const copy = Object.assign({}, mod);
const sandboxAgent = new mod.Agent();
// The exposed globalAgent is replaced with a sandbox-dedicated one
copy[agentKey] = sandboxAgent;
// Overwrite request and get to prevent fallback to host globalAgent
const hostRequest = mod.request;
const hostGet = mod.get;
if (typeof hostRequest === 'function') {
copy.request = function request(...args) {
return hostRequest.apply(this, requestArgsWithDefaultAgent(args, sandboxAgent));
};
}
if (typeof hostGet === 'function') {
copy.get = function get(...args) {
return hostGet.apply(this, requestArgsWithDefaultAgent(args, sandboxAgent));
};
}
return copy;
};
}The patch also resolves a red-teaming bypass technique. If a sandboxed script makes an outbound connection using https.request() without specifying an agent, the returned request object (ClientRequest) normally references the host's globalAgent via req.agent. The patched wrapper intercepts request and get calls, wrapping the arguments using requestArgsWithDefaultAgent to explicitly inject the sandboxAgent. This ensures that even the request's internal agent pointer refers to the sandboxed agent rather than the host singleton.
Exploitation of this vulnerability requires that the target application runs the vm2 sandbox in a configuration that allows the native http or https built-in modules. If the NodeVM is initialized with { require: { builtin: ['https'] } }, the attack surface is exposed. No authentication or privileged network position is required within the sandbox to invoke the exploit; any execution path capable of running arbitrary JavaScript can execute the attack payload.
The attack sequence begins when the sandboxed script requires the https module and retrieves the https.globalAgent reference. The attacker registers an event listener for the 'free' event via https.globalAgent.on('free', callback). This function call executes through the read-only proxy directly onto the host's EventEmitter, successfully establishing a callback handler inside the host realm.
When any unrelated, high-privilege thread in the host process issues an outbound request (such as communicating with a payment processor, fetching configuration, or executing a backend API call), the request is processed using the host's shared globalAgent. Once the request finishes and the TLS socket is released back to the idle pool, the host emitter triggers the 'free' event, calling the sandbox's listener and passing the original request's options object along with the raw TLSSocket object.
Upon receiving these parameters, the sandboxed callback extracts the options object, which contains the target hostname, port, and request headers—frequently containing Authorization: Bearer <token> or session cookies. Additionally, because the active TLSSocket is passed, the attacker can register raw data stream listeners on the socket itself. This allows the attacker to read the incoming response payload in plaintext or construct arbitrary write commands on the connection stream.
The impact of CVE-2026-92940 is categorized as critical, receiving a CVSS v3.1 base score of 10.0 and a CVSS v4.0 base score of 10.0. The CVSS vector highlights an attack complexity of low, no privileges required, and a high impact across confidentiality, integrity, and availability. Crucially, the vulnerability features a 'Scope: Changed' (S:C) designation, reflecting the successful breach of the sandbox boundary to compromise the host runtime context.
In multi-tenant environments, such as Serverless Function-as-a-Service (FaaS) platforms, online code editors, or SaaS automation tools where untrusted user-supplied scripts are executed, this flaw represents an absolute compromise of tenant isolation. A malicious user can execute code that silently monitors host-process network traffic, effectively harvesting credentials and API keys belonging to other tenants or the host infrastructure itself.
The exposure is not limited to passive credential harvesting. Because the attacker gains direct access to the live TLSSocket objects used by the host application, the integrity of subsequent communications is entirely compromised. Attackers can hijack existing secure sockets, inject arbitrary commands, or manipulate outbound data streams, causing cascading security failures throughout connected internal APIs and third-party services.
Although the EPSS score indicates a relatively low probability of mass internet exploitation (0.47%), this is characteristic of development-focused library flaws rather than exposed edge-routing hardware. The risk remains acute and immediate for any enterprise architecture utilizing vm2 to process untrusted scripts, requiring immediate isolation or deprecation of the library.
The definitive remediation for CVE-2026-92940 is to upgrade the vm2 dependency to version 3.11.7 or later. This version introduces the module-level sanitization routines that completely disconnect the sandboxed http and https modules from the host's shared global agents. Security administrators should immediately audit all dependency trees and lockfiles (e.g., package-lock.json or yarn.lock) to verify the absence of vulnerable vm2 versions.
If immediate patching is not feasible, organizations must implement strict temporary workarounds. Developers should audit the configuration of the NodeVM instance and remove both 'http' and 'https' from the list of permitted built-in modules. If the sandbox does not require native network access, disabling these built-ins entirely eliminates the attack vector.
If outbound network requests are an absolute business requirement, teams should implement outbound request handling outside the sandbox boundary. Rather than permitting native networking inside vm2, the host application can expose a highly restricted custom API function to the sandbox that performs well-defined and strictly validated network calls. This design pattern completely removes the need to import native network modules inside the untrusted context.
Finally, it is crucial to recognize that the vm2 project has been officially discontinued and deprecated due to systemic design limitations in Node.js that prevent robust, single-process JavaScript sandboxing. Security teams must transition from vm2 to modern isolation solutions. Recommended alternatives include migrating to isolated-vm, which leverages native V8 Isolate boundaries, or running untrusted code in fully containerized micro-virtual machines (such as AWS Firecracker) with restricted network configurations.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:L| Product | Affected Versions | Fixed Version |
|---|---|---|
vm2 patriksimek | >= 3.11.3, <= 3.11.6 | 3.11.7 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-668: Exposure of Resource to Wrong Sphere |
| Attack Vector | Network (AV:N) |
| CVSS v3.1 Score | 10.0 (Critical) |
| EPSS Score | 0.00474 (Percentile: 38.70%) |
| Impact | Host Credential Disclosure & Transport Socket Hijacking |
| Exploit Status | Proof-of-Concept (PoC) available via official regression test suite |
| CISA KEV Status | Not Listed |
The product construction allows a resource to be exposed to an actor that is outside of the intended control boundary, resulting in information leakage or unauthorized operations.
CVE-2026-92958 is a high-severity sandbox escape and denylist bypass vulnerability within the NodeVM subsystem of the vm2 sandboxing library. When configuring wildcards with negative deny entries, exact-string matches fail to block subpaths like fs/promises. Sandboxed code can import these subpaths to bypass isolation and execute arbitrary filesystem operations on the host.
An incorrect authorization and directory traversal vulnerability in the vm2 library before version 3.11.7 allows remote attackers to bypass the sandbox's external package allowlist. This flaw permits sandboxed code to resolve and execute arbitrary packages available on the host filesystem under host privileges, leading to unauthenticated sandbox escape and host code execution.
CVE-2026-92950 (GHSA-jxxv-8r27-vm4p) is a critical sandbox escape vulnerability in the command-line interface of the vm2 library prior to version 3.11.7. The flaw allows untrusted scripts to bypass sandbox constraints and execute arbitrary system commands with the privileges of the host process by exploiting insecure default configurations of the module resolver.
CVE-2026-92948 is a critical sandbox escape vulnerability in the vm2 library affecting versions 3.9.6 through 3.11.6 when executed on Node.js 24 and newer. The vulnerability allows an attacker to bypass built-in module blocking defenses by double-prefixing a restricted module name (such as node:node:test). This permits the loading of the node:test module, whose test runner execution can be leveraged to execute arbitrary shell commands outside the VM sandbox.
A high-severity sandbox escape and state corruption vulnerability exists in the vm2 library (versions 3.11.4 through 3.11.6). The vulnerability is caused by an incomplete blocklist of Node.js-internal registered symbols crossing the sandbox-host bridge boundary. Specifically, the filters in `lib/setup-sandbox.js` and write traps in `lib/bridge.js` omitted the symbols `nodejs.stream.disturbed` and `nodejs.stream.errored`, allowing untrusted code within the sandbox to corrupt host-realm stream state checks.
An architectural evaluation of CVE-2026-73607 in the SiYuan personal knowledge management system. This technical advisory details a Missing Authorization (CWE-862) vulnerability in the Go-based backend kernel, specifically within the outline storage API endpoint. Under certain configurations, authenticated low-privilege users can query metadata, heading structures, and block hierarchies of restricted documents.