
CVE-2026-22709: Understanding a Critical Sandbox Escape in vm2
Introduction
Running untrusted JavaScript safely is a common requirement in modern platforms, such as online code runners, plugin systems, rule engines, and low-code tools. One of the most popular libraries for this purpose in the Node.js ecosystem is vm2.
CVE-2026-22709 exposes a critical flaw in vm2’s sandbox model that allows attackers to escape the sandbox and execute arbitrary code on the host system. This blog post explains what went wrong, why it matters, and what developers can learn from it.
What Is vm2?
vm2 is an open-source Node.js library that provides a sandboxed environment for executing JavaScript code. It is designed to:
- Restrict access to Node.js internals
- Prevent filesystem and process access
- Isolate untrusted code from the host application
Internally, vm2 relies on JavaScript language features like proxies, contexts, and object wrapping to enforce isolation.
Overview of CVE-2026-22709
- CVE ID: CVE-2026-22709
- Affected software: vm2
- Affected versions: All versions prior to 3.10.2
- Severity: Critical (CVSS approximately 9.8)
- Impact: Sandbox escape leading to arbitrary code execution
- Fixed in: vm2 version 3.10.2
At a high level, this vulnerability allows malicious code running inside the vm2 sandbox to break out and execute code with the same privileges as the Node.js process itself.
The Root Cause: Promise Callback Sanitization
Sanitization in vm2
vm2 attempts to protect the host by sanitizing callback functions passed into sensitive APIs. This includes callbacks passed to:
- Promise.prototype.then
- Promise.prototype.catch
Sanitization ensures that these callbacks cannot access dangerous objects like Function, process, or require.
Where It Went Wrong
The vulnerability comes from an incomplete sanitization strategy:
- localPromise.prototype.then was sanitized
- globalPromise.prototype.then was not sanitized
This distinction is critical.
Why Async Functions Matter
In JavaScript, all async functions return a global Promise object.
This means:
- Code inside the vm2 sandbox defines an async function
- Calling that function returns a global Promise
- Callbacks attached via .then() or .catch() are not sanitized
- The callback executes with access to unsafe constructors
This creates a clean and reliable sandbox escape path.
Impact: From Sandbox to System Access
By exploiting this flaw, an attacker can:
- Reach powerful constructors such as Function
- Reconstruct access to Node.js internals
- Execute arbitrary JavaScript on the host
- Potentially run system commands
In security terms, this is a remote code execution vulnerability for any application running untrusted code via vm2.
Who Is Affected?
You are at risk if your application:
- Uses vm2 versions earlier than 3.10.2
- Executes untrusted or user-supplied JavaScript
- Runs in environments such as:
- Online editors
- Plugin systems
- Automation engines
- SaaS platforms with scripting features
Even if your usage seems limited, any exposure to attacker-controlled code is enough.
The Fix
The vm2 maintainers fixed this issue in version 3.10.2 by ensuring that:
- Promise callbacks are sanitized consistently
- Global and local Promise behavior is aligned
Recommended Actions
- Upgrade immediately to vm2 version 3.10.2 or later
- Audit where and why you run untrusted code
- Consider defense-in-depth approaches, including:
- OS-level sandboxing such as containers or microVMs
- Dedicated isolation libraries like isolated-vm
Security Lessons Learned
1. JavaScript Is a Complex Attack Surface
Seemingly small differences, such as global versus local Promise objects, can have major security implications.
2. Language Features Can Bypass Security Assumptions
Async and await are language features, not library features. Security models must account for native behavior.
3. Sandboxing Is Extremely Hard
Pure JavaScript sandboxes are fragile. Every new language feature expands the attack surface.
4. Assume Escape Is Possible
When executing untrusted code:
- Minimize privileges
- Isolate at the OS or VM level
- Monitor and limit potential damage
Conclusion
CVE-2026-22709 is a textbook example of how subtle JavaScript semantics can undermine sandbox security. A single missed sanitization path, combined with async functions, was enough to completely break vm2’s isolation guarantees.
If you rely on vm2, upgrading is mandatory. More importantly, this vulnerability is a reminder that JavaScript sandboxing should never be your only line of defense.
