Skip to content

chore(deps): bump vm2 from 3.11.5 to 3.11.8 in /workspaces/checkmarx - #1484

Open
dependabot[bot] wants to merge 1 commit into
mainfrom
dependabot/npm_and_yarn/workspaces/checkmarx/vm2-3.11.6
Open

chore(deps): bump vm2 from 3.11.5 to 3.11.8 in /workspaces/checkmarx#1484
dependabot[bot] wants to merge 1 commit into
mainfrom
dependabot/npm_and_yarn/workspaces/checkmarx/vm2-3.11.6

Conversation

@dependabot

@dependabot dependabot Bot commented on behalf of github Aug 20, 2026

Copy link
Copy Markdown

Bumps vm2 from 3.11.5 to 3.11.8.

Release notes

Sourced from vm2's releases.

v3.11.8

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: patriksimek/vm2@v3.11.7...v3.11.8

v3.11.7

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).

... (truncated)

Changelog

Sourced from vm2's changelog.

[3.11.8]

Security fixes

  • GHSA-3vgf-8m4q-q4qr (dup: GHSA-59g5-pmg6-5gr4) — default VM host intrinsic prototype pollution of the binary-data and iterator families. The protected inventory omitted ArrayBuffer / SharedArrayBuffer / DataView / every TypedArray / the abstract %TypedArray%.prototype, and the array/string/map/set/regexp-string iterator prototypes plus the shared %IteratorPrototype%. Because Buffer extends Uint8Array, the Category 20 proto-walk from a host Buffer reached those unprotected host prototypes and Reflect.defineProperty polluted them globally, corrupting every host-realm typed array and iterator. lib/bridge.js now lists the binary-data globals in globalsList and resolves the abstract intrinsic prototypes structurally into thisGlobalPrototypes, routing all of them into protectedHostObjects, the proto-mapping table, and the identity map so the write traps refuse sandbox set/defineProperty. See ATTACKS.md Category 20 (extended) and test/ghsa/GHSA-3vgf-8m4q-q4qr/.
  • GHSA-88hf-g992-jg85 — NodeVM default-config (console: 'inherit') sandbox escape. The sandbox extracted the raw host Object.prototype.__proto__ getter (via Buffer.call.call(__lookupGetter__, …, '__proto__'), the GHSA-v6mx/cfcw primitive) and, because that getter was never classified dangerous like the setter, climbed console._stdout's host prototype chain to the non-intrinsic EventEmitter.prototype, overwrote emit, and had the host invoke it with this === process → RCE. Closed with two independent layers in lib/bridge.js: (1) the raw host proto-readers (__proto__ getter, Object.getPrototypeOf, Reflect.getPrototypeOf) are denied delivery at thisFromOtherWithFactory/thisEnsureThis/thisFromOtherForThrow and the apply trap, so the sandbox can no longer climb host chains; (2) host [[Prototype]] objects are marked at delivery and sandbox function/accessor writes to them are diverted off the raw host object in BaseHandler.set/defineProperty. Legitimate Object.getPrototypeOf on host proxies and data/leaf writes are unchanged. See ATTACKS.md Category 50 and test/ghsa/GHSA-88hf-g992-jg85/.
  • GHSA-f8gf-w286-fmq2allowAsync: false async-execution boundary bypassed via Promise thenable assimilation. Blocking Promise.prototype.then left every native resolve capability open: Promise.resolve/all/race/any/allSettled/try, new Promise(r => r(thenable)), withResolvers().resolve, Array.fromAsync, and the realm-intrinsic base reached via Object.getPrototypeOf(Promise) all let V8's PromiseResolveThenableJob run an attacker .then in a microtask after run() returned, outside the configured timeout. Structural fix in lib/setup-sandbox.js, gated entirely to allowAsync: false: a TOCTOU-safe resolve-capability guard (refuses object/function values without ever reading .then), synchronous throws on the assimilating static methods, a non-configurable throwing Array.fromAsync stub, and a construct-guard Proxy on localPromise's prototype that makes the native base un-constructable from the sandbox. allowAsync: true is untouched. See ATTACKS.md Category 51 and test/ghsa/GHSA-f8gf-w286-fmq2/.
  • GHSA-gjq8-xm47-88rc — an embedder-exposed host function (or a host builtin such as events.once) that returns a rejected host Promise crashed the entire host process when sandbox code called it and ignored the result. The bridge handed the sandbox a wrapped promise but left the underlying host promise without a rejection reaction of its own, so Node's default unhandledRejection policy (Node 15+) tore the process down — a sandbox-triggered host DoS from a single line of untrusted code. Sibling of the parent advisory GHSA-hw58-p9xv-2mjh, which hardened the opposite (sandbox→host) direction. lib/bridge.js now attaches a benign no-op reaction to the underlying host promise on the host side, at the apply-trap boundary (markHostPromiseHandled). Promises multicast, so the sandbox's own GHSA-55hx-sanitized .then/.catch still fires and still observes the sanitized rejection; the no-op onRejected returns undefined, so it never creates a new unhandled rejection; fulfilled promises are untouched. See ATTACKS.md Category 22 (extended) and test/ghsa/GHSA-gjq8-xm47-88rc/.
  • GHSA-r273-hxvj-fxhp — NodeVM exposed host util to the sandbox as an unfiltered Object.assign({}, util), so util.getCallSites() (Node >= 22.9) handed sandboxed code the host process call stack — absolute paths including vm2's own lib/ and the embedder entrypoint — bypassing the GHSA-v27g host-frame redaction, which only covers sandbox-realm Error stacks. getCallSite / setTraceSigInt / private internals rode the same wholesale copy, and the sys alias leaked identically via the generic loader. lib/builtin.js now builds the exposed util from a vetted, forward-safe allowlist (SAFE_UTIL_MEMBERS, presence-gated Node 8→26) routed through the BUILTIN_MEMBER_SANITIZERS chokepoint for both util and sys, so no unreviewed host member reaches the sandbox. Information disclosure only. See ATTACKS.md Category 52 and test/ghsa/GHSA-r273-hxvj-fxhp/.
  • GHSA-x965-fc75-jpqh — incomplete-fix bypass of GHSA-m283-3h24-438v: a host-wrapped AggregateError/SuppressedError revisited within one handleException traversal (self-cycle agg.errors=[agg], duplicate [shared,shared], mutual cycle) returned the raw host carrier from the cycle memo, re-embedding a live host proxy into the rebuilt errors[] → host RCE on the caught-exception channel. lib/setup-sandbox.js now memoizes each carrier to exactly what a revisit must return (itself when sealed in place, its sandbox-realm replacement when rebuilt), builds host-wrapped aggregate/suppressed replacements in two phases so every cycle terminates on the replacement, memoizes the sanitizeHostOwnProps rebuild, and adds a _blockHostWrapped backstop at embed sites. See ATTACKS.md Category 49 and test/ghsa/GHSA-x965-fc75-jpqh/.

