TL;DR: Vm2 has a critical 9.8 CVSS vulnerability that lets attackers bypass promise sanitization and execute arbitrary code inside the Node.js process, affecting versions 3.10.1 and earlier, according to Semgrep. The case reinforces that code sanitization is not a substitute for real isolation when untrusted input can reach execution paths.
At a glance
What this is: Semgrep reports that vm2’s promise sanitization can be bypassed, allowing arbitrary code execution inside the host Node.js process.
Why it matters: This matters because teams using in-process sandboxes for untrusted JavaScript need to treat isolation as the control objective, not sanitization alone.
Context
Node.js sandboxing is meant to constrain untrusted code without giving it the same privileges as the parent application. The problem in this case is that promise handling created a fail-open path, so the sandbox boundary could be crossed even when sanitization was present. For teams running user-supplied code, that makes the trust boundary much thinner than it appears.
This is not only an application security concern. When a runtime sandbox is used to execute untrusted code, the boundary becomes part of the application’s control plane, and any bypass turns into a privilege and execution problem. The identity angle is indirect but real: the process identity, its filesystem reach, and any service credentials available to that process become exposed once the sandbox is escaped.
Key questions
Q: What breaks when a JavaScript sandbox fails to sanitise all Promise paths?
A: The entire containment model breaks, because untrusted code can cross from restricted execution into host-level execution through the unsanitised path. In practice, that means the sandbox no longer limits what attacker-controlled code can access, and the issue becomes arbitrary code execution rather than a confined runtime error.
Q: Why do in-process sandboxes increase the impact of untrusted code bugs?
A: Because the guest code and the application share the same runtime. If the sandbox is escaped, the attacker does not need a second foothold. The same process identity, memory space, and environment become available, which turns a local bypass into a broad execution and data exposure problem.
Q: How can security teams tell whether a sandbox model is too fragile for production use?
A: Look for repeated bypasses, reliance on language-level interception, and any design where hostile code can influence asynchronous control flow. If the control depends on perfect filtering inside the same process it is defending, the model is fragile. Stronger isolation should be the default for untrusted execution.
Q: When should teams choose containerised or isolate-based execution over sanitisation?
A: Choose stronger isolation when untrusted code can originate from users, plugins, templates, or any path that might later become externally reachable. Sanitisation may still help, but it should not be the only barrier. Once the application could face arbitrary input, the containment boundary must sit outside the application process.
Technical breakdown
How promise sanitization failed in vm2
vm2 used JavaScript proxies and helper functions to constrain dangerous operations inside the sandbox. The flaw described by Semgrep was a fail-open case in promise catch handling, where global promise handlers were not wrapped the same way as local ones. That created a path for attacker-controlled code to hoist into global scope and bypass the intended sanitization layer. In practical terms, the sandbox was relying on interception logic that did not consistently apply to all execution contexts, which is exactly the kind of gap that turns a logical boundary into a bypassable one.
Practical implication: Treat partial sanitization as an unsafe boundary when untrusted code can influence asynchronous control flow.
Why in-process sandboxing is fragile for untrusted code
In-process sandboxes run untrusted code inside the same Node.js process as the application. That means any escape immediately inherits the host process’s memory, filesystem access, environment variables, and available privileges. The article’s example shows how a simple promise handler can chain into arbitrary function construction and command execution. This is not just a bug in one library. It is a structural limitation of designs that try to police hostile code from inside the same execution context they are trying to protect.
Practical implication: Use runtime separation or container isolation when untrusted code can affect production systems.
What a real isolation boundary changes
A stronger design moves the trust boundary out of the application process. Native isolation primitives, separate containers, or purpose-built execution sandboxes reduce the blast radius if the guest code misbehaves or escapes local policy. The key difference is that the isolation boundary is enforced by the runtime or operating system, not by JavaScript-level hooks that attacker-controlled code may bypass. That distinction matters whenever a product accepts user-submitted scripts, plugins, templates, or expressions that can evolve from internal use into externally reachable input.
Practical implication: Re-evaluate any architecture that assumes code sanitization alone is sufficient to contain untrusted execution.
Threat narrative
Attacker objective: Execute arbitrary code inside the host Node.js process and expand from sandboxed input to full process-level control.
- Entry occurs when attacker-controlled JavaScript reaches the vm2 sandbox through an input path intended to run untrusted code.
- Credential access and execution follow when the promise catch bypass lets the attacker construct arbitrary functions and invoke host process capabilities.
- Impact is remote code execution inside the Node.js process, which can expose memory, files, and any privileges held by that process.
Breaches seen in the wild
- Gladinet Hard-Coded Keys RCE Exploitation: Actively exploited hard-coded keys in Gladinet CentreStack and Triofox enable remote code execution.
Read and download The State of NHI & AI Agent Breach Report 2026, covering 200+ breaches impacting Non-Human Identities including AI Agents.
NHI Mgmt Group analysis
Promise sanitization is not a trust boundary: the article shows that asynchronous control flow can create fail-open paths even when a sandbox appears to be instrumented correctly. That matters because many application teams confuse input filtering with execution containment. The practical conclusion is that sanitization may reduce noise, but only isolation defines the security boundary.
In-process sandboxes create shared-risk identity exposure: once attacker code escapes vm2, it inherits the Node.js process identity, including any environment secrets or filesystem reach tied to that runtime. That makes sandbox escape a governance issue for machine credentials as much as an application issue. Teams that run code as a service need to review which service identities are exposed to the execution context.
Repeated sandbox escapes expose architectural debt: the article’s reference to multiple critical advisories in a short period points to a design that was being patched around rather than made safe. This is a classic case of control fragility, where the security model depends on perfect interception of hostile behavior. Practitioners should treat repeated bypasses as evidence that the containment model itself needs to change.
Node.js isolation must be evaluated as runtime design, not library choice: the article makes clear that the successor pattern is not another sanitizer but stronger separation such as native isolate boundaries or containerized execution. That shifts the question from patching to architecture. Security teams should judge any untrusted-code runtime by the blast radius of a successful escape, not by the number of filtering hooks it exposes.
What this signals
Runtime containment has to absorb failure, not assume correctness: if a sandbox can be escaped through one asynchronous edge case, the programme should assume more bypasses will follow. That means evaluating guest-code controls by blast radius and recovery cost, not by whether the library claims to sanitise input.
Machine identity exposure becomes immediate after escape: the Node.js process identity, its credentials, and any reachable secrets are all part of the exposed surface once the sandbox fails. Teams running code as a service should therefore treat runtime isolation as part of secret containment, not just application hardening.
For practitioners
- Upgrade vm2 to the fixed release Move all deployments using vm2 3.10.1 or earlier to version 3.10.2 and verify the affected package is not pinned indirectly through dependencies.
- Inventory every untrusted-code execution path Map where user-provided or externally influenced JavaScript, templates, or expressions can reach vm2 or similar sandboxes, including internal tools that may later become externally reachable.
- Replace in-process sandboxes for high-risk code Use containerized execution or native isolate-based separation where untrusted code must run, and treat JavaScript-level promise sanitization as a secondary safeguard only.
- Review process privileges before code execution Strip unnecessary filesystem, network, and environment access from the Node.js process so a sandbox escape does not immediately expose broader runtime assets.
- Add detection for post-exploitation behaviour Monitor for child-process creation, unusual command execution, and unexpected reads from process memory after sandboxed code runs.
Key takeaways
- The central issue is not a bad regex or a weak filter. It is a sandbox design that can be crossed from inside the same process it is supposed to protect.
- The article cites a 9.8 CVSS vulnerability and an earlier cluster of vm2 advisories, which shows a repeated escape pattern rather than an isolated defect.
- The right control response is stronger runtime isolation, because sanitisation alone cannot reliably contain untrusted code once execution paths are attacker-influenced.
Standards & Framework Alignment
This section maps relevant standards and security frameworks to the operational risks and controls described in this guidance.
MITRE ATT&CK addresses the attack and risk surface, while NIST CSF 2.0, OWASP ASVS and CIS Controls v8 set the governance and control requirements practitioners need to meet.
| Framework | Control / Reference | Relevance |
|---|---|---|
| MITRE ATT&CK | TA0002;TA0006;TA0040 — Execution; Credential Access; Impact | The article describes code execution after a sandbox bypass and the resulting host-level impact. |
| Recommendation — Map sandbox escape paths to ATT&CK execution and impact techniques, then hunt for child-process abuse after guest-code runs. | ||
| NIST CSF 2.0 | PR.AA-05 — Access Permissions, Entitlements and Authorizations | The failure is a runtime privilege boundary that allowed untrusted code to exceed intended access. |
| Recommendation — Reassess runtime entitlements so untrusted code cannot inherit privileges that exceed its intended execution scope. | ||
| OWASP ASVS | V15 — Secure Coding and Architecture | The issue is architectural containment failure rather than an input-validation problem. |
| Recommendation — Apply secure architecture requirements to separate untrusted execution from the main application process. | ||
| CIS Controls v8 | CIS-4 — Secure Configuration of Enterprise Assets and Software | The response depends on hardening the execution environment and reducing the blast radius of the Node.js process. |
| Recommendation — Harden runtime configuration so a compromised sandbox cannot reach unnecessary files, network paths, or environment secrets. | ||
Key terms
- Sandbox Escape: A sandbox escape is when code breaks out of its intended isolation boundary and gains access to host capabilities. In identity terms, it turns a constrained non-human execution path into a privileged runtime that can touch files, secrets, or downstream systems.
- Promise sanitization: Promise sanitization is the attempt to strip or control dangerous behaviour when JavaScript promises handle errors or callbacks. In practice, it is a defensive wrapper, not a hard boundary, and it fails if runtime behaviour or scope handling lets attackers route around the guardrail.
- In-Process Isolation: In-process isolation keeps untrusted code inside the same runtime as the application but attempts to restrict what it can do. It offers convenience, but it also concentrates risk because a bypass immediately exposes the host process instead of a separate execution boundary.
- Blast Radius: The potential scope of damage if a specific credential or identity is compromised. Identities with broad permissions have a larger blast radius and represent a higher priority for least-privilege enforcement and security controls.
Deepen your knowledge
NHI Mgmt Group covers identity security, NHI governance, and agentic AI through independent research, practitioner guides, and the NHI Foundation Level course, the industry's only accredited NHI security programme. It is suitable for practitioners who need to connect identity governance to broader security decisions.
Published by the NHIMG editorial team on August 2, 2026.
Updated on October 12, 2026.
NHI Mgmt Group, the independent authority on Non-Human Identity, IAM, and Agentic AI security. nhimg.org