NVD has published CVE-2026-92935, rated CVSS 9 / CRITICAL with a NETWORK vulnerability pathway. The affected component is vm2, a widely used sandbox for running untrusted Node.js code, specifically versions >= 3.11.4 and <= 3.11.6. Any application that evaluates user-supplied JavaScript, plugin code, theme logic, workflow expressions, template helpers, CI snippets, or tenant-custom scripts through vm2 should treat this as urgent until proven otherwise.
The immediate risk is not simply that a dependency is vulnerable; it is that vm2 often sits at a trust boundary. Teams deploy it precisely because they expect hostile input. A logic error in option handling at that boundary can turn a sandboxed execution feature into an attacker-controlled primitive inside a Node.js service that is frequently internet-facing and frequently over-privileged.
What happened
According to the NVD summary, the flaw is in how NodeVM validates and translates legacy require options. In affected vm2 versions, the constructor computes hasRealRequireConfig using a guard equivalent to:
# Logic described in the advisory summary, shown defensively for code review
typeof requireOpts === 'object' && requireOpts !== null
In JavaScript, arrays are objects. That means an array-shaped value such as require: [] can satisfy a guard that was intended to distinguish a real require configuration from an unsafe nesting state. The summary states that makeResolverFromLegacyOptions() then destructures that array into undefined option fields and returns a resolver containing only NESTING_OVERRIDE.vm2.
From a defender's perspective, the important point is the class of bug: input-shape confusion at a security boundary. A value that looks inert to an administrator — an empty array — is interpreted differently by the option parser. The code path assumes it has rejected an unsafe configuration when, in fact, it has built a resolver from mostly undefined fields plus a vm2-specific override.
The published summary is truncated where it describes the full attacker outcome, so do not over-claim impact beyond the source. What is established is enough to act: critical severity, network-exploitable pathway, affected vm2 range, unsafe parsing of require when array-shaped, and attacker relevance where they can supply JavaScript or influence execution through a NodeVM configuration path.
Affected products, versions, and exposure patterns
Confirmed from the news item:
- Component: vm2
- Affected versions: >= 3.11.4 and <= 3.11.6
- CVE: CVE-2026-92935
- Severity: CVSS 9, CRITICAL
- Attack pathway: NETWORK
- Typical exposure: Node.js services that execute untrusted or semi-trusted JavaScript using vm2, including plugin/theme runners, extensibility engines, low-code expressions, user script features, webhook transformations, CI job evaluators, and multi-tenant SaaS customization points.
The news title references Node.js plugins and themes; the summary names vm2 explicitly. Operationally, prioritize internet-reachable Node.js applications with extensibility features, then any internal automation that executes third-party snippets.
High-risk deployment patterns include:
- Public APIs that accept JavaScript expressions, filters, transforms, or plugin bundles.
- CMS, commerce, documentation, or portal platforms with server-side theme/plugin execution.
- Multi-tenant services where customers can upload logic isolated only by vm2.
- CI/CD runners that evaluate repository-controlled JavaScript inside the same process as secrets.
- Containers running Node as root, with broad egress, cloud metadata access, mounted docker sockets, or ambient service-account credentials.
How the vulnerability works, defensively
The defensive attack chain to validate is:
- An attacker reaches a network-exposed feature that executes JavaScript through vm2 or influences the options passed to
NodeVM. - The affected service runs vm2 3.11.4-3.11.6.
- A legacy
requirevalue is array-shaped, explicitly or through option normalization, for examplerequire: []. - The type guard treats the array as an object and misclassifies the configuration state.
makeResolverFromLegacyOptions()destructures fields that do not exist on an array, producing undefined option values.- The resulting resolver is built with only
NESTING_OVERRIDE.vm2rather than the expected validated require policy. - Depending on surrounding code, sandbox assumptions about module resolution and nesting no longer hold.
For defenders, the key observables are not a magic string in an exploit. They are where vm2 is loaded, which version is present, whether untrusted input can reach NodeVM, and whether a Node process does something a sandbox should never do: spawn shells, read cloud credentials, open outbound tunnels, write outside approved directories, load unexpected native modules, or access metadata endpoints.
Exploitation status
Based only on the provided item: CVE-2026-92935 is published by NVD as critical and network-exploitable. The source does not confirm in-the-wild exploitation, a public proof of concept, or CISA KEV inclusion. Treat absence of evidence as absence of telemetry, not absence of risk. Check CISA KEV, the vm2 project release notes/security advisories, your vendor advisories, and NVD daily until a fixed version and exploit status are unambiguous.
Because vm2 has historically been a high-value sandbox target, assume exploit development will move quickly once technical details circulate.
Detection and response
The highest-confidence detections combine inventory with behavior. First find affected vm2. Then alert on Node processes that cross sandbox boundaries: shell spawns, egress to rare destinations, credential-file access, metadata queries, or writes to persistence locations. The rules below are intentionally bounded; tune parent/child relationships to your application names before broad deployment.
---
title: Node.js vm2 Process Spawning Shell or Script Interpreter
tid: 9f2d6f61-7c47-4f2d-9f8b-vm2a92935001
status: experimental
description: Detects a Node.js process launching a shell or script interpreter, a high-value post-exploitation signal for services using vm2 to execute untrusted JavaScript.
references:
- https://nvd.nist.gov/vuln/detail/CVE-2026-92935
- https://attack.mitre.org/techniques/T1059/
author: Security Arsenal
date: 2026/01/15
tags:
- attack.execution
- attack.t1059
- attack.t1059.007
logsource:
category: process_creation
product: windows
detection:
selection_parent:
ParentImage|endswith:
- '\node.exe'
- '\nodejs.exe'
selection_child:
Image|endswith:
- '\cmd.exe'
- '\powershell.exe'
- '\pwsh.exe'
- '\wscript.exe'
- '\cscript.exe'
- '\mshta.exe'
- '\rundll32.exe'
- '\curl.exe'
- '\wget.exe'
filter_service_accounts:
User|contains:
- 'IIS APPPOOL'
- 'NETWORK SERVICE'
- 'LOCAL SERVICE'
condition: selection_parent and selection_child and not filter_service_accounts
falsepositives:
- Build agents, package managers, and Node CLIs that legitimately invoke shells. Tune by application pool, image path, and deployment role before enabling at high level.
level: high
---
title: Linux Node Process Unexpected Shell or Network Tool Execution
tid: 0c4a7bd2-93b1-4a19-9f18-vm2a92935002
status: experimental
description: Detects Linux node processes spawning shells or network transfer tools, consistent with sandbox-boundary violations after malicious JavaScript execution.
references:
- https://nvd.nist.gov/vuln/detail/CVE-2026-92935
- https://attack.mitre.org/techniques/T1059/
- https://attack.mitre.org/techniques/T1105/
author: Security Arsenal
date: 2026/01/15
tags:
- attack.execution
- attack.t1059.004
- attack.command_and_control
- attack.t1105
logsource:
category: process_creation
product: linux
detection:
selection_parent:
Image|endswith:
- '/node'
- '/nodejs'
selection_child:
Image|endswith:
- '/bin/sh'
- '/bin/bash'
- '/usr/bin/bash'
- '/usr/bin/curl'
- '/usr/bin/wget'
- '/bin/nc'
- '/usr/bin/nc'
- '/usr/bin/ncat'
- '/usr/bin/python3'
- '/usr/bin/perl'
selection_cli:
CommandLine|contains:
- 'curl '
- 'wget '
- 'nc '
- 'bash -i'
- '/dev/tcp/'
- 'chmod +x'
- 'base64 -d'
- '169.254.169.254'
- 'metadata.google.internal'
condition: selection_parent and selection_child and selection_cli
falsepositives:
- npm lifecycle scripts and CI jobs. Scope to production Node services and exclude known pipeline namespaces or container images.
level: high
// Hunt Node.js sandbox-boundary violations in Microsoft Defender for Endpoint / Sentinel
let lookback = 7d;
let shells = dynamic(['cmd.exe','powershell.exe','pwsh.exe','wscript.exe','cscript.exe','mshta.exe','rundll32.exe','curl.exe','wget.exe','sh','bash','nc','ncat','python3','perl']);
DeviceProcessEvents
| where TimeGenerated >= ago(lookback)
| where InitiatingProcessFileName has_any ('node.exe','nodejs.exe','node','nodejs')
or ProcessCommandLine has_any ('vm2','NodeVM','node_modules/vm2')
| where FileName in~ (shells)
or ProcessCommandLine has_any ('169.254.169.254','metadata.google.internal','/dev/tcp/','bash -i','base64 -d','curl ','wget ','nc ')
| project TimeGenerated, DeviceName, AccountName, InitiatingProcessFileName, InitiatingProcessCommandLine, FileName, ProcessCommandLine, FolderPath, SHA256, ReportId
| extend Vm2Context = iff(ProcessCommandLine has 'vm2' or InitiatingProcessCommandLine has 'vm2', 'vm2-reference', 'node-parent')
| summarize FirstSeen=min(TimeGenerated), LastSeen=max(TimeGenerated), Commands=make_set(ProcessCommandLine, 20) by DeviceName, AccountName, InitiatingProcessFileName, FileName, Vm2Context
| order by LastSeen desc;
-- Hunt Node.js processes referencing vm2 or executing sandbox-boundary tools
SELECT Pid, Ppid, Name, Exe, CommandLine, Username, CreateTime,
if(condition=CommandLine =~ '(?i)vm2|NodeVM|require:\s*\[\s*\]',
then='vm2-option-context', else='node-process') AS Context
FROM pslist()
WHERE Name =~ '(?i)^node(js)?(\.exe)?$'
AND (
CommandLine =~ '(?i)vm2|NodeVM|node_modules.(/|\\)vm2|require:\s*\[\s*\]'
OR CommandLine =~ '(?i)cmd\.exe|powershell|pwsh|/bin/sh|/bin/bash|curl |wget |nc |ncat |169\.254\.169\.254|metadata\.google\.internal'
)
#!/usr/bin/env bash
# Identify vm2 installs affected by CVE-2026-92935: >=3.11.4 and <=3.11.6
set -euo pipefail
ROOT='.'
if [ -n '${1:-}' ]; then ROOT='$1'; fi
node - <<'NODE'
const fs = require('fs');
const path = require('path');
const root = process.env.ROOT || '.';
const bad = v => {
const m = String(v).match(/^(\d+)\.(\d+)\.(\d+)/);
if (!m) return false;
const n = m.slice(1).map(Number);
const ge = (a,b) => a[0]>b[0] || (a[0]===b[0] && (a[1]>b[1] || (a[1]===b[1] && a[2]>=b[2])));
const le = (a,b) => a[0]<b[0] || (a[0]===b[0] && (a[1]<b[1] || (a[1]===b[1] && a[2]<=b[2])));
return ge(n,[3,11,4]) && le(n,[3,11,6]);
};
const seen = new Set();
function walk(dir) {
let ents=[];
try { ents = fs.readdirSync(dir,{withFileTypes:true}); } catch { return; }
for (const e of ents) {
const p = path.join(dir,e.name);
if (e.isDirectory()) {
if (['.git','proc','sys','dev'].includes(e.name)) continue;
if (e.name === 'vm2' && dir.endsWith('node_modules')) {
try {
const pkg = JSON.parse(fs.readFileSync(path.join(p,'package.json'),'utf8'));
if (!seen.has(p)) { seen.add(p); console.log(`${pkg.version} ${p} ${bad(pkg.version)?'AFFECTED':'ok'}`); }
} catch {}
}
walk(p);
}
}
}
walk(root);
NODE
# Lockfile and audit context; run from each application repo as well
find . -name package-lock.json -o -name yarn.lock -o -name pnpm-lock.yaml | while read -r f; do echo '--- lockfile:' "$f"; grep -n '"vm2"\|vm2@' "$f" | head -50 || true; done
npm ls vm2 --all 2>/dev/null || true
npm audit --json 2>/dev/null | grep -i 'vm2\|CVE-2026-92935' || true
Immediate containment actions
If you confirm vm2 3.11.4-3.11.6 in a network-reachable service, do not wait for perfect certainty.
- Freeze deployment of new user-supplied JavaScript, plugins, themes, expressions, and workflow code until exposure is understood.
- Disable or gate the vulnerable execution path behind admin-only access, strict schema validation, and allowlisted tenants.
- Reject non-object
requirevalues at the application boundary. Specifically deny arrays, strings, numbers, booleans, and null unless the expected API explicitly requires them. Treatrequire: []as suspicious input, not as a harmless empty config. - Run the Node service with least privilege: non-root user, read-only filesystem, no mounted cloud credentials, no Docker socket, restricted Linux capabilities, seccomp/AppArmor/SELinux where available, and no direct metadata endpoint reachability.
- Egress-filter Node workloads. A sandbox that cannot initiate new outbound connections sharply reduces post-exploitation value.
- Isolate execution in a separate worker, container, VM, or microVM with short lifetime and no shared secrets. Do not rely on vm2 as the only barrier.
- Rotate secrets reachable by affected Node processes if the service executed untrusted code while vulnerable: cloud keys, database credentials, JWT signing keys, npm tokens, CI variables, and service-account credentials.
Remediation
- Inventory now. Enumerate every repo, image, container, serverless artifact, and CI runner for vm2. Use package manifests, lockfiles, SBOMs,
node_modules, container layers, and runtime process command lines. Do not trust top-levelpackage.json; transitive dependencies matter. - Upgrade or pin outside the affected range. The source identifies only affected versions >=3.11.4 <=3.11.6 and does not state a fixed version. Move to a vm2 release explicitly listed by the vendor as fixed for CVE-2026-92935, or pin to a version confirmed not affected by the vendor advisory. Verify after install with
npm ls vm2and lockfile diff review; do not rely on semver ranges alone. - Validate the fix in a representative harness. Add regression tests that submit array-shaped option values such as
require: [], nested arrays, objects with array prototypes, and JSON-normalized inputs. Assert that the application rejects invalid shapes before vm2 sees them. - Add defense in depth around module resolution. If business requirements allow, disable dynamic
requirefor untrusted code entirely. Otherwise use a strict allowlist of built-ins/modules, deny nesting/override paths unless explicitly needed, and fail closed on undefined resolver fields. - Harden runtime blast radius. Non-root execution, no ambient credentials, read-only rootfs, dropped capabilities, egress deny-by-default, separate identity per tenant/job, and short-lived sandbox workers.
- Hunt retrospectively. Review the last 30-90 days for Node child shells, rare egress, metadata access, writes to startup/persistence paths, unexpected npm/postinstall execution, new native module loads, and crashes in processes that load vm2.
- Monitor authoritative sources. Track NVD at https://nvd.nist.gov/vuln/detail/CVE-2026-92935, the vm2 project security advisories/releases, your upstream framework vendors, and CISA KEV. If KEV listing or confirmed exploitation appears, escalate to emergency change and treat exposed secrets as compromised.
Executive takeaway
This is a critical parser-trust-boundary flaw in a component whose entire purpose is to contain hostile code. The practical move is not to debate CVSS vectors; it is to find vm2, prove the version, choke off untrusted JavaScript execution paths, remove array-shaped option ambiguity, constrain the Node runtime, and hunt for signs that the sandbox was treated as stronger than it was.
Related Resources
Security Arsenal Penetration Testing Services AlertMonitor Platform Book a SOC Assessment vulnerability-management Intel Hub
Is your security operations ready?
Get a free SOC assessment or see how AlertMonitor cuts through alert noise with automated triage.