deniz.in

Markets

Weather

Loading weather

· via dev.to (home feed)

vm2 3.11.7 patches six sandbox escapes, two tied to specific Node.js lines

Six advisories from 1 October 2026 document separate sandbox escapes in vm2, all fixed in 3.11.7, with two reachable only on particular Node.js release lines.

vm2 3.11.7 patches six sandbox escapes, two tied to specific Node.js lines

Six security advisories published on 1 October 2026 describe separate ways for code inside a vm2 sandbox to reach the host Node.js process, and all six are resolved in vm2 3.11.7. According to a dev.to write-up, the affected version ranges differ between reports, and two of the flaws only become reachable on particular Node.js release lines, so patch level and runtime version need checking together.

Policy that was matched as text

Three of the failures come from vm2 comparing builtin policy entries as plain strings while Node.js resolves module names under its own rules.

CVE-2026-92957 concerns negative builtin entries written with a scheme prefix. An embedder configuring builtin: ['*', '-node:child_process'] intends to block process spawning, but as the dev.to report explains, the check in lib/builtin.js compares that entry against canonical names such as child_process. The prefixed form matches nothing, and require('child_process') inside the sandbox returns the real host module with execSync and spawn attached.

CVE-2026-92958 breaks the same policy surface from the other side. A configuration that denies fs and child_process does not deny fs/promises, because wildcard expansion handles the subpath as an unrelated name. Sandboxed code can then create and write files on the host through the promise-based filesystem API.

CVE-2026-92951 moves from builtins to packages. When a NodeVM combines an external allowlist with a custom resolve callback, the requested package name is matched non-exactly, so an allowlist holding only left-pad also admits evil-left-pad if a colliding package already sits in a resolvable host path.

A doubled prefix that spawns a process

CVE-2026-92948 affects Node.js 24 and newer. There, module.builtinModules includes the scheme-only key node:test; when an embedder allows it explicitly, vm2's generic loader stores a read-only proxy to the host module, and the internal require path strips a single node: prefix before lookup. A sandbox requesting node:node:test is normalised onto that stored key and receives the proxy. Calls to node:test.run() pass the caller's execArgv on to a newly spawned Node process, so --eval= executes JavaScript outside the sandbox. The advisory records that direct sandbox imports of fs, child_process, module and process stayed blocked throughout the demonstration.

State the wrapper cannot isolate

CVE-2026-92941 lets a sandbox replace the host thread's default certificate authorities. The tls builtin is exposed through a recursive read-only wrapper that stops ordinary property assignment, yet tls.setDefaultCACertificates() is not sandbox-local. The proof of concept builds a genuine host-realm array via URLSearchParams.getAll() from the permitted url module, because the native TLS function demands a host array rather than a mapped sandbox value. After the call, later host HTTPS clients accept a certificate signed by an attacker-controlled certificate authority. The API is present in Node.js 22.19.0 and later within 22.x, and 24.5.0 and later within 24.x.

CVE-2026-92944 is the runtime-dependent case, affecting vm2 on Node.js 26. V8's SetPrototypeProperties path there updates the existing Promise.prototype.then and catch data properties without invalidating the PromiseThenLookupChain protector. Promise.prototype.finally() trusts the stale protector, takes the InvokeThen fast path and calls the original native then, bypassing vm2's wrapper and its resetPromiseSpecies() hardening. An attacker-supplied species constructor provides the resolve and reject functions, and a deliberately induced stack overflow at that native reaction point yields a host-realm RangeError whose constructor chain reaches the host Function and process objects. The reporter tested vm2 3.10.2 through 3.11.5 on Node.js 26.7.0 with the same result each time, while Node.js 22.23.2, 24.19.0 and 25.9.0 were reported safe running stock vm2 3.11.5.

What operators should do

The immediate step is vm2 3.11.7 plus a check of the Node.js line in use, since CVE-2026-92944 needs Node.js 26 and CVE-2026-92948 needs Node.js 24 or newer. Where an upgrade cannot ship at once, the write-up suggests denying dangerous builtins under both canonical and scheme-prefixed names and treating fs, fs/promises, child_process, tls, module, process and node:test as one decision; avoiding custom resolve callbacks in front of an external allowlist and using exact package identifiers; and preferring process-level isolation for code from outside the trust boundary. It also argues that verification beats configuration review: a short probe attempting require('fs/promises'), require('child_process'), require('node:child_process') and a node:node:test lookup inside the sandbox shows whether policy is actually enforced.

Why it matters

vm2's core guarantee is that the embedding application controls which builtins and packages untrusted code can reach. Six independent demonstrations that this control can slip, through prefixed deny entries, unexpanded subpaths, a doubled scheme prefix, a non-exact package match, a TLS setter and a V8 fast path, show how fragile string-based policy is against a runtime that normalises names its own way. Five of the six share that root shape, per the write-up; the TLS case is the outlier, where the policy held but the allowed module still reached process-wide state. Anyone running untrusted JavaScript in-process on Node.js should treat 3.11.7 as mandatory, treat the runtime version as part of the threat model, and ask whether an in-process sandbox is the right boundary for code they do not trust.

  • #node-js
  • #security
  • #javascript
  • #sandbox-escape
  • #vm2

Related posts