[3.11.7]

Security fixes

  • GHSA-27g9-p43v-cw3v — VM sandbox escape on Node 26 via a stale PromiseThenLookupChain protector. vm2 installed its Promise.prototype.then / catch wrappers by plain assignment; on V8 14.6 that updates the data property without invalidating the protector, so Promise.prototype.finally took an internal InvokeThen fast path straight to the original native then and vm2's wrapper — and its resetPromiseSpecies — never ran. An attacker constructor[Symbol.species] on an ordinary async-function Promise therefore survived p.finally() and took control of a native reaction, which a calibrated stack overflow turned into a raw host-realm RangeError (e.constructor.constructor → host Function → host process), reachable with eval: false and wasm: false. lib/setup-sandbox.js now installs the then / catch wrappers through Reflect.defineProperty (which does invalidate the protector) and wraps Promise.prototype.finally to run resetPromiseSpecies(this) before delegating to the cached native implementation. See ATTACKS.md Category 43 and test/ghsa/GHSA-27g9-p43v-cw3v/.
  • GHSA-46pr-c5wc-xffxcrypto.setEngine(path) handed 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 rejected the file — native RCE from a NodeVM allowing only crypto. lib/builtin.js now neutralizes the member before the read-only wrap, so no library is ever loaded; the rest of crypto is untouched. See ATTACKS.md Category 40 and test/ghsa/GHSA-46pr-c5wc-xffx/.
  • GHSA-633r-hq9m-c4ffvm.freeze() / vm.readonly() read-only bypass: a frozen host object's accessor set was still reachable from the sandbox via Object.getOwnPropertyDescriptor(...).set, __lookupSetter__, Reflect.getOwnPropertyDescriptor and Object.getOwnPropertyDescriptors, letting sandbox code mutate host state through a view the embedder declared read-only. Fixed in lib/bridge.js by stripping set in ReadOnlyHandler.getOwnPropertyDescriptorDesc and routing doPreventExtensions descriptors through that same hook; getter reads are preserved and non-frozen host objects are unaffected. See ATTACKS.md Category 44 and test/ghsa/GHSA-633r-hq9m-c4ff/.
  • GHSA-647f-g98j-qq25 — patch bypass of the GHSA-m283-3h24-438v host-Promise rejection sanitizer, allowing sandbox-to-host RCE. The apply-trap gate identity-checked only the direct apply target, so registering an onRejected handler through Function.prototype.call/.apply indirection skipped the capability-stripping rebuild and delivered the raw host rejection to sandbox code. lib/bridge.js now peels call/apply indirection to the effective host then/catch and wraps the callbacks regardless of invocation shape, snapshotting .apply argument arrays getter-free and failing closed past a bounded peel depth. ATTACKS.md Category 39; tests in test/ghsa/GHSA-647f-g98j-qq25/.
  • GHSA-6rh5-qq4q-97xh — NodeVM builtin deny tokens did not cover subpath siblings: fs and fs/promises are separate builtinModules entries, so builtin: ['*', '-fs'] removed only fs and left the full host fs/promises API (host filesystem writes via writeFile) exposed. The same gap affected every subpath family (-pathpath/posix, -streamstream/*, -timerstimers/promises). The '*' deny check in lib/builtin.js now routes through isBuiltinDenied, which treats <family>/<sub> as denied whenever -<family> is present, in either node: spelling; undenied families keep their subpaths. See ATTACKS.md Category 21 and test/ghsa/GHSA-6rh5-qq4q-97xh/.
  • GHSA-6w8r-xxw2-g3hxnode:sqlite's DatabaseSync(':memory:', { allowExtension: true }).loadExtension(path) loaded a native SQLite extension into the host process — native RCE from a NodeVM allowing only that builtin. The exposed DatabaseSync now forces allowExtension off (for object- and function-typed options alike, since Node accepts a function there), so Node throws ERR_INVALID_STATE from both loadExtension() and enableLoadExtension() while ordinary SQL keeps working. lib/setup-node-sandbox.js additionally rejects repeated node: prefixes, closing the require('node:node:sqlite') second spelling and making the canonical require('node:sqlite') resolve. See ATTACKS.md Category 40 and test/ghsa/GHSA-6w8r-xxw2-g3hx/.
  • GHSA-7q3f-wx44-378m — NodeVM external allowlist bypass: a prefix-sharing sibling package loaded as allowlisted. LegacyResolver.isPathAllowedForModule authorized a require from an allowlisted module with a raw path.startsWith(mod.path) test, so .../node_modules/foo2/index.js passed for allowlisted .../node_modules/foo. lib/resolver-compat.js now requires a path boundary after mod.path (exact match / trailing separator / next char a separator), so foo2 no longer matches foo. Scoped names (@scope/pkg vs @scope/pkg-evil) were affected identically and are covered. See ATTACKS.md Category 46 and test/ghsa/GHSA-7q3f-wx44-378m/.
  • 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 child_process name and the host module (RCE via execSync/spawn) stayed exposed. The '*' deny check in lib/builtin.js now matches both -${name} and -node:${name}, mirroring the node: normalization the resolver already applies on the require side. See ATTACKS.md Category 21 and test/ghsa/GHSA-8686-vhfx-7r3j/.
  • GHSA-8hr7-r645-pc6w — patch-bypass of the GHSA-m4wx-m65x-ghrr NodeVM nesting guard. The guard accepted any typeof requireOpts === 'object' value as a real require config, so { nesting: true, require: [] } (an array — and likewise Date/RegExp/Map/boxed primitives) passed it, destructured to all-undefined, and produced a NESTING_OVERRIDE-only resolver that exposes host vm2 to the sandbox with no restriction → nested NodeVMchild_process → host RCE. The guard now accepts only a Resolver or a plain config object (Object.prototype/null prototype, not an array) via a shared isPlainConfigObject predicate, enforced at two layers: the lib/nodevm.js constructor throws for non-config shapes under nesting, and lib/resolver-compat.js makeResolverFromLegacyOptions fail-closed strips the nesting override for any non-plain options so no alternate caller can re-open it. The documented escape hatch ({ nesting: true, require: {} } / { builtin: [...] }) is unchanged. See ATTACKS.md Category 25 and test/ghsa/GHSA-8hr7-r645-pc6w/.
  • 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. Argument-side defenses were insufficient (the required host array is forgeable through URLSearchParams.getAll()), so the member itself is now neutralized in lib/builtin.js; the rest of tls is unaffected. See ATTACKS.md Category 40 and test/ghsa/GHSA-98xx-8mx4-x7cm/.
  • GHSA-c48m-32m9-vx93 — NodeVM require.external allowlist bypass when a custom require.resolve is configured. The bare-specifier pre-check in LegacyResolver.customResolve matched by substring, so external: ['left-pad'] also admitted evil-left-pad / left-pad-evil; anchoring that matcher then left a second route, since the permitted subpath tail accepted .. segments (left-pad/../evil-package). Either way the resolver located an un-allowlisted host package and, under the default context: 'host', ran its top-level code in host context. Two composed layers in lib/resolver-compat.js: the externalCache matcher is anchored to the whole specifier (wildcard segment semantics preserved), and any bare specifier carrying a .. path segment is rejected before the custom resolver is consulted. See ATTACKS.md Category 45 and test/ghsa/GHSA-c48m-32m9-vx93/.
  • GHSA-fcqc-726x-5wfc — sandbox read/write of host-realm memory through Node's shared small-buffer pool. Node serves small Buffer.from(...) / Buffer.concat(...) / Buffer.of(...) allocations out of one shared 64 KiB backing ArrayBuffer, and a pooled buffer's .buffer getter exposed that whole pool — so Buffer.from(Buffer.from([0]).buffer, 0, 65536) inside the sandbox could disclose and corrupt any host buffer (secrets, tokens, DB rows) sharing it. lib/setup-sandbox.js now enforces a backing-store ownership invariant (byteOffset === 0 && buffer.byteLength === length): every pooling factory (Buffer.from non-ArrayBuffer overloads, concat, of, copyBytesFrom, and the deprecated Buffer(...) / new Buffer(...) forms) copies a pool-backed result into a standalone non-pooled buffer, while the documented Buffer.from(arrayBuffer, byteOffset, length) sharing overload is preserved via a spoof-proof brand test. Independent of bufferAllocLimit. See ATTACKS.md Category 41 and test/ghsa/GHSA-fcqc-726x-5wfc/.
  • GHSA-h85j-hv3c-qfgqhttp.globalAgent / https.globalAgent handed the sandbox the real shared host singleton, so a .on('free') listener received live host request options (including Authorization headers) and released sockets from unrelated host requests. The sandbox now sees a dedicated Agent, and the module's request() / get() default to it so req.agent cannot re-expose the host singleton; a caller-supplied agent is preserved. See ATTACKS.md Category 40 and test/ghsa/GHSA-h85j-hv3c-qfgq/.
  • 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. lib/resolver-compat.js now denies a sandbox require() of vm2's own lib/ directory and package main entry, matched by realpath so a symlinked candidate cannot dodge it; nesting: true is unaffected, as it uses the builtin-override mechanism rather than a file require. Note that require.external without require.root still host-requires any named path — the documented meaning of that option — and now warns once; tightening it to deny-by-default is a breaking change deferred to the next major. See ATTACKS.md Category 47 and test/ghsa/GHSA-j3hm-6rg5-mchv/.
  • GHSA-jf8q-945g-9q4c — incomplete nodejs.* symbol filtering let sandbox code corrupt host-visible WebStream state. The dangerous cross-realm symbol checks were fixed lists that omitted nodejs.stream.disturbed / nodejs.stream.errored, so sandbox code could extract those real host symbols from a ReadableStream.prototype reachable through the sandbox and defineProperty them onto a host stream, flipping stream.Readable.isDisturbed() / isErrored() host-side on an already-consumed stream. isDangerousSymbol (lib/setup-sandbox.js) and isDangerousCrossRealmSymbol (lib/bridge.js) now flag any registered symbol whose Symbol.keyFor is in the reserved nodejs. namespace, covering the extraction filter, the getOwnPropertyDescriptors scrub and the set / defineProperty / deleteProperty write traps, so the guard no longer goes stale as Node adds nodejs.* symbols; well-known symbols and benign registered symbols still cross. See ATTACKS.md Category 8 (extended) and test/ghsa/GHSA-jf8q-945g-9q4c/.
  • GHSA-jxxv-8r27-vm4p — the shipped CLI (npx vm2 ./script.js) ran the target script with no sandbox boundary. lib/cli.js built NodeVM.file(path, {require: {external: true}}) with no require.root and the default require.context: 'host', so the script could require(__filename) — or any absolute path — and execute it in the host realm with full host authority. The CLI now confines requires to the script's own directory (root: pa.dirname(script)) and loads them inside the sandbox (context: 'sandbox'). See ATTACKS.md Category 47 and test/ghsa/GHSA-jxxv-8r27-vm4p/.
  • GHSA-qhwx-74w5-xhxq — NodeVM builtin: ['node:test'] (and ['*']) exposed the real host node:test, whose run({ execArgv: ['--eval=<js>'] }) spawns a separate host Node process running attacker code with full host authority — RCE from a sandbox with no fs or child_process of its own. lib/builtin.js adds test to DANGEROUS_BUILTINS, so the family (including node:test/reporters) is dropped from wildcard expansion, refused on explicit request, and never enters the builtins map; isDangerousBuiltin now strips repeated node: prefixes so node:node:test normalizes too. See ATTACKS.md Category 21 and test/ghsa/GHSA-qhwx-74w5-xhxq/.
  • GHSA-r4fx-v8hh-22mv — bypass of the documented timeout control via a FinalizationRegistry cleanup callback. timeout bounds only the synchronous body of run(); a cleanup callback is invoked by the garbage collector after run() has returned, so sandbox code inside it ran with no timeout accounting at all and could block the host event loop indefinitely — and unlike Promise continuations, allowAsync: false did not close it. lib/setup-sandbox.js now removes FinalizationRegistry and WeakRef from the default sandbox globals, the way timers already are; NodeVM inherits the removal, and embedders who need them for trusted code can re-expose them through the sandbox option. This restores the timeout guarantee for the default configuration; it is not a general DoS-prevention claim (see the README Hardening recommendations). See ATTACKS.md Category 42 and test/ghsa/GHSA-r4fx-v8hh-22mv/.
  • GHSA-wjwh-qqvp-g4p4 (dup: GHSA-m3pp-qgq7-gwm6) — VM sandbox escape via WebAssembly.compileStreaming / instantiateStreaming. On Node 26 both return a Promise whose [[Prototype]] chain reaches the host realm's Promise.prototype, so vm2's sandbox-side Promise overrides and resetPromiseSpecies never run on it; an attacker constructor[Symbol.species] plus p.finally() then delivered the raw host rejection into sandbox code (e.constructor.constructor("return globalThis")() → host process). lib/setup-sandbox.js now removes both streaming-compile APIs alongside the JSPI constructors (GHSA-6j2x-vhqr-qr7q); the non-streaming WebAssembly.compile / instantiate are unaffected. See ATTACKS.md Category 33 (extended) and test/ghsa/GHSA-wjwh-qqvp-g4p4/.
  • GHSA-x6m4-chr9-cg97 — host filesystem path disclosure to sandbox code via host-realm error stacks, a patch bypass of GHSA-v27g-jcqj-v8rw. v27g redacts host frames only from stacks formatted in the sandbox realm, but a host-realm Error arrives with .stack already formatted host-side — absolute paths, node:/internal/ frames, vm2's own lib/*.js, and the embedding application's source — and crossed to the sandbox verbatim; reachable with no special configuration via eval('@@@ catch'), which makes vm2's host-side transformer throw, and generally via any embedder-exposed host function or host builtin that throws. Redacted at three chokepoints, each preserving the message and clean sandbox frames: lib/bridge.js (get + getOwnPropertyDescriptor traps, gated on a severance-robust host-error brand check, covering both the data-property and the Node 22+ accessor shape of Error#stack), lib/setup-sandbox.js (sanitizeHostOwnProps, covering the GHSA-m283 rebuild path that never crosses a bridge trap), and lib/vm.js (sandbox-destined compile errors truncated pre-bridge). Information disclosure only. See ATTACKS.md Category 48 and test/ghsa/GHSA-x6m4-chr9-cg97/.

[3.11.6]

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

Security fixes

  • GHSA-cfcw-xp6x-25gj — stacked-indirection bypass of the GHSA-v6mx-mf47-r5wg apply-trap peel: the peel inspected one layer of Function.prototype.{call,apply,bind} indirection, so two layers slipped the host prototype mutator past it, and a follow-up variant laundered the severance entirely host-side. Closed at two independent chokepoints — lib/bridge.js refuses to deliver host prototype mutators, and both it and handleException reject any host object whose prototype chain reaches null without passing through the sandbox Object.prototype. See ATTACKS.md Category 37 and test/ghsa/GHSA-cfcw-xp6x-25gj/.
  • GHSA-gmc2-2x9w-cgh9bufferAllocLimit (GHSA-6785-pvv7-mvg7) bypass via Buffer.concat(list, totalLength) and Buffer.from(arrayLike), whose host implementations reach the C++ allocator without traversing the sandbox-side allocUnsafe wrapper. lib/setup-sandbox.js now caps concat, from, and copyBytesFrom, and a fail-closed enumeration of host.Buffer's own keys turns any future uncapped allocator into an explicit error rather than a silent bypass. See ATTACKS.md Category 23 (extended) and test/ghsa/GHSA-gmc2-2x9w-cgh9/.
  • GHSA-m283-3h24-438v — host errors leaked live host references into sandbox catch blocks through four channels: Error.cause, the SuppressedError / AggregateError sub-error slots, arbitrary own properties (err.detail = process), and — beyond the reach of own-key enumeration — the carrier's own prototype chain. handleException now seals the spec-defined slots and rebuilds every host-wrapped carrier as a fresh sandbox-realm error carrying only its primitive properties, discarding the host prototype chain entirely. See ATTACKS.md Category 38 and test/ghsa/GHSA-m283-3h24-438v/.
  • GHSA-m5w8-4gq2-6f8x — sibling of GHSA-9g8x: NodeVM builtin: ['*'] still surfaced os and dns, the last two process-wide builtins. Beyond host-identity and network-topology reads, both carry writes reachable in one line of sandbox code — dns.setServers() hijacks the host's DNS resolver, os.setPriority() renices the host process. lib/builtin.js adds both to DANGEROUS_BUILTINS, covering node: spellings and dns/promises automatically. See ATTACKS.md Category 35 (extended) and test/ghsa/GHSA-m5w8-4gq2-6f8x/.
  • GHSA-v836-6xw4-9cx3bufferAllocLimit bypass via ArrayBuffer / SharedArrayBuffer / TypedArray / WebAssembly.Memory, which reach the same synchronous, timeout-immune V8 backing-store allocator uncapped. When a finite limit is set, lib/setup-sandbox.js wraps each constructor with a construct trap capping the ToIndex-coerced byte count, and pins prototype.constructor so the uncapped intrinsic cannot be recovered by a constructor walk. The default Infinity leaves them untouched. See ATTACKS.md Category 36 and test/ghsa/GHSA-v836-6xw4-9cx3/.

Upgrade notes

... (truncated)

Commits

@dependabot dependabot Bot added dependencies Pull requests that update a dependency file javascript Pull requests that update Javascript code labels Aug 20, 2026
Bumps [vm2](https://github.com/patriksimek/vm2) from 3.11.5 to 3.11.8.
- [Release notes](https://github.com/patriksimek/vm2/releases)
- [Changelog](https://github.com/patriksimek/vm2/blob/main/CHANGELOG.md)
- [Commits](patriksimek/vm2@v3.11.5...v3.11.8)

---
updated-dependencies:
- dependency-name: vm2
  dependency-version: 3.11.6
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot dependabot Bot changed the title chore(deps): bump vm2 from 3.11.5 to 3.11.6 in /workspaces/checkmarx chore(deps): bump vm2 from 3.11.5 to 3.11.8 in /workspaces/checkmarx Aug 31, 2026
@dependabot
dependabot Bot force-pushed the dependabot/npm_and_yarn/workspaces/checkmarx/vm2-3.11.6 branch from 1c257a7 to a585e90 Compare August 31, 2026 19:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file javascript Pull requests that update Javascript code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants