Patch Forecast
Yellow warning
Plan into this sprint.

CVE-2026-92952 · GHSA-jf8q-945g-9q4c

vm2: Incomplete nodejs.* symbol filtering lets sandbox override host WebStream state checks

Risk score
59/100
CVSS
6.8 v3.1
Exploit chance (30 days)
0.5%
Published
1 Oct 2026

Open in the live forecast Today's forecast

What to know

What it is
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 enu…
Affected
vm2 (npm) >= 3.11.4, <= 3.11.6
Fix
Upgrade vm2 to 3.11.7 or later.
Exploitation
Public proof of concept

Story map

Story map: vm2THE FLAWSTHE FIXCVE-2026-92952Incomplete nodejs.* symbolfiltering lets sandbox overridehost WebStream state checksCVSS 6.8 · EPSS 0%CVE-2026-92946NodeVM `require.external`without an explicit`require.root` grants…CVSS 10.0 · EPSS 1%CVE-2026-92944Vm2 sandbox escape on Node.js26 through a stalePromiseThenLookupChain…CVSS 9.8 · EPSS 1%CVE-2026-92956Vm2 sandbox escape viaWebAssembly.compileStreaming Promise species bypassCVSS 10.0 · EPSS 1%FixUpgrade vm2 to 3.11.7 or later.Who is exposedvm2 (npm) <= 3.11.6RED · ACT TODAYvm2NodeVM's require.externaloption lets sandboxed coderequire() local files and npmpackages. When require.externalis enabled and require.root is notexplicitly set to a path thatexcludes node_modules, two…Where it standsPublic proof of conceptCONNECTS TOSame weaknessCWE-669 (CWE-669)8 other warnings in 30 days →
Story map: the flaws, the fix, how it unfolded and what it connects to. Boxes link to the record or article.

At a glance

vm2: Red, act today. 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 node_modules, two defaults combine to fully defeat the sandbox: require.root defaults to unrestricted — "if omitted every path is …

The flaws

  • CVE-2026-92952: Incomplete nodejs.* symbol filtering lets sandbox override host WebStream state checks (CVSS 6.8 · EPSS 0%)
  • CVE-2026-92946: NodeVM `require.external` without an explicit `require.root` grants unrestricted host filesystem access and full RCE (CVSS 10.0 · EPSS 1%)
  • CVE-2026-92944: Vm2 sandbox escape on Node.js 26 through a stale PromiseThenLookupChain protector (CVSS 9.8 · EPSS 1%)
  • CVE-2026-92956: Vm2 sandbox escape via WebAssembly.compileStreaming Promise species bypass (CVSS 10.0 · EPSS 1%)

The fix

Fix: Upgrade vm2 to 3.11.7 or later.

Who is exposed: vm2 (npm) <= 3.11.6

Where it stands

  • Public proof of concept

Connects to

Why it is Yellow

  • CVSS 6.8 medium
  • Public exploit or proof of concept referenced
  • On your watchlist: npm

Affected packages

PackageEcosystemVulnerableFixed in
vm2npm>= 3.11.4, <= 3.11.63.11.7

References

Original description

