devnotes.

The VM Module — Running Code in a Sandbox

Sometimes an application needs to run a piece of JavaScript that comes from somewhere untrusted — a user-submitted formula, a plugin script, code stored in a database. Running it with a plain eval() would let it touch anything your main program can touch, which is a serious risk. The vm module exists to give that code its own, restricted execution context instead.

Running code in a completely fresh, isolated context

const vm = require('vm');

const untrustedCode = 'result = 6 * 7';
const sandbox = { result: null };

vm.createContext(sandbox); // turns `sandbox` into a usable execution context
vm.runInContext(untrustedCode, sandbox);

console.log(sandbox.result); // 42

The code only ever sees the properties you explicitly put in sandbox — it has no access to your application's other variables, your file system, or your network, unless you deliberately expose them.

Reusing a compiled script across multiple runs

If the same code needs to run repeatedly (with different input data each time), compiling it once with vm.Script avoids re-parsing it every single time:

const vm = require('vm');

const script = new vm.Script('total = price * quantity');

function calculate(price, quantity) {
  const context = { price, quantity, total: null };
  vm.createContext(context);
  script.runInContext(context);
  return context.total;
}

console.log(calculate(50, 3));  // 150
console.log(calculate(20, 10)); // 200

A realistic example: a small formula-evaluation feature

function evaluateFormula(formula, variables) {
  const context = { ...variables, result: undefined };
  vm.createContext(context);
  try {
    vm.runInContext(`result = ${formula}`, context, { timeout: 1000 });
    return context.result;
  } catch (error) {
    throw new Error('Invalid formula: ' + error.message);
  }
}

console.log(evaluateFormula('basePrice * (1 + taxRate)', { basePrice: 100, taxRate: 0.1 }));
// 110

The timeout option is worth calling out — it stops runaway code (like an infinite loop a user might accidentally, or deliberately, submit) from hanging the whole process indefinitely.

An important honesty check: the vm module is not a full security boundary

This matters enough to state plainly: vm restricts what variables and objects the code can directly reach, but it is not a complete, bulletproof security sandbox on its own — cleverly crafted malicious code has historically found ways to escape a vm context and reach the surrounding Node.js process. For genuinely untrusted code (arbitrary user-submitted scripts, for instance), the safer real-world approach is running it in a fully separate, isolated process or container, not relying on vm alone as the only protection.

When it's the right tool

vm is well-suited for semi-trusted scenarios — evaluating a formula, running a plugin from a source you mostly trust, or building a simple scripting feature inside your own application — where you want isolation from your main program's variables, but aren't defending against a genuinely hostile, security-conscious attacker.

The VM Module — Running Code in a Sandbox | DevNotes