vm2 Sandbox Escape Hardening Found Incomplete: Host Promise Rejections Can Still Crash the Process (CVE-2026-92954)
What Happened
A newly published GitHub Security Advisory (GHSA-gjq8-xm47-88rc, CVE-2026-92954) reports that vm2, through current head (v3.11.5), remains vulnerable to a denial-of-service primitive despite a prior fix. According to the advisory, vm2's earlier hardening (GHSA-hw58-p9xv-2mjh) added a "swallow tail" to sandbox-created Promises so that unhandled rejections inside the sandbox never reach the host's unhandledRejection event. The researcher found that this hardening only covers sandbox-created Promises — not host-realm Promises that cross the bridge into the sandbox. If untrusted sandbox code calls a host function that returns a rejected Promise and never attaches .catch() to it, the original host Promise is left unhandled, and Node's default unhandled-rejection behavior terminates the entire host process. The advisory documents two reproduction paths: a generic case where an embedder exposes any Promise-returning host function, and a more specific case via vm2's NodeVM built-in events allowlist, using events.once() paired with an emitted error event. Reproduction is reported across Node.js v16 through v25.9.0.
Why It Matters
This affects any service embedding vm2 to run untrusted or third-party code — multi-tenant plugin systems, code-execution backends, queue/worker processes, or notebook runners — where the host exposes Promise-returning APIs to the sandbox, or where NodeVM's events builtin is allowed. Per the advisory, a single crafted call from sandboxed code can kill the worker process serving all tenants, and restart policies don't fully mitigate it since the same payload can be replayed after each restart, giving an attacker a cheap, repeatable availability primitive against a shared process. CVSS is reported at 8.6 with a public PoC already available, so this should be treated as readily exploitable where the preconditions apply.
What Defenders Should Do Now
- Inventory where vm2 (
VMorNodeVM) is embedded, and specifically whether host code exposes any function to the sandbox that can return a rejected Promise. - Check
NodeVMconfigurations forrequire.builtinallowlists that includeevents— per the advisory this builtin enables theevents.once()variant of the issue. - As an application-level workaround (not a library fix), register a process-level
unhandledRejectionhandler to prevent process termination from vm2-originated host Promise rejections, consistent with vm2's own README guidance for related async caveats. - From a monitoring angle, watch for unexpected worker/process restarts correlated with sandbox code execution, repeated crash-and-restart cycles on code-execution services, or spikes in process exit codes tied to
unhandledRejectionin application logs. - Longer term, treat vm2-based sandboxing as a weak isolation boundary for untrusted code; consider process-level or OS-level isolation (separate worker processes, containers, or a maintained alternative sandbox) for workloads that must run fully untrusted input.
Developing Intel
This is a same-day advisory and the details above are drawn directly from the GHSA writeup; there is no indication yet of in-the-wild exploitation or a shipped fix version. Treat this as actively developing — check back for an official patched release before assuming the mitigation above is a full substitute. Full technical details, PoC code, and the researcher's suggested fix direction are in the original advisory: GHSA-gjq8-xm47-88rc.