ZeroPath at Black Hat USA 2026

vm2 CVE-2026-47209: Brief Summary of the Bridge Proxy Set Trap Receiver Bypass

A short review of CVE-2026-47209, a high severity sandbox escape in the vm2 Node.js library where the Bridge Proxy set trap ignores the receiver parameter, allowing sandbox code to write properties directly to host objects. Includes patch analysis and affected version details.

CVE Analysis

12 min read

ZeroPath CVE Analysis
ZeroPath CVE Analysis

2026-06-12

vm2 CVE-2026-47209: Brief Summary of the Bridge Proxy Set Trap Receiver Bypass
Experimental AI-Generated Content

This CVE analysis is an experimental publication that is completely AI-generated. The content may contain errors or inaccuracies and is subject to change as more information becomes available. We are continuously refining our process.

If you have feedback, questions, or notice any errors, please reach out to us.

[email protected]

Introduction

A flaw in how the vm2 Node.js sandbox library handles JavaScript's Proxy set trap allows untrusted sandbox code to write properties directly onto host objects, breaking the fundamental isolation guarantee that vm2 exists to provide. With over 56 million total npm downloads and roughly 1 million weekly downloads, vm2 is one of the most widely deployed JavaScript sandboxing libraries, used in code execution platforms, online IDEs, and increasingly in AI agent frameworks that evaluate LLM generated JavaScript.

vm2 is an open source advanced sandbox for Node.js, maintained by Patrik Simek, that lets embedders run untrusted JavaScript with whitelisted Node built in modules. The library has accumulated 4,100+ GitHub stars and 324 forks on GitHub. Its relevance extends beyond traditional sandboxing: AI agent frameworks rely on vm2 to safely execute code produced by large language models, making any sandbox escape a potential vector for converting prompt injections into host level remote code execution.

CVE-2026-47209 carries a CVSS v3.1 score of 8.6 (HIGH) with a network attack vector, no privileges required, no user interaction needed, and a scope change indicating that the impact crosses the sandbox boundary. The vulnerability affects all vm2 versions from 3.0.0 through 3.11.3 and was patched in version 3.11.4.

Technical Information

Root Cause: The Ignored receiver Parameter

The core defect resides in the BaseHandler.set trap within lib/bridge.js at line 1231. To understand why this matters, we need to look at how JavaScript's [[Set]] internal method interacts with Proxy objects.

Per ECMA-262 §9.5.9, when a property write walks up a prototype chain and reaches a Proxy, the engine passes four arguments into the set trap: target, property, value, and receiver. The receiver is the original object on which the property assignment was initiated. When receiver !== proxy (for example, when a child object inherits from the proxy via Object.create), the specification requires that the property assignment create an own property on the receiver, not on the proxy's underlying target.

The vm2 implementation completely ignored this receiver parameter. Instead, it unconditionally called otherReflectSet(object, key, value) against the host target object. This means that any property write on an object that prototypically inherits from a sandboxed proxy leaks through to the host side target, violating the sandbox boundary.

Classification: CWE-693 Protection Mechanism Failure

This vulnerability is classified under CWE-693, defined by MITRE as a weakness where "the product does not use or incorrectly uses a protection mechanism that provides sufficient defense against directed attacks." In this case, the receiver parameter handling in the set trap is an insufficient protection mechanism: the boundary check that should distinguish between writes to the proxy target versus writes to a child receiver is entirely absent.

Attack Flow

The exploitation path follows a clear sequence:

  1. Create a prototype inheriting child: Sandbox code creates a child object via Object.create(proxy), where proxy is a bridged reference to a host object.

  2. Assign a property on the child: The sandbox code writes a property on the child object. Per the Proxy specification, because the receiver (the child) is not the proxy itself, the write should create an own property on the child and never touch the host object.

  3. Property leaks to host target: Due to the bug, the set trap ignores the receiver and writes directly to the host target via otherReflectSet(object, key, value). The inherited property write leaks through to the host object.

The specific danger materializes when writing dangerous cross realm Symbol keys, such as nodejs.util.promisify.custom, to host objects. These Symbol keyed properties cause semantic confusion attacks where the host runtime interprets the injected property in a way that changes the behavior of built in Node.js APIs. This bypasses any future isDangerousCrossRealmSymbol guard that might be implemented on the direct set path, because the inherited write path does not go through that guard.

