Skip to content

Releases: patriksimek/vm2

v3.11.8

Choose a tag to compare

@patriksimek patriksimek released this 27 Aug 19:49

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

v3.11.7

Choose a tag to compare

@patriksimek patriksimek released this 24 Aug 22:16

Twenty advisories closed. Patch release — no API changes, but several observable behaviour changes; see Upgrade Notes.

What's Changed

Security fixes

  • GHSA-647f-g98j-qq25 — Patch bypass of the GHSA-m283-3h24-438v host-Promise rejection sanitizer: the apply-trap gate identity-checked only the direct apply target, so registering onRejected through Function.prototype.call / .apply indirection (including stacked and mixed forms) skipped the capability-stripping rebuild and delivered the raw host rejection to sandbox code (RCE).
  • GHSA-8hr7-r645-pc6w — Patch bypass of the GHSA-m4wx-m65x-ghrr nesting guard: the guard accepted any typeof requireOpts === 'object' value, so { nesting: true, require: [] } — and likewise Date / RegExp / Map / boxed primitives — destructured to all-undefined and produced a NESTING_OVERRIDE-only resolver exposing host vm2 with no restriction → nested NodeVM → child_process (RCE).
  • GHSA-27g9-p43v-cw3v — Sandbox escape on Node 26 through a stale PromiseThenLookupChain protector: installing the then / catch wrappers by plain assignment left the protector valid on V8 14.6, so Promise.prototype.finally took an internal fast path to the original native then and an attacker constructor[Symbol.species] survived to capture a native reaction — reachable with eval: false and wasm: false (RCE).
  • GHSA-wjwh-qqvp-g4p4WebAssembly.compileStreaming / instantiateStreaming returned Promises whose prototype chain reached the host realm, so vm2's sandbox-side Promise overrides never ran on them; an attacker species plus finally() delivered the raw host rejection into sandbox code (RCE).
  • GHSA-fcqc-726x-5wfcBuffer.from([0]).buffer handed the sandbox Node's shared 64 KB allocation pool, which backs unrelated host allocations — sandbox code could read host secrets out of neighbouring buffers and corrupt them in place (host memory disclosure and corruption).
  • GHSA-46pr-c5wc-xffxcrypto.setEngine(path) passed a sandbox-supplied path to OpenSSL's ENGINE loader, and the OS dynamic loader ran the named library's constructor as native code before OpenSSL validated the file — reachable from a NodeVM allowing only crypto (native RCE).
  • GHSA-6w8r-xxw2-g3hxnode:sqlite's DatabaseSync(':memory:', { allowExtension: true }).loadExtension(path) loaded a native SQLite extension into the host process, reachable from a NodeVM allowing only that builtin; require('node:node:sqlite') was additionally accepted as a second spelling (native RCE).
  • GHSA-98xx-8mx4-x7cmtls.setDefaultCACertificates() let sandbox code replace the host thread's process-wide default CA trust store, so subsequent host TLS clients accepted attacker-signed certificates; the required host array was forgeable through URLSearchParams.getAll() (host trust-store hijack).
  • GHSA-h85j-hv3c-qfgqhttp.globalAgent / https.globalAgent exposed the real shared host singleton, so a sandbox .on('free') listener received live host request options — including Authorization headers — and released sockets from unrelated host requests (host credential and traffic disclosure).
  • GHSA-qhwx-74w5-xhxqnode:test's run({ execArgv: ['--eval=<js>'] }) spawned a separate host Node process running attacker code with full host authority, from a sandbox with no filesystem or child_process of its own (RCE).
  • GHSA-8686-vhfx-7r3j — NodeVM's builtin: ['*', '-node:child_process'] deny token was a silent no-op: the wildcard expansion matched deny tokens by exact string, so the node:-prefixed spelling never matched the canonical name and the host module stayed exposed (RCE).
  • GHSA-6rh5-qq4q-97xh — Builtin deny tokens did not cover subpath siblings: builtin: ['*', '-fs'] removed only fs and left the full host fs/promises API exposed. The same gap affected every subpath family (-pathpath/posix, -streamstream/*, -timerstimers/promises).
  • GHSA-c48m-32m9-vx93 — External allowlist bypass with a custom require.resolve: the bare-specifier pre-check matched by substring, so external: ['left-pad'] also admitted evil-left-pad; anchoring that left a second route, since the permitted subpath tail accepted .. segments. Either way an un-allowlisted host package ran its top-level code in host context (RCE).
  • GHSA-7q3f-wx44-378m — External allowlist authorized requires with a raw startsWith test, so a prefix-sharing sibling package (.../node_modules/foo2 for allowlisted foo) loaded as if it were allowlisted. Scoped names (@scope/pkg vs @scope/pkg-evil) were affected identically.
  • GHSA-j3hm-6rg5-mchv (dup: GHSA-w9c4-gw9x-53mq) — Sandboxed code under require.external could require('vm2') from disk and construct an unrestricted nested NodeVM, defeating the guarantee that nesting is off by default and reaching child_process (RCE).
  • GHSA-jxxv-8r27-vm4p — The shipped CLI (npx vm2 ./script.js) ran the target with no effective boundary: NodeVM.file was constructed with no require.root and the default host context, so the script could require(__filename) — or any absolute path — and execute it in the host realm (RCE).
  • GHSA-jf8q-945g-9q4c — Incomplete nodejs.* symbol filtering let sandbox code extract real host nodejs.stream.disturbed / errored symbols and defineProperty them onto a host stream, flipping isDisturbed() / isErrored() host-side on an already-consumed stream (host state corruption).
  • GHSA-633r-hq9m-c4ffvm.freeze() / vm.readonly() bypass: a frozen host object's accessor set was still reachable via Object.getOwnPropertyDescriptor(...).set, __lookupSetter__, Reflect.getOwnPropertyDescriptor and getOwnPropertyDescriptors, letting sandbox code mutate state through a view the embedder declared read-only.
  • GHSA-r4fx-v8hh-22mvtimeout bypass via FinalizationRegistry cleanup callbacks, which run outside the timeout's reach and block the host event loop for an unbounded period (host availability).
  • GHSA-x6m4-chr9-cg97 — Patch bypass of the GHSA-v27g-jcqj-v8rw host-path redaction: a host-realm SyntaxError raised by the transformer had its stack formatted host-side, so the sandbox-realm redaction never ran and e.stack disclosed absolute host paths — vm2's own files, Node internals, and the embedding application's source (information disclosure).

Documentation

  • docs/ATTACKS.md extended with Categories 39–48, plus extensions to Categories 8, 21, 25 and 33 and matching rows in the "How The Bridge Defends" table.

Upgrade Notes

  • CLI usersnpx vm2 ./script.js now confines requires to the target script's own directory (root: dirname(script)) and loads them inside the sandbox (context: 'sandbox'). A script that previously reached host modules through the CLI will no longer do so; run it with node directly if that was the intent.
  • builtin: ['*'] users depending on node:test — the test family is now denied, including subpaths such as node:test/reporters, and is refused on explicit request. Its run({ execArgv }) spawns a host process, so it cannot be exposed safely; re-introduce a narrowed wrapper via require.mock / require.override if needed.
  • Builtin deny tokens are now stricter. -fs also denies fs/promises (and equivalently for every subpath family), and -node:x now matches the canonical x spelling. If you relied on a subpath surviving its family's deny token, list it explicitly.
  • FinalizationRegistry and WeakRef are no longer sandbox globals by default. Sandbox code referencing them now sees undefined. Re-expose via the sandbox option only if you accept that GC callbacks are not bounded by timeout.
  • WebAssembly.compileStreaming / instantiateStreaming are removed from the sandbox. Non-streaming WebAssembly.compile / instantiate / Module / Memory are unaffected.
  • Host error stacks reaching sandbox catch blocks now have host frames redacted. The error message is preserved; absolute host paths, Node internals and file: / wasm: frames are gone by design. Sandbox-realm frames still appear.
  • vm.freeze() / vm.readonly() no longer expose accessor setters through any descriptor-reading path. Getter reads are preserved; non-frozen host objects are unaffected.
  • require.external without require.root now emits a one-time warning. The breadth of that option is unchanged in this release — it still host-requires any named path — and tightening it to deny-by-default is a breaking change deferred to the next major. Set require.root, or context: 'sandbox', or both.
  • NodeVM({ nesting: <truthy> }) with an array-shaped or other non-config require now throws at construction, alongside the shapes already rejected by GHSA-m4wx-m65x-ghrr. Pass a plain config object (e.g. require: { builtin: [] }) or a Resolver.

Full Changelog: 3.11.6...v3.11.7

v3.11.6

Choose a tag to compare

@patriksimek patriksimek released this 14 Aug 20:08

Five advisories closed. Patch release — no API changes for valid configurations.

What's Changed

Security fixes

  • GHSA-cfcw-xp6x-25gj — Stacked-indirection bypass of the GHSA-v6mx-mf47-r5wg apply-trap peel. Two layers of Function.prototype.{call,apply,bind} pushed the host prototype mutator out of the inspected position; a later variant ran the __proto__ setter entirely host-side, defeating both identity checks (RCE).
  • GHSA-gmc2-2x9w-cgh9bufferAllocLimit bypass via Buffer.concat(list, totalLength) and Buffer.from(arrayLike), which reach the host allocator without traversing the sandbox-side wrapper — one call allocates past a configured cap (host memory-exhaustion DoS).
  • GHSA-m283-3h24-438v — Host errors leaked live host references into sandbox catch blocks via Error.cause, the SuppressedError / AggregateError slots, arbitrary own properties (err.detail = process), and the carrier's prototype chain. Any embedder function throwing an error that carried a host reference was an escape (RCE).
  • GHSA-m5w8-4gq2-6f8x — NodeVM builtin: ['*'] exposed os and dns, the last two process-wide builtins. Both leak host identity and network topology, and both write host state: dns.setServers() hijacks the host's DNS resolver, os.setPriority() renices the host process.
  • GHSA-v836-6xw4-9cx3bufferAllocLimit bypass via ArrayBuffer, SharedArrayBuffer, every TypedArray constructor, and WebAssembly.Memory, which hit the same timeout-immune V8 allocator uncapped — a ~200-byte payload becomes gigabytes of host RSS (host memory-exhaustion DoS).

Upgrade Notes

  • builtin: ['*'] users depending on os or dns — both are now denied, along with node:os, node:dns and dns/promises. Re-introduce a safe subset via require.mock / require.override.
  • Finite bufferAllocLimit users — the cap now also covers Buffer.concat / from / copyBytesFrom and the ArrayBuffer / SharedArrayBuffer / TypedArray / WebAssembly.Memory constructors. The default Infinity is unaffected.
  • Host errors now reach sandbox catch blocks as sandbox-realm errors. Primitive diagnostics are preserved and instanceof now works for subclasses; non-primitive properties and the host prototype chain are gone by design.

Full Changelog: v3.11.5...3.11.6

v3.11.5

Choose a tag to compare

@patriksimek patriksimek released this 18 May 15:35
7a1f510

What's Changed

Bug fixes

  • #566 — Restore util.inspect output on Node 26+. console.log(vm.run(...)) was rendering as Proxy(Proxy({})) / Proxy(Proxy([])) instead of the underlying value. Triggered by Node 26's stricter handling of nested proxies in the inspector.
  • #567 — Restore array iteration on vm.freeze()'d host arrays. Calling .map() / .filter() / .forEach() etc. inside the sandbox on a frozen host object containing arrays threw
    TypeError: 'isExtensible' on proxy: trap result does not reflect extensibility of proxy target. Regression from the 3.11.0 proxy-invariant hardening.
  • #568 — Fix .node extension handler key in lib/resolver.js (the key was ' .node' with a leading space, so native addon resolution silently fell through to the default path). Thanks to @cherr-cc.

Upgrade Notes

Drop-in replacement for 3.11.4. No API or configuration changes.

Full Changelog: v3.11.4...v3.11.5

v3.11.4

Choose a tag to compare

@patriksimek patriksimek released this 18 May 12:42

Ten advisories closed. Patch release — no API changes for valid configurations.

What's Changed

Security fixes

  • GHSA-c4cf-2hgv-2qv6 — Bridge set trap ignoring ECMA-262 §9.5.9 Receiver, letting Object.create(hostObj) children and Reflect.set(hostObj, k, v, custom) writes leak onto the host object (write-channel → RCE).
  • GHSA-m5q2-4fm3-vfqp — Cross-realm Symbol.for namespace leak + missing dangerous-symbol guards on the bridge's write traps (set / defineProperty / deleteProperty), enabling sandbox-installed nodejs.util.promisify.custom / stream brand / webstream hooks on host objects (RCE).
  • GHSA-v6mx-mf47-r5wg — Host prototype mutation via Function.prototype.{call,apply,bind} and Reflect.{apply,construct} indirection through Object.prototype.__proto__ setter, severing host intrinsic prototype chains and escaping via thisEnsureThis proto-walk fallthrough (RCE).
  • GHSA-q3fm-4wcw-g57x — Defense Invariant #11 violation in defaultSandboxPrepareStackTrace (second variant of GHSA-9qj6-qjgg-37qq in a different file): sandbox-installed Array.prototype[N] setter / Array.prototype.join override could observe bridge-internal stack-trace state.
  • GHSA-76w7-j9cq-rx2j — Promise species hijack in localPromise's swallow-tail, hijacking the downstream child constructor to capture V8's internal (resolve, reject) capability and reach a raw host-realm error → host Function (RCE).
  • GHSA-m4wx-m65x-ghrr — NodeVM constructor patch bypass of GHSA-8hg8-63c5-gwmx: any truthy nesting paired with a non-real-config require produced a NESTING_OVERRIDE-only resolver → inner NodeVM with attacker-chosen requirechild_process RCE.
  • GHSA-6j2x-vhqr-qr7q — WebAssembly JSPI (WebAssembly.promising / WebAssembly.Suspending, Node 24+ behind a flag, Node 26+ default) producing Promise objects with a host-realm [[Prototype]] chain and no bridge interposition; species hijack delivers a raw host-realm rejection to sandbox .catch → host Function (RCE).
  • GHSA-rp36-8xq3-r6c4 — NodeVM builtin denylist bypass via process (whose getBuiltinModule(name) reloads any core module regardless of allow/deny config) and inspector/promises (whose Session().post('Runtime.evaluate', ...) evaluates attacker JS in the host realm). Supersedes GHSA-947f-4v7f-x2v8.
  • GHSA-r9pm-gxmw-wv6p — NodeVM builtin: ['*'] wildcard exposing Node's undocumented underscored network builtins (_http_client, _http_server, _tls_*, _stream_*) even when the documented -http/-https/-net/-tls exclusions were used — SSRF-class capability bypass (CVSS 8.6).
  • GHSA-9g8x-92q2-p28f — NodeVM builtin allowlist surfacing four process-wide observability builtins (diagnostics_channel, async_hooks, perf_hooks, v8) that read state of the entire host process rather than sandbox-local state — HTTP header / async-context / perf-mark / heap-snapshot exfiltration.

Documentation

  • docs/ATTACKS.md extended through Category 35, plus two new Defense Invariants: #12 ("No sandbox-visible object has a host-realm prototype chain without bridge interposition") and #13 ("The NodeVM builtin allowlist is a closed system").

Upgrade Notes

  • If you constructed NodeVM({ nesting: <truthy> }) without an explicit require config object, new NodeVM(...) now throws (GHSA-m4wx-m65x-ghrr). This covers every shape that previously silently produced a vm2-only resolver: omitting require, or setting it to any falsy value (false/undefined/null/0/'') or any truthy non-object value (true/number/string/symbol/function); and also any truthy nesting value, not only nesting: true. Either drop nesting, or pass an explicit require config object (e.g. require: { builtin: [] }) to acknowledge that vm2 will be requireable from inside the sandbox. The error message is actionable and links to the README hardening section.
  • No other valid configurations are affected. Embedders who explicitly listed any of process / inspector / worker_threads / cluster / vm / repl / module / trace_events / wasi / diagnostics_channel / async_hooks / perf_hooks / v8 in builtin were already running an unsandboxed sandbox; those names now throw at load time and can be re-introduced as safe wrappers via mock / override / SPECIAL_MODULES.

Full Changelog: v3.11.3...v3.11.4

v3.11.3

Choose a tag to compare

@patriksimek patriksimek released this 11 May 02:17

What's Changed

Security fix

Documentation

Full Changelog: v3.11.2...v3.11.3

v3.11.2

Choose a tag to compare

@patriksimek patriksimek released this 03 May 21:33

What's Changed

Security fixes

  • GHSA-9vg3-4rfj-wgcm — Sandbox-realm null-proto write-through via bridge.from() set trap (RCE)
  • GHSA-2cm2-m3w5-gp2f — Internal state reachable via computed-key access on globalThis
  • GHSA-9qj6-qjgg-37qq — Bridge saved-state leak via sandbox-installed Array.prototype[N] setter (RCE)

Documentation

  • docs/ATTACKS.md updated through Category 28, plus a new Defense Invariant
    ("Bridge-internal containers must not invoke sandbox code").

Full Changelog: v3.11.1...v3.11.2

v3.11.1

Choose a tag to compare

@patriksimek patriksimek released this 01 May 21:21

Single advisory closed plus prominent documentation of an existing escape hatch. Patch release — no API changes for valid configurations.

Embedders running untrusted code with nesting: true should read the new README section.

What's Changed

Security fix

  • GHSA-8hg8-63c5-gwmxnesting: true bypassed require: false, allowing sandbox-to-host RCE via inner NodeVM construction. The contradictory option pair { nesting: true, require: false } now throws VMError at new NodeVM(...) time citing the advisory. Same shape as the GHSA-cp6g eager FileSystem-contract probe — surface contradictory configuration at the API surface, not silently produce an unsandboxed sandbox.

Documentation

  • New README section "nesting: true is an escape hatch" under Hardening recommendations. Spells out the inner-VM independence: a nested VM's require config is chosen by the sandbox code that constructs it, not constrained by the outer VM. Do not enable nesting: true for untrusted code.
  • JSDoc on the nesting option (lib/nodevm.js) upgraded to match.
  • docs/ATTACKS.md gains Category 25 documenting the configuration trap, plus a matching row in the "How The Bridge Defends" table.

Upgrade Notes

  • If you set { nesting: true, require: false } anywhere in your codebase, new NodeVM(...) now throws. Either drop nesting: true (if you wanted deny-all), or replace require: false with an explicit require config (e.g. require: { builtin: [] }) to acknowledge that vm2 will be requireable. The error message is actionable and links to the README section.
  • No other configurations are affected. Bare new NodeVM({ nesting: true }) continues to work as documented; this is the documented escape hatch and is not closed by this patch (out of scope — would change nesting: true semantics substantially).

What This Fix Does NOT Close

nesting: true itself remains an escape hatch for any non-trivial require config. The fix closes the specific contradictory pair flagged by the advisory; the broader recommendation is in the new README section: do not enable nesting: true when running untrusted code. Constraint propagation from outer to inner NodeVM (where the outer's require config would constrain inner construction) was considered and deferred — it would change the documented semantics of nesting: true and is a major-version-shaped change.

Full Changelog: v3.11.0...v3.11.1

v3.11.0

Choose a tag to compare

@patriksimek patriksimek released this 01 May 20:37

Coordinated security release closing 13 advisories, plus a new bufferAllocLimit option and a realpath() method on the FileSystem adapter contract. Minor version bump because of the new public option and the FileSystem contract addition; no incompatible changes to the existing public API surface.

Embedders running untrusted code should upgrade. Several of the advisories close full sandbox-escape RCE primitives.

What's Changed

Security fixes

Plus two related hardenings discovered during pre-release red-team:

  • trace_events added to the dangerous-builtins denylist — createTracing({categories: [Proxy<Array>]}) triggered a C++ assertion that aborted the host process.
  • wasi added to the denylist — experimental syscall surface (filesystem preopens, host clock/random, network) too broad for default '*' exposure.

New options

  • bufferAllocLimit (VM, NodeVM) — non-negative number or Infinity. Caps individual Buffer.alloc family requests from inside the sandbox. Default: Infinity (fully backwards-compatible). Embedders running untrusted code should opt into a finite cap as part of layered DoS defense.

FileSystem contract

  • DefaultFileSystem.realpath() and VMFileSystem.realpath() added. require.root boundary checks now canonicalise candidate paths via realpath before the prefix check, closing CWE-59 (symlinks inside the allowed root pointing outside it). Especially relevant for pnpm / npm-workspaces / npm link layouts where every node_modules entry is a symlink by design.

Upgrade Notes

  • Custom fs adapters with require.root must implement realpathSync (or realpath() on a fully custom FileSystem class). Without it, new NodeVM({require: {root, fs: customAdapter}}) now throws a VMError at construction citing GHSA-cp6g-6699-wx9c. Default fs users are unaffected.
  • Embedders running untrusted async code should install a host-side unhandledRejection handler. The GHSA-hw58 fix closes synchronous executor throws but cannot reach async-function / async-generator / await using rejection paths (V8 creates rejection promises via the realm's intrinsic Promise). See Hardening recommendations.
  • Embedders in memory-constrained environments should opt into a finite bufferAllocLimit (e.g. 32 * 1024 * 1024).

Documentation

Full Changelog: v3.10.5...v3.11.0

v3.10.5

Choose a tag to compare

@patriksimek patriksimek released this 17 Feb 01:14

What's Changed

  • fix: allow Object.setPrototypeOf on sandbox-local objects
  • fix: block Function constructor access via getOwnPropertyDescriptor
  • fix: block Function constructor from crossing bridge regardless of access path
  • fix: block all code-executing constructors from crossing bridge via property descriptors
  • fix: block Function constructor extraction via nested property descriptors
  • fix: prevent proxy unwrapping to block Function constructor extraction via Object.entries
  • fix: prevent sandbox escape via doPreventExtensions exposure in util.inspect
  • fix: prevent sandbox escape via getFactory exposure in util.inspect
  • fix: sanitize SuppressedError sub-errors to prevent sandbox escape
  • fix: block host Function constructor leak via direct handler.get() call
  • fix: block WebAssembly.JSTag to prevent wasm-level exception catch sandbox escape in Node 25

Full Changelog: v3.10.4...v3.10.5