## Summary 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: ```js if (apply(localStringStartsWith, keyStr, ['nodejs.'])) { ... return fresh; } ``` However, the extraction filters are still driven by a fixed `realDangerousSymbols` list. That list does not include `nodejs.stream.disturbed` or `nodejs.stream.errored`, so `Object.getOwnPropertySymbols()` and related paths can still return those real host symbols. `lib/bridge.js` has the same fixed-list problem in `isDangerousCrossRealmSymbol()` and in the host-result scrub list. Because the two new symbols are not recognized, the `set` and `defineProperty` traps allow sandbox-originated writes using those keys. Current-head source references: - `lib/setup-sandbox.js:180-196` denies `Symbol.for('nodejs.*')` by namespace. - `lib/setup-sandbox.js:214-230` uses a fixed `realDangerousSymbols` list for extraction filtering; the two reported symbols are absent. - `lib/bridge.js:187-199` uses a fixed `isDangerousCrossRealmSymbol()` list; the two reported symbols are absent. - `lib/bridge.js:1499-1509` treats the write trap as the last line of defense, but it only rejects keys recognized by that fixed list. ## Impact Sandbox code can corrupt host-visible WebStream state checks for host WebStream objects that cross into the sandbox. In the validated PoV, a stream that the host has already consumed is made to appear undisturbed to `stream.Readable.isDisturbed()`. This can bypass host logic that relies on Node's public stream-state helpers to enforce one-shot body consumption, reject errored streams, or decide whether a host WebStream is safe to hand to another component. This is not a host-code-execution primitive in the current PoV. The report is an incomplete-fix / guard-coverage gap in the same symbol-boundary family as the prior `nodejs.*` symbol advisory. ## Affected Package/Versions Confirmed affected on Node.js `v25.8.0`: - `v3.11.4` - `v3.11.5` - current head `7a1f5100b96f48d34e0fe104ab37c0acc5944f92` `v3.11.3` is also affected, but it predates the broader `nodejs.*` symbol fix. For this incomplete-fix report, the suggested affected range is `>= 3.11.4, <= 3.11.5` on Node.js versions where these stream symbols exist. No patched version is known. ## Configuration Required The PoV uses a `VM` where the embedder exposes a host WebStream object and the host `stream/web` module object to sandbox code: ```js const vm = new VM({ sandbox: { rs, streamWeb } }); ``` This matches the same trust boundary as the earlier cross-realm symbol-write class: sandbox code must not be able to obtain registered Node.js internal symbols and write them back onto host objects. The PoV is local-only. It does not require network access, a public target, `NodeVM` builtin access, `process`, filesystem access, or child-process access. ## Local Proof of Concept Run from the `oss-zero-day-harness` directory: ```sh node submission-bundle/vm2-pov-test-incomplete-nodejs-stream-symbol-filter/pov-nodejs-stream-symbol-incomplete-fix.js ``` The PoV: 1. The host creates a `ReadableStream`. 2. The host reads one chunk so `stream.Readable.isDisturbed(rs)` is `true`. 3. Sandbox code confirms `Symbol.for('nodejs.stream.disturbed')` is blocked and returns a sandbox-local symbol. 4. Sandbox code extracts the real registered `nodejs.stream.disturbed` / `nodejs.stream.errored` symbols from the host `ReadableStream.prototype`. 5. Sandbox code writes an own `nodejs.stream.disturbed` property onto the host stream with value `false`. 6. The host calls `stream.Readable.isDisturbed(rs)` again and receives `false`. Observed result on current head: ```json { "beforeHostDisturbed": true, "controls": { "symbolForDisturbedIsRegistered": false, "symbolForErroredIsRegistered": false }, "extracted": [ { "description": "nodejs.stream.disturbed", "keyFor": "nodejs.stream.disturbed" }, { "description": "nodejs.stream.errored", "keyFor": "nodejs.stream.errored" } ], "overrideDisturbedOk": true, "afterHostDisturbed": false } ``` ## Official Disclosure Policy Fit vm2's `SECURITY.md` asks reporters not to create a public issue and to submit It asks for reproduction steps, affected versions, environment/configuration details, and potential impact: - Policy: https://github.com/patriksimek/vm2/blob/main/SECURITY.md - Private report route: https://github.com/patriksimek/vm2/security/advisories/new This bundle is formatted for that private GitHub report flow and should not be posted publicly before maintainer triage and a fixed release. ## Suggested Fix Direction Make the dangerous-symbol checks namespace-based instead of list-based: - In `setup-sandbox.js`, make `isDangerousSymbol(sym)` return true for any registered symbol whose `Symbol.keyFor(sym)` starts with `nodejs.`. - In `bridge.js`, make `isDangerousCrossRealmSymbol(key)` do the same for any symbol key crossing the bridge. - In host-result scrubbing, delete all own symbol keys whose registered key starts with `nodejs.` instead of iterating a hard-coded list. - Keep the current explici

Last activity 1 Oct 2026. Patch Forecast updates every hour.