Skip to content

v3.11.8

Choose a tag to compare

@patriksimek patriksimek released this 27 Aug 19:49
· 10 commits to main since this release

Six advisories closed. Patch release — no API changes for valid configurations, with a few narrow observable behaviour changes (see Upgrade Notes).

What's Changed

Security fixes

  • GHSA-3vgf-8m4q-q4qr (dup: GHSA-59g5-pmg6-5gr4) — the default VM's protected-intrinsic inventory omitted the binary-data and iterator families (ArrayBuffer / SharedArrayBuffer / DataView / every TypedArray and %TypedArray%.prototype, plus the array/string/map/set/regexp-string iterator prototypes and %IteratorPrototype%). Because Buffer extends Uint8Array, a proto-walk from a host Buffer reached those unprotected host prototypes and polluted them globally — corrupting every host-realm typed array and iterator (host-state corruption).
  • GHSA-88hf-g992-jg85 — a NodeVM in its default console: 'inherit' configuration was escapable: sandbox code extracted the raw host Object.prototype.__proto__ getter (never classified dangerous like the setter), climbed console._stdout's host prototype chain to EventEmitter.prototype, overwrote emit, and had the host invoke it with this === process — full host RCE.
  • GHSA-f8gf-w286-fmq2allowAsync: false could be bypassed through Promise thenable assimilation: Promise.resolve / all / race / any / allSettled / try, new Promise(r => r(thenable)), withResolvers().resolve, Array.fromAsync, and the realm-intrinsic Promise base all let V8's PromiseResolveThenableJob run an attacker .then in a microtask after run() returned, outside the configured timeout.
  • GHSA-gjq8-xm47-88rc — an embedder-exposed host function (or a host builtin such as events.once) returning a rejected host Promise crashed the entire host process when sandbox code called it and ignored the result: the underlying host promise had no rejection reaction, so Node's default unhandledRejection policy tore the process down — a host DoS from one line of untrusted code.
  • GHSA-r273-hxvj-fxhp — NodeVM exposed host util as an unfiltered Object.assign({}, util), so util.getCallSites() (Node ≥ 22.9) handed the sandbox the host process call stack — absolute paths including vm2's own lib/ and the embedder entrypoint, node:internal frames, function names and line numbers — bypassing the GHSA-v27g host-frame redaction, which only covers sandbox-realm error stacks. getCallSite / setTraceSigInt / private internals rode the same wholesale copy; the sys alias leaked identically (information disclosure).
  • GHSA-x965-fc75-jpqh — patch bypass of GHSA-m283-3h24-438v: a host-wrapped AggregateError / SuppressedError revisited within a single handleException traversal (self-cycle, [shared, shared], mutual cycle) had its raw host proxy returned by the cycle memo and re-embedded into the rebuilt errors[], delivering a live host reference to sandbox catch code — host RCE on the exact channel Defense Invariant #3 promises to sanitize.

Documentation

  • docs/ATTACKS.md gains Categories 49 (revisited error-carrier cycle memo), 50 (host prototype-chain climb via the raw __proto__ getter), 51 (allowAsync: false thenable assimilation) and 52 (host util member auto-forwarding), plus extensions to Categories 20 (protected binary-data / iterator intrinsics) and 22 (ignored host-promise rejection), and matching rows in the "How The Bridge Defends" table.

Upgrade Notes

  • allowAsync: false is now strict about thenable assimilation. Under allowAsync: false, the assimilating Promise static methods (resolve / all / race / any / allSettled / try) and Array.fromAsync now throw in the sandbox, and the native Promise base is not constructable from sandbox code. This closes the microtask-after-run() bypass. allowAsync: true is completely unaffected — no valid async configuration changes.
  • The sandbox util builtin is now a vetted allowlist. Host-introspection / host-mutation members — util.getCallSites, util.getCallSite, util.setTraceSigInt, and private internals — are no longer forwarded to the sandbox (nor via the deprecated sys alias). Documented util members (format, inspect, promisify, callbackify, types, TextEncoder, parseArgs, …) are unchanged. A member Node adds in future no longer auto-enters the sandbox.
  • Raw host prototype readers are denied delivery. The sandbox can no longer obtain the raw host Object.prototype.__proto__ getter, Object.getPrototypeOf, or Reflect.getPrototypeOf to climb a host object's prototype chain. Legitimate Object.getPrototypeOf on a host proxy, and ordinary data/leaf property writes, are unchanged.
  • No other valid configurations are affected. The intrinsic-prototype protection (GHSA-3vgf), host-promise rejection handling (GHSA-gjq8), and error-sanitization (GHSA-x965) fixes are transparent to correct sandbox code.

Full Changelog: v3.11.7...v3.11.8