CVE-2026-92957: vm2 NodeVM Node:-prefixed Negative Builtin Deny Vulnerability
CRITICALCVE-2026-92957 is a CRITICAL (CVSS 9.9) nodevm node:-prefixed negative builtin deny vulnerability (CWE-284: Improper Access Control) affecting vm2 libraries in the npm ecosystem. The issue involves nodevm node:-prefixed negative builtin deny bypass exposes child_process. Remediation is available by upgrading affected packages to version 3.11.7.
CVE-2026-92957 at a Glance
Security Impact
CVSS: 9.9
The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H).
How to Fix CVE-2026-92957
To remediate this vulnerability, upgrade all affected vm2 dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.
npm install vm2@3.11.7
Run the following command in your project directory to verify that all resolved dependencies use the patched release:
npm ls vm2
How to Check if You Are Affected
To determine whether your Node.js application is affected by CVE-2026-92957, inspect your project dependencies using npm:
npm ls vm2
Analyze the terminal output to locate any version of vm2 in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 3.11.7), your application is affected.
Note: The npm ls vm2 command inspects dependencies declared in your package.json and lockfile; it does not perform automated runtime dynamic scanning.
Technical Details
Vulnerability Mechanism & Technical Analysis
This vulnerability is classified under CWE-284: Improper Access Control, where the product does not restrict or incorrectly restricts access to a resource from an unauthorized actor. In vm2, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.
Source Advisory Statement
CVE-2026-92957 Affected vm2 Versions
1 component(s)| Product / Package | Ecosystem | Affected Versions | Fixed Version |
|---|---|---|---|
|
vm2
|
npm |
< 3.11.7
|
3.11.7
|
References
Frequently Asked Questions
What is CVE-2026-92957?
Which vm2 versions are affected?
What version fixes CVE-2026-92957?
Is vm2 affected?
What is the CVSS score of CVE-2026-92957?
What CWE is associated with CVE-2026-92957?
How can I check npm dependencies for CVE-2026-92957?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"schema_version" : "1.4.0",
"id" : "GHSA-8686-vhfx-7r3j",
"modified" : "2026-10-01T15:36:01Z",
"published" : "2026-10-01T15:36:01Z",
"aliases" : [ "CVE-2026-92957" ],
"summary" : "vm2: NodeVM node:-prefixed negative builtin deny bypass exposes child_process",
"details" : "## Summary\n\nNodeVM normalizes `node:`-prefixed builtin specifiers during `require()` resolution, but it does not normalize user-provided negative builtin entries in wildcard policy.\n\nAs a result, this configuration:\n\n```js\nnew NodeVM({\n require: {\n builtin: ['*', '-node:child_process']\n }\n});\n```\n\ndoes not deny the canonical `child_process` builtin. Sandboxed code can require both `child_process` and `node:child_process`, and receives the host module with process-spawning APIs such as `execSync` and `spawn`.\n\nThe safe proof below only checks module and function reachability. It does not execute any OS command.\n\n## Affected Mode\n\nNodeVM.\n\n## Affected Configuration\n\n```js\nnew NodeVM({\n require: {\n builtin: ['*', '-node:child_process']\n }\n});\n```\n\nThis affects users who deny builtins using their `node:`-prefixed spelling, expecting `-node:child_process` to deny `require('node:child_process')` and `require('child_process')`.\n\n## Affected Files / Functions\n\n- `lib/builtin.js`\n - `makeBuiltinsFromLegacyOptions`\n - wildcard builtin expansion\n - exact negative entry check: `builtins.indexOf(\\`-${name}\\`)`\n - `addDefaultBuiltin`\n- `lib/resolver.js`\n - `Resolver.resolve`\n- `lib/setup-node-sandbox.js`\n - `requireImpl`\n - `node:` prefix stripping before builtin load\n\n## Root Cause\n\n`lib/setup-node-sandbox.js` strips the `node:` prefix from resolved builtin filenames before loading the builtin:\n\n```js\nif (localStringPrototypeStartsWith(filename, 'node:')) {\n id = localStringPrototypeSlice(filename, 5);\n let nmod = cacheBuiltins[id];\n if (!nmod) {\n nmod = loadBuiltinModule(id);\n if (!nmod) throw new VMError(`Cannot find module '${filename}'`, 'ENOTFOUND');\n cacheBuiltins[id] = nmod;\n }\n return nmod;\n}\n```\n\nBut `lib/builtin.js` checks wildcard negative entries by exact string match against the names in `BUILTIN_MODULES`:\n\n```js\nif (builtins.indexOf(`-${name}`) === -1) {\n addDefaultBuiltin(res, name, hostRequire);\n}\n```\n\n`BUILTIN_MODULES` contains the canonical name `child_process`, not `node:child_process`. Therefore `-node:child_process` does not exclude `child_process`, and `addDefaultBuiltin()` registers the host builtin.\n\n## Security Boundary Crossed\n\nSandboxed code reaches a host builtin that the embedder attempted to deny.\n\nBoundary crossed:\n\n- sandbox -> host `child_process` builtin\n- sandbox -> host process-spawning function references\n\n## Impact\n\nConfirmed impact:\n\n- `require('child_process')` succeeds inside the sandbox.\n- `require('node:child_process')` succeeds inside the sandbox.\n- The returned module exposes `execSync` and `spawn` as functions.\n\nWorst confirmed impact is access to host process-spawning APIs. The proof does not execute any command.\n\nThe proof does not execute a command, but it confirms access to the host child_process module and its process-spawning APIs. For untrusted sandbox code, this is equivalent to command execution capability.\n\n## Safe Local Reproduction\n\nTested on Node.js `v24.14.0`.\n\nThis proof only checks whether the module and dangerous functions are reachable. It does not spawn a process and does not run OS commands.\n\n```js\n'use strict';\n\nconst { NodeVM } = require('./');\n\nfunction probe(builtin) {\n const vm = new NodeVM({\n require: {\n builtin\n }\n });\n\n return vm.run(`\n const out = {};\n\n for (const spec of ['child_process', 'node:child_process']) {\n try {\n const cp = require(spec);\n out[spec] = {\n loaded: true,\n execSyncType: typeof cp.execSync,\n spawnType: typeof cp.spawn,\n moduleToStringTag: Object.prototype.toString.call(cp)\n };\n } catch (e) {\n out[spec] = {\n loaded: false,\n name: e && e.name,\n code: e && e.code,\n message: e && e.message\n };\n }\n }\n\n module.exports = out;\n `);\n}\n\nconsole.log(JSON.stringify({\n nodeVersion: process.version,\n denyNodePrefixed: probe(['*', '-node:child_process']),\n denyCanonical: probe(['*', '-child_process'])\n}, null, 2));\n```\n\nObserved result:\n\n```json\n{\n \"nodeVersion\": \"v24.14.0\",\n \"denyNodePrefixed\": {\n \"child_process\": {\n \"loaded\": true,\n \"execSyncType\": \"function\",\n \"spawnType\": \"function\",\n \"moduleToStringTag\": \"[object Object]\"\n },\n \"node:child_process\": {\n \"loaded\": true,\n \"execSyncType\": \"function\",\n \"spawnType\": \"function\",\n \"moduleToStringTag\": \"[object Object]\"\n }\n },\n \"denyCanonical\": {\n \"child_process\": {\n \"loaded\": false,\n \"name\": \"VMError\",\n \"code\": \"ENOTFOUND\",\n \"message\": \"Cannot find module 'child_process'\"\n },\n \"node:child_process\": {\n \"loaded\": false,\n \"name\": \"VMError\",\n \"code\": \"ENOTFOUND\",\n \"message\": \"Cannot find module 'node:child_process'\"\n }\n }\n}\n```\n\n## Expected Secure Behavior\n\n`-node:child_process` and `-child_process` should be equivalent.\n\nIf either spelling is denied, both of these should fail:\n\n```js\nrequire('child_process')\nrequire('node:child_process')\n```\n\n## Suggested Fix\n\n1. Canonicalize builtin names before allow/deny comparison:\n - Strip `node:` from user-provided builtin entries.\n - Preserve whether an entry is negative (`-...`) before canonicalizing.\n - Store and compare one canonical builtin key.\n\n2. Apply the same normalization to:\n - wildcard negative entries\n - explicit allowlist entries\n - object-form builtin entries\n - mock/override keys if they are intended to support `node:` spelling\n\n3. Add regression tests:\n - `builtin: ['*', '-node:child_process']` blocks `child_process`.\n - `builtin: ['*', '-node:child_process']` blocks `node:child_process`.\n - `builtin: ['*', '-node:fs']` blocks `fs` and `node:fs`.\n - `builtin: ['*', '-node:fs/promises']` and `-fs/promises` behave consistently.\n - Canonical dangerous builtins remain denied even if explicitly requested with `node:` spelling.",
"severity" : [ {
"type" : "CVSS_V3",
"score" : "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H"
} ],
"affected" : [ {
"package" : {
"ecosystem" : "npm",
"name" : "vm2"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "0"
}, {
"fixed" : "3.11.7"
} ]
} ],
"database_specific" : {
"last_known_affected_version_range" : "<= 3.11.6"
}
} ],
"references" : [ {
"type" : "WEB",
"url" : "https://github.com/patriksimek/vm2/security/advisories/GHSA-8686-vhfx-7r3j"
}, {
"type" : "ADVISORY",
"url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-92957"
}, {
"type" : "WEB",
"url" : "https://github.com/patriksimek/vm2/commit/b0f50662dd499ff33544bb42387958c64711af1e"
}, {
"type" : "PACKAGE",
"url" : "https://github.com/patriksimek/vm2"
}, {
"type" : "WEB",
"url" : "https://github.com/patriksimek/vm2/releases/tag/v3.11.7"
}, {
"type" : "WEB",
"url" : "https://www.vulncheck.com/advisories/vm2-before-3.11.7-authentication-bypass-via-node-prefix"
} ],
"database_specific" : {
"cwe_ids" : [ "CWE-269", "CWE-284" ],
"severity" : "CRITICAL",
"github_reviewed" : true,
"github_reviewed_at" : "2026-10-01T15:36:01Z",
"nvd_published_at" : null
}
}