
vm2 v3.11.6
Isolated JavaScript sandbox for Node.js that runs untrusted code with restricted access to built-in modules and host resources via Proxy-based interception.
vm2 [![NPM Version][npm-image]][npm-url] [![NPM Downloads][downloads-image]][downloads-url] [![License][license-image]][license-url]
[![Known Vulnerabilities][snyk-image]][snyk-url]
vm2 is a sandbox that can run untrusted code with whitelisted Node's built-in modules.
Installation
npm install vm2
Quick Examples
import { VM } from 'vm2';
const vm = new VM();
vm.run(`process.exit()`); // TypeError: process.exit is not a function
import { NodeVM } from 'vm2';
const vm = new NodeVM({
require: {
external: true,
root: './',
},
});
vm.run(
`
var request = require('request');
request('http://www.google.com', function (error, response, body) {
console.error(error);
if (!error && response.statusCode == 200) {
console.log(body); // Show the HTML for the Google homepage.
}
});
`,
'vm.js',
);
Important Security Disclaimer
Before using vm2, you should understand how it works and its limitations.
vm2 attempts to sandbox untrusted JavaScript code within the same Node.js process as your application. It does this through a complex network of Proxies that intercept and mediate every interaction between the sandbox and the host environment.
The Fundamental Challenge
JavaScript is an extraordinarily dynamic language. Objects can be accessed through prototype chains, constructors can be reached via error objects, symbols provide protocol hooks, and async execution creates timing windows. The sheer number of ways to traverse from one object to another in JavaScript makes building an airtight in-process sandbox extremely difficult.
We are honest about this reality: Despite our best efforts, researchers and security professionals continuously discover new ways to escape the vm2 sandbox. We actively patch these vulnerabilities as they are reported, but the cat-and-mouse nature of in-process sandboxing means that:
- New bypasses will likely be discovered in the future. Check our security advisories for known vulnerabilities.
- You must keep vm2 updated to benefit from the latest security fixes. Subscribe to security advisories and update promptly.
- vm2 should not be your only line of defense. Defense in depth is essential when running untrusted code.
More Robust Alternatives
If you require stronger isolation guarantees, consider these alternatives that provide true process or hardware-level isolation:
| Solution | Approach | Performance | Trade-offs |
|---|---|---|---|
| isolated-vm | Separate V8 isolates (different V8 heap) | Fast | In maintenance mode; requires manual V8 updates |
| Separate process / Worker | child_process or Worker threads with limited permissions | Medium | Higher IPC overhead; data must be serialized |
| Containers / VMs | Docker, gVisor, Firecracker | Slow | Startup overhead; resource-heavy |
| Managed services | Cloud-based code execution (e.g., AWS Lambda, Cloudflare Workers) | Variable | Network latency; external dependency |
When vm2 May Still Be Appropriate
vm2 can be suitable when:
- You need tight integration with host objects and fast synchronous communication
- The untrusted code comes from a relatively trusted source (e.g., internal tools, plugin systems with vetted authors)
- You combine vm2 with other security layers (network isolation, filesystem restrictions, resource limits)
- You accept the risk and actively monitor for security updates
If you're running code from completely untrusted sources (e.g., arbitrary user submissions), we strongly recommend using a solution with stronger isolation guarantees.
Runtimes
| Runtime | Status |
|---|---|
| Node.js | Supported. The sandbox is a security boundary. |
| Bun | Experimental. Partial functional compatibility — not a security boundary. |
Two separate limitations apply to Bun, and neither implies the other.
It is not a security boundary. vm2's threat model, the attack catalogue in
docs/ATTACKS.md, and every regression test in test/ghsa/
are derived from V8 internals. JavaScriptCore, which Bun uses, has its own
equivalents, and none have been audited against vm2's bridge. The suite passing
under Bun demonstrates compatibility, not that the sandbox holds there. Do not
use vm2 on Bun to isolate untrusted code.
Compatibility is partial, not parity. A green Bun run covers only the tests
that actually execute there. test/bun-skips.js lists what is excluded and why,
and the known behavioural gaps include:
Buffer.from(arrayLike)returns a zero-length bufferVMScriptfilename/lineOffset/columnOffsetmetadata is not observable, because JSC's CallSite objects carry no methodsObject.freezeon a frozen host object with a non-configurable accessor throws a proxy-invariantTypeErrorwhere V8 does not- some
Bufferoperations across the sandbox boundary are drastically slower — a 64 MBallocUnsafetakes over 400 seconds against 1.7 on Node, slow enough to read as a hang
Treat Bun support as best-effort compatibility for trusted code, and check the skip list before relying on any particular behaviour.
Features
- Runs untrusted code securely in a single process with your code side by side
- Full control over the sandbox's console output
- The sandbox has limited access to the process's methods
- It is possible to require modules (built-in and external) from the sandbox
- You can limit access to certain (or all) built-in modules
- You can securely call methods and exchange data and callbacks between sandboxes
- Actively maintained with patches for known escape methods (see Security Disclaimer)
- Transpiler support
How does it work
- It uses the internal VM module to create a secure context.
- It uses Proxies to prevent escaping from the sandbox.
- It overrides the built-in require to control access to modules.
For an in-depth look at vm2’s internals, see docs/ATTACKS.md.
What is the difference between Node's vm and vm2?
Try it yourself:
import { runInNewContext } from "node:vm";
runInNewContext('this.constructor.constructor("return process")().exit()');
console.log('Never gets executed.');
import { VM } from 'vm2';
new VM().run('this.constructor.constructor("return process")().exit()');
// Throws ReferenceError: process is not defined
Documentation
- VM
- NodeVM
- VMScript
- Error handling
- Debugging a sandboxed code
- Read-only objects
- Protected objects
- Cross-sandbox relationships
- CLI
- 2.x to 3.x changes
- 1.x and 2.x docs
- Contributing
VM
VM is a simple sandbox to synchronously run untrusted code without the require feature. Only JavaScript built-in objects and Node's Buffer are available. Scheduling functions (setInterval, setTimeout and setImmediate) are not available by default.
Options: