NIST deprecates RSA-2048 from 2030. Find every one of them before attackers do.
📦
📦 npm
Not in CISA KEV
CRITICAL severity

vm2 Custom Module Resolver Can Bypass the External Package Allowlist by Loading a Colliding Host PackageGHSA-c48m-32m9-vx93

CRITICALFix: patriksimek/vm2@ab4ee7d

GHSA-c48m-32m9-vx93 is a critical-severity (CVSS 9.9) CWE-706 vulnerability in vm2. A fix is available for vm2 — see the affected versions and patch details below.

Also known asCVE-2026-92951
Published
Updated
Affected
1 pkg
Patched
1 / 1
Exploits
None indexed
Exploitation data as of Oct 31, 2026 · OSV.dev, NVD, FIRST.org (EPSS)

Exploitation Status

No confirmed exploitation observed yet

  • A successful exploit gives an attacker total control of the affected component, not partial access.
  • CISA’s own triage has not observed active exploitation or public proof-of-concept code for this CVE as of its last assessment.

Exploitation and automatability from CISA’s SSVC triage for GHSA-c48m-32m9-vx93.

EPSS Exploitation Probability

via FIRST.org ↗
0.5%probability of exploitation in next 30 days
Lower Risk-0.07%
Lower risk than most CVEs40th percentile — riskier than 40% of all scored CVEsHighest risk
0.00%0.35%0.69%1.04%0.5%0.5%Oct 26Oct 26

Probability of exploitation in the next 30 days, from FIRST.org EPSS.

How urgent is this, really

Where this sits among everything scored

Real-World Exposure

1 pkg affected

How broadly this vulnerability is actually deployed: weekly install volume shows current usage, and reverse-dependency count shows how many other packages break if it stays unpatched.

900other npm packages depend on this — each one inherits the vulnerability until it's patched upstream
vm2npm
1.3Mdownloads / week

Description

Summary

vm2 is a sandbox library for isolating and executing untrusted JavaScript code inside a Node.js process. It can restrict access to built-in modules and external packages.

When NodeVM enables an external allowlist together with a custom resolve callback, vm2 checks the requested package name with a non-exact match. For example, if the allowlist only permits left-pad, an attacker can still bypass the check with a colliding package name such as evil-left-pad, because it contains the allowlisted name.

If the colliding package already exists in a host path resolvable by the custom resolver, or if the target application's custom resolver / dependency-management workflow downloads the package and places it in a resolvable path, vm2 loads and executes that package in the host context. This lets sandboxed code bypass the module allowlist and may further lead to host code execution.

Details

NodeVM supports require.external to configure which external npm packages sandboxed code may load. It also supports a custom resolver through require.resolve. This combination is commonly used in business plugin systems, user-script platforms, or sandbox execution environments: the application allows only a small set of trusted dependencies while using a custom resolver that points to the application's own package directory.

The vulnerability is in the allowlist pre-check logic before the custom resolver is called. vm2 generates a regular expression from the external allowlist and uses it to check the original package name supplied by sandboxed code. However, the regular expression is not anchored to the full package-name boundary, so it performs a substring match on the original package name.

For example, with the following configuration:

new NodeVM({
  require: {
    external: ['left-pad'],
    resolve: id => require.resolve(id, { paths: [customRoot] }),
    context: 'host',
    builtin: []
  }
})

The intended policy is that sandboxed code can only load left-pad. However, because vm2 uses a non-exact match similar to /left\-pad/ for the requested package name, names such as evil-left-pad and left-pad-backdoor also pass the allowlist pre-check.

After evil-left-pad passes the check, vm2 calls the custom resolver configured by the application. If the resolver can find that colliding package in a host-resolvable path, vm2 adds the returned path to the loadable list and, in context: 'host' mode, loads the package with the host require(). At that point, the package's top-level code executes in the host context instead of being constrained by sandbox restrictions such as NodeVM's builtin: [].

In local verification, the PoC only allows external: ['left-pad'] and sets builtin: [], so sandboxed code cannot directly load child_process. However, after the sandboxed code executes require('evil-left-pad'), vm2 still loads the colliding package from a host path. The colliding package's top-level code successfully invokes the host child_process module and prints HOST_EXEC.

This issue does not mean that vm2 automatically downloads malicious packages from npm at runtime. The attack requires the colliding package to already be in a path resolvable by the custom resolver, or for the target application's own dependency resolution / download workflow to place that package in such a path. This precondition limits the affected scenarios, but it does not change the core vulnerability: the external allowlist is not enforced against complete package-name boundaries, allowing sandboxed code to load a host package that the application did not authorize.

PoC

An independent PoC is attached in the poc directory. The PoC installs vm2 3.11.5, creates a temporary host package directory, and writes an unauthorized evil-left-pad package into it.

Run the following from the poc directory:

npm install
node poc.js

When the vulnerability is present, the output is similar to:

[+] vm2 version: 3.11.5
[+] customRoot: /tmp/vm2-resolver-poc-xxxxxx/custom
[+] Direct sandbox require("child_process") is blocked: ENOTFOUND
[+] Unrelated package is blocked: ENOTFOUND
[+] require("evil-left-pad") result: HOST_EXEC
[+] RESULT: VULNERABLE

HOST_EXEC is produced by the top-level code of the evil-left-pad package through host-side child_process.execFileSync(), demonstrating that the unauthorized package has executed in the host context.

Expected result: when the external allowlist only contains left-pad, evil-left-pad should be rejected.

Actual result: evil-left-pad passes the check because it contains the left-pad substring and then executes in the host context.

Impact

Under the affected configuration, an attacker can load a colliding host package that is not allowed by the external allowlist through sandboxed code, breaking NodeVM's module access control.

If the attacker can control or influence the contents of the colliding package, for example by placing a malicious package through a plugin upload directory, a user-controllable dependency directory, a private registry synchronization directory, an application-specific dependency download workflow, or another path reachable by the resolver, the attacker can further execute code in the host Node.js process context. The attacker may read files, environment variables, and secrets accessible to the host process, modify host-writable data, or interrupt the host service.

The main exploitation prerequisites are:

  • The application uses vm2 NodeVM to execute untrusted or low-trust JavaScript code.
  • The application enables a require.external allowlist instead of allowing all external packages.
  • The application configures a custom require.resolve.
  • The colliding package already exists in a path resolvable by that custom resolver, or the application's workflow downloads / synchronizes it into that path.
  • The application loads external packages in the host context, or keeps the relevant default behavior.

If the deployed custom resolver only searches a fixed dependency directory that is fully trusted and cannot be influenced by an attacker, practical exploitability is reduced. However, in scenarios such as plugin systems, user scripts, uploadable dependency packages, private package-source synchronization, or multi-tenant sandbox platforms, this vulnerability can bypass the sandbox boundary.

Credit

This vulnerability was discovered by:

Affected Packages

1 total 1 fixed
EcosystemPackageVulnerable rangeFix
📦npmvm2all versions3.11.7npm install vm2@3.11.7

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for vm2, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.

  2. Fix

    Update vm2 to 3.11.7 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-c48m-32m9-vx93 is resolved across your whole dependency graph.

  3. Workarounds

    Put an independent control in front of the weakness: restrict the affected endpoint or interface to trusted networks, require an additional authentication factor or proxy-level check, and invalidate existing sessions and credentials in case the flaw has already been used.

Fixing This On Your OS

If you run this on a Linux distribution, patch through your package manager against the distro's own security advisory below — it tracks the exact backported fix for your release, which can ship on a different timeline (and sometimes a different severity) than the upstream project.

Red HatImportant

This vulnerability is rated as having an Important impact for Red Hat products because an attacker capable of supplying untrusted code to the sandbox can break out of isolation and achieve code execution on the underlying host system. Applications such as Red Hat Developer Hub and Ansible Automation Platform that…

Workaround published by Red Hat
Mitigation for this issue is either not available or the currently available options do not meet the Red Hat Product Security criteria comprising ease of use and deployment, applicability to widespread installation base, or stability.
Source: Red Hat security advisory for GHSA-c48m-32m9-vx93 (CC BY 4.0)
ProductFixed inAdvisory
Red Hat Ansible Automation Platform 2.1ansible-automation-platform/automation-portal:1791295379RHSA-2026:80603
Red Hat Ansible Automation Platform 2.2ansible-automation-platform/automation-portal:1791293780RHSA-2026:80644
Red Hat Ansible Automation Platform 2.2ansible-automation-platform/bootc-automation-portal-rhel9:1791865660RHSA-2026:82151
Red Hat Developer Hub 1.10rhdh/red-hat-developer-hub-backstage-plugin-lightspeed-backend:1791242697RHSA-2026:76788
Red Hat Developer Hub 1.10rhdh/rhdh-hub-rhel9:1791311495RHSA-2026:79088
Red Hat Developer Hub 1.9rhdh/rhdh-hub-rhel9:1793117813RHSA-2026:91651

Frequently Asked Questions

### Summary vm2 is a sandbox library for isolating and executing untrusted JavaScript code inside a Node.js process. It can restrict access to built-in modules and external packages. When `NodeVM` enables an `external` allowlist together with a custom `resolve` callback, vm2 checks the requested package name with a non-exact match. For example, if the allowlist only permits `left-pad`, an attacker can still bypass the check with a colliding package name such as `evil-left-pad`, because it contains the allowlisted name. If the colliding package already exists in a host path resolvable by the
O3 Security · Impact-Aware SCA

Is GHSA-c48m-32m9-vx93 in your dependencies?

Find it across npm, including transitive dependencies.

vm2 Custom Module Resolver Can Bypass the External…