CVE-2026-92933: vm2 before 3.11.8 Information Disclosure via util.getCallSites

Published Sep 17, 2026
·
Updated

Summary

NodeVM exposes the host util module to the sandbox through an unfiltered shallow copy (Object.assign({}, util)). On Node.js >= 22.9 this hands sandboxed code util.getCallSites(), a programmatic stack-introspection API that returns the host process's full call stack — absolute file paths, function names, and line numbers — including vm2 bridge internals and the embedding application's entrypoint. This bypasses the host-frame redaction established in GHSA-v27g-jcqj-v8rw, which only covers the Error.prepareStackTrace channel.

Details

- Root cause — defaultBuiltinLoaderUtil copies every static member of the host util module and wraps the copy in vm.readonly() without filtering any member, so newly added Node APIs land in the sandbox automatically: https://github.com/patriksimek/vm2/blob/7a1f5100b96f48d34e0fe104ab37c0acc5944f92/lib/builtin.js#L25-L38 - Second equivalent channel — the deprecated sys builtin (an alias of host util) goes through the generic builtin loader vm.readonly(hostRequire(key)), which also carries getCallSites: https://github.com/patriksimek/vm2/blob/7a1f5100b96f48d34e0fe104ab37c0acc5944f92/lib/builtin.js#L230 - Bypassed protection — GHSA-v27g's redaction (isHostFrameFileName + applyCallSiteGetters) rewrites host-frame metadata getters to null only when the sandbox realm formats an error stack: https://github.com/patriksimek/vm2/blob/7a1f5100b96f48d34e0fe104ab37c0acc5944f92/lib/setup-sandbox.js#L818-L870

util.getCallSites() produces its data host-side and never passes through that formatter, so frames such as lib/bridge.js @apply (the bridge apply trap), lib/nodevm.js @run, the embedder's own entry file, and node:internal/ frames reach the sandbox verbatim as data properties (scriptName, functionName, lineNumber, columnNumber, scriptId). A repository-wide grep (source, docs/ATTACKS.md, CHANGELOG, tests) shows no occurrence of getCallSites; the member was never considered.

PoC

Prerequisites: Node.js >= 22.9 (verified on v22.23.1); run npm ci --no-audit --no-fund at the repository root.

One-line reproducer (prints /workspace/repo/lib/bridge.js, i.e. a vm2-internal host path):

node -e "const {NodeVM}=require('/workspace/repo'); console.log(new NodeVM({require:{builtin:['util']}}).run(\"module.exports = require('util').getCallSites(4)[0].scriptName\"))"

Observed frames inside the sandbox, with both require: { builtin: ['util'] } and require: { builtin: [''] }:

/workspace/repo/lib/bridge.js @apply:1664 vm.js @:5 /workspace/repo/lib/bridge.js @apply:1664 /workspace/repo/lib/nodevm.js @run:563 /workspace/out/<embedder entrypoint>.js @main:29 node:internal/modules/cjs/loader @:1781 node:internal/modules/cjs/loader @:1913 ... (8 node:internal/ frames in total)

Impact

Information disclosure. Any NodeVM configuration that allows the util (or sys) builtin — including the wildcard builtin: [''], both typical configurations from the README — lets untrusted sandboxed code programmatically read the host call stack: the vm2 installation path, the embedding application's file layout and entrypoint, and internal function names and line numbers. This is the same information category redacted by GHSA-v27g-jcqj-v8rw (Defense Invariant #5 in docs/ATTACKS.md) and breaks defense-in-depth assumptions of embedders that rely on host frames being invisible to the sandbox. The API returns only strings/numbers (no host object references); no escalation to code execution was identified.

Suggested fix: filter members in defaultBuiltinLoaderUtil (allowlist, or at minimum drop getCallSites), apply the same treatment to the generic loader path used by sys, and extend the GHSA-v27g regression tests to cover programmatic stack-introspection APIs.

Other sources

vm2 is a sandbox for running untrusted Node.js code. In versions <= 3.11.7, NodeVM exposes the host util module to the sandbox as an unfiltered shallow copy (Object.assign({}, util) in defaultBuiltinLoaderUtil), and the deprecated sys builtin (an alias of host util) is exposed through the generic builtin loader. On Node.js >= 22.9 this hands sandboxed code util.getCallSites(), a programmatic stack-introspection API that returns the host process's full call stack, including absolute file paths, function names, and line numbers for vm2 bridge internals and the embedding application's entrypoint. This bypasses the host-frame redaction introduced for GHSA-v27g-jcqj-v8rw, which only applies to the Error.prepareStackTrace formatting channel. The issue is fixed in vm2 3.11.8.

— MITRE

Affected Software

2 affected componentsFixes available
npm/vm2<=3.11.7
npm/vm2<=3.11.7
3.11.8

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade npm/vm2 to a version that resolves this vulnerability.

    Fixed in 3.11.8
  2. Upgrade

    Upgrade vm2 to a version that resolves this vulnerability.

    Fixed in 3.11.8

Event History

Sep 17, 2026
CVE Published
via MITRE·01:45 PM
Data Sourced
via MITRE·01:45 PM
DescriptionSeverityWeakness
Data Sourced
via NVD·02:17 PM
DescriptionSeverityWeakness
Oct 5, 2026
Advisory Published
via GitHub·10:37 PM
Data Sourced
via GitHub·10:37 PM
DescriptionSeverityWeaknessAffected Software

Frequently Asked Questions

1

Which deployments are exposed to this information disclosure?

Deployments using vm2 versions 3.11.7 or earlier are affected when sandboxed code can run in NodeVM and the host runs Node.js 22.9 or later. The exposure comes from the built-in util module and the deprecated sys alias being available to the sandbox.

2

What does an attacker need to exploit the issue?

An attacker needs the ability to execute code inside a vm2 NodeVM sandbox. No privileges or user interaction are required, and the attacker can invoke util.getCallSites() to inspect host stack information.

3

What information can be disclosed?

The API can return the host process's full call stack, including absolute file paths, function names, line numbers, vm2 bridge internals, and the embedding application's entrypoint.

4

How can the issue be remediated?

Upgrade vm2 to version 3.11.8, which fixes the issue.

5

How can I determine whether my environment is affected?

Check whether your application uses vm2 3.11.7 or earlier with NodeVM to execute untrusted code, and whether it runs on Node.js 22.9 or later. In those conditions, sandboxed code may access util.getCallSites() through util or sys.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203