Per Aikido Intel, this Protection Mechanism Failure can be chained to execute arbitrary code in the host Node.js process, escalating from a boundary violation to full remote code execution.

CVSS Scoring Breakdown

CVSS Metricv3.1 ValueInterpretation
Attack VectorNetwork (AV:N)Exploitable remotely over the network
Attack ComplexityLow (AC:L)No specialized conditions required
Privileges RequiredNone (PR:N)No authentication needed
User InteractionNone (UI:N)No user action required
ScopeChanged (S:C)Impact crosses sandbox boundary
ConfidentialityNone (C:N)No direct information disclosure
IntegrityHigh (I:H)Full control over integrity of host objects
AvailabilityNone (A:N)No direct availability impact

A CVSS v4.0 score has also been assigned: CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N/E:U/U:Amber, where E:U confirms exploit status is "Unreported" as of the assessment date.

Patch Information

The vulnerability was patched by maintainer Patrik Simek in commit 26d0318, committed on May 17, 2026, and shipped in vm2 v3.11.4 (released May 18, 2026) under advisory GHSA-c4cf-2hgv-2qv6. The release was a pure security patch with no API changes.

How the Fix Works

The pre-fix BaseHandler.set in lib/bridge.js completely ignored the receiver argument and unconditionally forwarded every write to the host object via otherReflectSet(object, key, value). The fix introduces a receiver identity check that gates host object writes on the receiver being the canonical bridge proxy, and routes all non-canonical receivers to a safe install-on-receiver path.

The core code change adds 24 lines to BaseHandler.set in lib/bridge.js. Placed after the existing __proto__ and constructor-on-array short circuit checks but before the host write forwarding path, the new logic is:

// SECURITY (GHSA-c4cf-2hgv-2qv6): honour ECMA-262 9.5.9 [[Set]] // receiver semantics. const canonicalProxy = thisReflectApply(thisWeakMapGet, mappingOtherToThis, [object]); if (receiver !== canonicalProxy) { return thisReflectDefineProperty(receiver, key, { __proto__: null, value: value, writable: true, enumerable: true, configurable: true, }) === true; }

Here is how this works step by step:

  1. Canonical proxy lookup: The fix retrieves the bridge's own canonical sandbox facing proxy for the given host object from mappingOtherToThis, a WeakMap that vm2 already populates during proxy construction for both the !isHost (inner proxy) and isHost (outer proxy2) code paths. No new bridge state is needed.

  2. Identity gate: It then compares the trap's receiver to this canonical proxy. If receiver === canonicalProxy, we know the write came directly through the proxy (e.g., hostProxy.x = v or Reflect.set(hostProxy, k, v, hostProxy)), and the code falls through to the existing legitimate otherReflectSet forwarding path, preserving the embedder contract that direct writes to sandbox exposed host objects still work.

  3. Install on receiver fallback: When receiver !== canonicalProxy, meaning the write originated from an inheriting sandbox side child (e.g., Object.create(hostProxy).x = v) or a forged Reflect.set call with a custom receiver, the property is installed on the receiver itself via Reflect.defineProperty. Critically, the host object is never touched. The descriptor uses a null prototype (__proto__: null) to prevent prototype pollution of the property descriptor itself, and creates a standard writable/enumerable/configurable data property on the receiver.

Consistency Across Handler Types

This design mirrors how two other vm2 handlers already operated: ReadOnlyHandler.set uses the same install-on-receiver shape unconditionally (since its policy is "host is read only"), and ProtectedHandler.set does the same in its function value branch. ProtectedHandler.set also inherits this fix automatically through its super.set delegation for non-function values, so all three handler types are now consistent with the spec.

Test Coverage

The commit includes a comprehensive five variant test suite in test/ghsa/GHSA-c4cf-2hgv-2qv6/repro.js covering:

  • Symbol key inherited write via Object.create(hostFn)[Symbol.for(...)] = fn
  • Plain string key inherited write
  • Forged Reflect.set with an explicit non-proxy receiver
  • Deep prototype chain write (Object.create(Object.create(hostObj)))
  • Object.assign(Object.create(hostObj), src) bulk pollution
  • A negative control test confirming that direct proxy.x = v writes still correctly mutate the host object

The fix restores what the commit message calls "Defense Invariant #1" with a new corollary: a sandbox originated write reaches a host realm object only when the spec [[Set]] receiver equals the canonical bridge proxy for that object's target. All other write paths are safely confined to the sandbox side receiver.

Affected Systems and Versions

All vm2 versions from 3.0.0 through 3.11.3 are affected. The vulnerability is patched in version 3.11.4.

Version RangeStatus
< 3.0.0Not affected (predates the vulnerable code)
3.0.0 through 3.11.3Vulnerable
3.11.4 and laterPatched

The latest published version on npm is 3.11.5 as of the npm registry.

Any application that uses vm2 to execute untrusted or semi-trusted JavaScript is potentially affected. This includes code execution platforms, online IDEs, plugin sandboxing systems, and AI agent frameworks that evaluate LLM generated code.

Vendor Security History

vm2 has a well documented history of sandbox escape vulnerabilities. The following table summarizes known CVEs across the library's lifecycle:

CVE IDAffected VersionsPatched VersionSeverityDescription
CVE-2023-29017Prior to 3.9.153.9.15HighSandbox escape via Proxy handler exploitation
CVE-2023-30547Prior to 3.9.173.9.17CriticalException sanitization bypass
CVE-2023-37466Prior to 3.9.183.9.18HighInsufficient fix for prior CVEs
CVE-2026-22709<= 3.10.13.10.2CriticalPromise callback sanitization bypass enabling RCE
CVE-2026-24118< 3.11.03.11.0HighSandbox escape
CVE-2026-24120< 3.10.53.10.5HighInsufficient fix for CVE-2023-37466
CVE-2026-24781< 3.11.03.11.0CriticalSandbox breakout via inspect function
CVE-2026-26332< 3.11.03.11.0HighSandbox escape
CVE-2026-26956< 3.10.53.10.5CriticalSandbox escape via TypeError / Symbol to string conversion
CVE-2026-43997< 3.11.03.11.0HighSandbox escape
CVE-2026-43999< 3.11.03.11.0HighSandbox escape
CVE-2026-44005< 3.11.03.11.0HighSandbox escape
CVE-2026-44006< 3.11.03.11.0HighSandbox escape
CVE-2026-44007< 3.11.13.11.1HighSandbox escape
CVE-2026-44008< 3.11.23.11.2HighSandbox escape
CVE-2026-44009< 3.11.23.11.2HighSandbox escape
CVE-2026-47209< 3.11.43.11.48.6 (High)Bridge Proxy set trap ignores receiver parameter

At least 13 CVEs were disclosed in 2026 alone, revealing systemic weaknesses in vm2's Proxy based isolation model that cannot be addressed by individual patches. The project was deprecated between July 2023 and October 2025 due to critical security issues, and the maintainer has acknowledged that new bypasses will likely be discovered in the future. Endor Labs specifically recommends avoiding vm2 for attacker controlled code in production environments.

Kodem Security characterizes the 2026 wave of vm2 vulnerabilities as particularly concerning for AI agent frameworks, where sandbox escapes can transform prompt injections into host level RCE, a pattern Microsoft terms "Prompts Become Shells."

Behavioral Indicators of Compromise

For organizations monitoring for potential exploitation of vm2 sandbox escapes in general, Kodem Security identifies the following behavioral IOCs:

IOC CategoryIndicators
Processnode processes spawning child_process.exec or spawn from sandboxed contexts
Shell activityShell invocations (sh, bash, cmd.exe) parented to Node.js
Credential accessFilesystem reads of ~/.aws/credentials, ~/.npmrc, /proc/self/environ
NetworkUnexpected outbound DNS or HTTPS from agent runtimes

Strategic Recommendation

For organizations currently using vm2, the recommended path forward is dual track: apply the 3.11.4 patch immediately as a tactical measure, while strategically evaluating migration to alternative sandboxing approaches (such as process level isolation with Firecracker or gVisor) that do not share vm2's Proxy based architectural vulnerability pattern. Layering container level or process level isolation beneath vm2 provides defense in depth against the next inevitable bypass.

References

Detect & fix
what others miss

Works with
  • GitHub
  • GitLab
  • Bitbucket
  • Azure DevOps Services
  • Jira
  • Linear
  • Slack
  • Security Compass
Security magnifying glass visualization