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 NodeVM versions before 3.12.1 contain a sandbox escape vulnerability where the DANGEROUSBUILTINS denylist omits childprocess despite blocking other host-spawning modules. Attackers can require childprocess and execute arbitrary commands on the host system when NodeVM is configured with builtin:[''] or explicit childprocess allowance. This fork hardens NodeVM with a DANGEROUSBUILTINS denylist that blocks host‑code‑reaching core modules even when the sandbox requests builtin:[''] or names them explicitly — the list contains module, workerthreads, cluster, vm, repl, inspector, process, traceevents, wasi, diagnosticschannel, asynchooks, perfhooks, v8, os, dns, and test. It omits childprocess — the single most direct command‑execution primitive. As a result, a sandbox running under require:{builtin:['']} (or the fork's own documented ['','-http','-net',…] subtract pattern) can require('childprocess').execSync(...) and execute arbitrary commands on the host. The omission is internally…
Recommended action: Upgrade to 3.12.1 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
At source revision 91034466bfb7f56b95fd48083ec6ca36d058f164 of vm2 3.11.8, an untrusted NodeVM guest can turn one allowlisted custom-resolved module into authorization for a separate file whose path merely shares the resolved path's string prefix. LegacyResolver.customResolve stores the resolved path in this.externals as ^<path without an end or separator boundary; a later absolute require of a sibling such as foo2/index.js therefore passes the external check and is loaded through hostRequire when the configured context is host. The decisive attack loaded foo as FOOOK and then executed the prefix-sharing sibling, which returned PREFIXPWN after invoking childprocess; an otherwise identical control denied the sibling with ENOTFOUND. Technical Details The affected configuration is a documented NodeVM use in which the embedder sets require.external to {modules: ['foo'], transitive: false}, supplies a custom resolver that returns the foo directory, sets a root directory, and uses…
Recommended action: Upgrade to 3.12.2 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
An untrusted script run by VM.run can construct an embedder-exposed host function that returns a rejected native Promise and ignore the result. The sandbox-to-host construct trap forwards that Promise without applying the host-side rejection handling already used by the neighboring apply trap, so Node's strict unhandled-rejection policy terminates the host process. Technical Details The precondition is an application-supplied constructable host function in the VM sandbox whose constructor body returns a native rejected Promise. In JavaScript, an object explicitly returned by a constructor replaces the newly allocated instance, so new HostReject() produces that Promise. VM.run executes the attacker-controlled source. For an ordinary host-function call, BaseHandler.apply invokes the host function, calls markHostPromiseHandled(ret), and then wraps the result. The adjacent BaseHandler.construct path instead calls Reflect.construct and returns thisFromOtherWithFactory(...) without…
Recommended action: Upgrade to 3.12.2 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
When an application explicitly exposes Node's zlib module through vm2's NodeVM builtin allowlist, an untrusted guest can obtain a pool-backed host Buffer from zlib.deflateSync, create a full-width view of its backing ArrayBuffer, read bytes outside the compressed result, and flip a byte in an unrelated host buffer. The pinned vm2 revision reproduces disclosure and host-memory modification, while a sandbox-local Buffer control remains exact-size and non-mutating. Technical Details NodeVM accepts require: { builtin: ['zlib'] }. The builtin resolver reaches addDefaultBuiltin in lib/builtin.js, where the host module is exposed through the generic readonly wrapper. zlib.deflateSync returns a Node Buffer; for a small result, that buffer can use Node's shared pool, whose .buffer is the complete pool rather than only the logical result slice. The guest can therefore call Buffer.from(result.buffer, 0, result.buffer.byteLength) and inspect or modify pooled bytes outside result. The…
Recommended action: Upgrade to 3.12.2 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
When allowAsync is set to false, vm2 is expected to reject attempts to run asynchronous code. Direct use of Promise.prototype.then is blocked, but Promise static methods still assimilate attacker-controlled thenables. Promise.resolve(thenable), Promise.all([thenable]), Promise.race([thenable]), Promise.any([thenable]), and Promise.allSettled([thenable]) can invoke the thenable's then method in a microtask after VM.run() or NodeVM.run() has already returned. This bypasses the documented async-execution restriction and runs outside the configured timeout, allowing sandboxed code to continue executing after the host believes execution is complete. Details The documented VM option says allowAsync: false should cause attempts to run async code to throw a VMError; README.md:139-145 also recommends using it with timeout. The implementation enforces part of this policy by replacing localPromise.prototype.then with an AsyncErrorHandler when async is disabled: -…
Recommended action: Upgrade to 3.11.8 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
It being possible to obtain the host proto getter/setter, has been used in many reports: - https://github.com/patriksimek/vm2/security/advisories/GHSA-vwrp-x96c-mhwq - https://github.com/patriksimek/vm2/security/advisories/GHSA-v6mx-mf47-r5wg - https://github.com/patriksimek/vm2/security/advisories/GHSA-grj5-jjm8-h35p - https://github.com/patriksimek/vm2/security/advisories/GHSA-47x8-96vw-5wg6 Yet it was never patched... --- This can, still, be used to escape the sandbox, one example (I'm sure there's other ways as well), is via console.stdout/console.stderr (NodeVM with console: 'inherit', which is the default) Details The prototype chain for console.stdout/console.stderr is: process is an EventEmitter, and nothing stops us from writing things to EventEmmiter.prototype By overwriting EventEmmiter.prototype.emit with a function, and making process emit an event (e.g. exit, unhandledRejection etc.), we can execute code with this being process. This also bypasses…
Recommended action: Upgrade to 3.11.8 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
vm2 current head (v3.11.5, commit 7a1f5100b96f48d34e0fe104ab37c0acc5944f92) can still be used to terminate the host Node.js process when sandbox code calls a host-realm function that returns a rejected host Promise and then ignores the returned value. This is an incomplete-fix variant of the GHSA-hw58-p9xv-2mjh unhandled rejection hardening. The localPromise constructor now catches and consumes sandbox-created unhandled rejections, but host Promises returned across the bridge are not marked handled at the bridge boundary. If the sandbox does not attach .catch() or .then(..., onRejected), Node's default unhandled rejection behavior terminates the host process. Technical Details lib/setup-sandbox.js hardens sandbox-created Promises by wrapping the executor and attaching a benign swallow tail: That only applies to localPromise instances created inside the sandbox. Host-returned Promises cross the membrane through the bridge apply path. The bridge wraps callbacks when sandbox…
Recommended action: Upgrade to 3.11.8 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
vm2's current host-intrinsic prototype protection is incomplete. The fix for GHSA-vwrp-x96c-mhwq blocks sandbox writes into classic host intrinsics such as Object.prototype, Array.prototype, and Function.prototype, but current head still lets sandbox code in a default VM reach and mutate host Uint8Array.prototype, %TypedArray%.prototype, and ArrayBuffer.prototype. After VM.run() returns, normal host typed-array and ArrayBuffer objects observe attacker-controlled properties and methods installed by the sandbox. Technical Details The existing mitigation relies on protectedHostObjects in lib/bridge.js. That set is populated from otherGlobalPrototypes, which is built from a fixed inventory of classic globals: The inventory omits typed-array and ArrayBuffer intrinsics. Sandbox code can still reuse the host-prototype walking primitive from the prior public advisory: Those objects are not sandbox-local. Inside the sandbox, hostUint8ArrayPrototype === Uint8Array.prototype,…
Recommended action: Upgrade to 3.11.8 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 (this fork's latest release) contains an incomplete-fix bypass of the Error.cause host-reference sanitization added in GHSA-m283-3h24-438v (commit 7e3faaf). Sandbox code that catches a host-wrapped AggregateError which is revisited within a single handleException traversal (self-cycle, mutual-cycle, or the same host aggregate referenced twice in errors[]) receives a live, unsanitized host proxy inside the "sanitized" errors[], yielding full host RCE on the throw channel that the fix and Defense Invariant #3 explicitly promise to sanitize. Root Cause handleException (lib/setup-sandbox.js) breaks recursion cycles at line 1819 with if (apply(localWeakMapGet, visited, [e])) return e; — returning the RAW host carrier on revisit. For plain-Error carriers this is safe because sanitizeErrorCause/sanitizeHostOwnProps seal the host object in place on first visit. But sanitizeAggregateError (~1954-1972) snapshot-and-rebuilds host-wrapped carriers into a fresh…
Recommended action: Upgrade to 3.11.8 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
NodeVM exposes the host util module to the sandbox through an unfiltered shallow copy (Object.assign({}, util)). On Node.js = 22.9 this hands sandboxed code util.getCallSites(), a programmatic stack-introspection API that returns the host process's full call stack — absolute file paths, function names, and line numbers — including vm2 bridge internals and the embedding application's entrypoint. This bypasses the host-frame redaction established in GHSA-v27g-jcqj-v8rw, which only covers the Error.prepareStackTrace channel. Details - Root cause — defaultBuiltinLoaderUtil copies every static member of the host util module and wraps the copy in vm.readonly() without filtering any member, so newly added Node APIs land in the sandbox automatically: https://github.com/patriksimek/vm2/blob/7a1f5100b96f48d34e0fe104ab37c0acc5944f92/lib/builtin.js#L25-L38 - Second equivalent channel — the deprecated sys builtin (an alias of host util) goes through the generic builtin loader…
Recommended action: Upgrade to 3.11.8 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.
Attacker-controlled code can trigger a host-realm syntax error and read its stack through the vm2 bridge. Host-realm stack formatting bypasses the sandbox-side redaction, so the returned value exposes absolute paths from vm2, Node.js internals, and the embedding application. Default VM and NodeVM configurations are affected without requiring special options. PoC A default-configured VM runs attacker-supplied code. Calling eval with deliberately malformed source makes the host-side source transformer throw a SyntaxError; reading .stack on the caught error exposes the host call stack to the sandbox. Observed output The stack string returned to the sandbox contains absolute host paths for lib/transformer.js, lib/vm.js, lib/setup-sandbox.js, Node internals, and the embedding application's own source file. Impact Sandboxed code running under the default new VM() or new NodeVM() configuration can read absolute filesystem paths of the embedding application's source tree plus host…
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.
Vulnerability Summary vm2's VM({ timeout }) option is documented and relied upon as the mechanism that bounds how long sandboxed code may execute. In the current implementation, the timeout only wraps the single synchronous call to VM#run() (via doWithTimeout → this.runScript(script) in lib/vm.js). It does not, and structurally cannot, bound code that the V8 engine itself schedules to run after that call has already returned. FinalizationRegistry and WeakRef are exposed to sandboxed code completely unmodified — they are not present anywhere in lib/setup-sandbox.js's list of specially-wrapped/hardened globals (only WeakMap, Promise, Proxy, Reflect, etc. receive hardening there). Sandboxed code can register a FinalizationRegistry callback against an object it creates and immediately drops. VM#run() returns normally, well within the configured timeout, because registration is instant. At some later point — determined entirely by the V8 garbage collector, and forceable on demand by the…
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.
NodeVM's require.external option lets sandboxed code require() local files and npm packages. When require.external is enabled and require.root is not explicitly set to a path that excludes nodemodules, two defaults combine to fully defeat the sandbox: - require.root defaults to unrestricted — "if omitted every path is allowed." - require.context defaults to "host" — files loaded this way run through the real Node.js require(), not inside any vm2 sandbox. Sandboxed code can therefore require() a relative or absolute path to vm2's own installed package (nodemodules/vm2), obtain the real, unwrapped NodeVM/VM classes, construct a brand-new unrestricted nested NodeVM instance, and execute arbitrary host OS commands via childprocess. This is exploitable using vm2's own documented "Quick Examples" configuration in README.md: root: './' reads as a safety restriction but, in any ordinary npm project layout, ./nodemodules/vm2 sits inside that same directory tree — so the restriction…
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.
Sandboxed code is able to disclose host memory used by small allocations by Buffer.from, Buffer.concat. Details vm2 exposes Buffer object to sandboxed code by default. Small Buffer allocations (for example, Buffer.allocUnsafe(), Buffer.from(array), Buffer.from(string), and Buffer.concat()) use the same Buffer pool, which is shared with the sandbox. This allows sandboxed code to disclose host memory used by the functions listed above. PoC Tested against vm2@3.11.5 in the node REPL. <img width="1044" height="104" alt="Screenshot 2026-07-23 at 17 54 15" src="https://github.com/user-attachments/assets/987b860c-6702-4818-bfaa-c77919c777e5" / Impact Since sandbox acquires an ArrayBuffer that is used by the host, it can disclose sensitive data going through functions mentioned above and even write to these buffers, which can lead to sensitive data exposure and potentially denial-of-service.
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.
There is a sandbox escape in vm2 3.11.5 / current HEAD when it is used on Node.js 26. The issue is reachable from a default new VM() sandbox. No NodeVM, require permission, host object injection, or intentionally unsafe configuration is required. The escape is a patch-bypass of the same security invariant addressed by GHSA-6j2x-vhqr-qr7q. The earlier fix removed the JSPI entry points WebAssembly.promising and WebAssembly.Suspending, because those APIs exposed a Promise path whose host-realm Promise.prototype was not intercepted by vm2's Promise hardening or bridge layer. The same unsafe class remains reachable through WebAssembly.compileStreaming and WebAssembly.instantiateStreaming. On Node 26, these streaming APIs can produce a raw host-realm Promise path that rejects with a host-realm error. By controlling Symbol.species through Promise.prototype.finally, sandbox code can receive that raw host error object, walk from the host error constructor to the host Function…
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 current head (v3.11.5, commit 7a1f5100b96f48d34e0fe104ab37c0acc5944f92) still exposes registered Node.js internal symbols from host WebStream prototypes to sandbox code. The prior nodejs. symbol hardening blocks Symbol.for('nodejs.<name') at the source, but the extraction filters and bridge write traps still enumerate a fixed set of known registered symbols. On Node.js v25.8.0, stream/web exposes two additional registered symbols: - nodejs.stream.disturbed - nodejs.stream.errored Sandbox code can extract those real host symbols with Object.getOwnPropertySymbols(streamWeb.ReadableStream.prototype) and then use them as write keys on host objects. On a real host ReadableStream, an attacker can make stream.Readable.isDisturbed(stream) return false after the stream has already been read. Technical Details lib/setup-sandbox.js correctly blocks future nodejs. keys at the Symbol.for() source: However, the extraction filters are still driven by a fixed realDangerousSymbols list.…
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.
On Node.js 24 and newer, vm2 can expose the host node:test module to sandboxed NodeVM code when the embedder explicitly allows the node:test builtin. Sandbox code can reach that module through require('node:node:test') and call run() with attacker-controlled execArgv. node:test.run() starts a separate Node process for process-isolated test execution and forwards the supplied execArgv values to that process. Supplying --eval=<JavaScript therefore executes arbitrary JavaScript in an unrestricted host Node process, outside the NodeVM sandbox. The PoC confirms that direct sandbox imports of fs, childprocess, module, and process remain denied before the spawned process imports host fs and writes a harmless marker. Affected versions and environment - Package: vm2 - Affected versions: =3.9.6, <=3.11.5 - Latest reproduced version: 3.11.5 - Reproduced runtime: Node.js v24.18.0 - Exact path is not present on Node.js 22 because module.builtinModules does not expose the scheme-only…
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 vm2 command-line tool installed by npm install -g vm2 and documented in the README's "CLI" section runs the supplied script under NodeVM with require:{external:true} and no root / context / builtin configured. With these defaults the resolver loads every relative or absolute require() target through the host require() function, executing the attacker's module body in the host Node.js process before the result is ever proxied back into the sandbox. A single attacker-controlled file passed to vm2 ./script.js can call require(filename) to re-execute itself in host realm and reach fs, childprocess, etc. The documented sandbox runner is therefore equivalent to node ./script.js. No additional files, flags, or user interaction are required. Details The vulnerability lets a malicious sandboxed script - the file argument to the documented vm2 <file CLI - execute arbitrary code in the host Node.js process, crossing the sandbox → host boundary that vm2 is meant to enforce.…
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 process's real https.globalAgent when a NodeVM is explicitly allowed to require https. The module is wrapped as read-only, but calls to methods on the shared agent still mutate the host object. Sandbox code can register a free listener and receive host request options and the host TLS socket whenever an unrelated host HTTPS request releases a pooled connection. In a contained test, sandbox code allowed only the https builtin: - read the host request's bearer token from the agent event; - attached a data listener to the released host TLS socket and read the next host response body in plaintext; - learned the private service host and port; - sent an attacker-chosen authenticated POST using the stolen host token; and - received confirmation that the service accepted the action. The host application never passed its credentials, request, response, socket, or destination into the sandbox. They crossed the boundary solely because vm2 exposes the…
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 is a sandbox library for isolating and executing untrusted JavaScript code inside a Node.js process. It can restrict access to built-in modules and external packages. When NodeVM enables an external allowlist together with a custom resolve callback, vm2 checks the requested package name with a non-exact match. For example, if the allowlist only permits left-pad, an attacker can still bypass the check with a colliding package name such as evil-left-pad, because it contains the allowlisted name. If the colliding package already exists in a host path resolvable by the custom resolver, or if the target application's custom resolver / dependency-management workflow downloads the package and places it in a resolvable path, vm2 loads and executes that package in the host context. This lets sandboxed code bypass the module allowlist and may further lead to host code execution. Details NodeVM supports require.external to configure which external npm packages sandboxed code may…
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.
NodeVM's builtin wildcard policy can allow sandboxed code to access fs/promises even when the embedder denies fs. With the following configuration: require('fs') and require('childprocess') are blocked, but require('fs/promises') and require('node:fs/promises') are still available. This allows sandboxed code to create and write files on the host filesystem through the promise-based filesystem API. Affected Mode NodeVM. Affected Configuration This affects configurations where users rely on negative builtin entries such as -fs to deny filesystem access while using the '' builtin wildcard. Affected Files / Functions - lib/builtin.js - DANGEROUSBUILTINS - BUILTINMODULES - makeBuiltinsFromLegacyOptions - addDefaultBuiltin - lib/resolver.js - Resolver.resolve - Resolver.loadBuiltinModule - lib/setup-node-sandbox.js - requireImpl Root Cause lib/builtin.js builds BUILTINMODULES from Node's builtin module list and filters dangerous/default-denied modules. In wildcard mode, negative…
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.
NodeVM normalizes node:-prefixed builtin specifiers during require() resolution, but it does not normalize user-provided negative builtin entries in wildcard policy. As a result, this configuration: does not deny the canonical childprocess builtin. Sandboxed code can require both childprocess and node:childprocess, and receives the host module with process-spawning APIs such as execSync and spawn. The safe proof below only checks module and function reachability. It does not execute any OS command. Affected Mode NodeVM. Affected Configuration This affects users who deny builtins using their node:-prefixed spelling, expecting -node:childprocess to deny require('node:childprocess') and require('childprocess'). Affected Files / Functions - lib/builtin.js - makeBuiltinsFromLegacyOptions - wildcard builtin expansion - exact negative entry check: builtins.indexOf(\-${name}\) - addDefaultBuiltin - lib/resolver.js - Resolver.resolve - lib/setup-node-sandbox.js - requireImpl - node:…
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.
Untrusted JavaScript running inside new VM().run() / new NodeVM().run() can bypass vm.freeze() / vm.readonly() and mutate a host object the embedder explicitly marked read-only - the documented contract is "prevent sandboxed scripts from adding, changing, or deleting properties". If the frozen host object has an accessor (get/set) own-property, the sandbox can read the host setter back out via Object.getOwnPropertyDescriptor() and call it directly; the call lands in BaseHandler.apply which unwraps the readonly proxy to the raw host object and runs the host setter against it. No non-default VM/NodeVM options are required; the only precondition is that the embedder froze an object whose shape includes an accessor property. A second route to the same sink exists via lookupSetter. PoC Observed output: The first line shows ReadOnlyHandler's documented traps work; the next two show the sandbox mutated the host-side level despite vm.freeze(). Impact A sandboxed script can mutate any…
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 NodeVM constructor computes hasRealRequireConfig using typeof requireOpts === 'object' && requireOpts !== null, so require: [] bypasses the guard intended to reject nesting without an explicit require configuration. makeResolverFromLegacyOptions() then destructures the array to undefined option fields and returns a resolver containing only NESTINGOVERRIDE.vm2. Any attacker whose JavaScript is executed by a downstream NodeVM configured with {nesting: true, require: []} can load the host vm2 module, create an inner NodeVM with an attacker-selected builtin allowlist, and execute commands as the host process. No equivalent plain-object validation exists in makeResolverFromLegacyOptions(). Array is converted into the vm2-only resolver: https://github.com/patriksimek/vm2/blob/54b54b74a382577f0bcd0538c5bf99acdcd7f53b/lib/resolver-compat.js#L205-L226 Nesting loader returns the host VM constructors:…
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.
Untrusted JavaScript run by vm2 can escape the sandbox and execute arbitrary commands in the host Node.js process when an embedder-exposed host Promise rejects. This is an incomplete fix for GHSA-m283-3h24-438v: the advisory's capability-bearing rejection rebuild runs only through this direct Promise-handler path, so call/apply indirection bypasses the protection it introduced. The bridge sanitises host rejection values before sandbox callbacks run, but the gate at lib/bridge.js:1624 identity-checks only the direct call target. Registering the rejection handler through Function.prototype.call indirection, p.then.call(p, undefined, cb), makes the intercepted target host Function.prototype.call, so the sanitiser never runs and the raw host error reaches the sandbox (lib/bridge.js:1639) without the rebuild that strips host references carried by its own properties (lib/setup-sandbox.js:2104). A rejection error whose own property references a powerful host object, for example…
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.