Песочницы и VM в Node

Модуль vm позволяет компилировать и исполнять JavaScript в изолированном контексте — для плагинов, hot reload, sandbox-режимов и FaaS.

Суть

Каждый Node.js процесс делит один глобальный объект между модулями. Чтобы запустить ненадёжный/перезагружаемый код изолированно, используется vm.createContext — он создаёт отдельный JS-контекст со своим global, своими таймерами и своим API. Это НЕ безопасный sandbox от вредоносного кода (для этого нужны worker_threads/процессы), но полезно для:

  • горячей перезагрузки модулей (Metarhia: application code в vm)
  • DI: инжектируем нужное API внутрь контекста, остальное недоступно
  • перехвата console.log / process.exit в подсистемах
  • ограничения времени выполнения через timeout

Пример

const vm = require('vm');

const sandbox = {
  console,
  require: (id) => id === 'fs' ? require('fs') : null, // только fs разрешён
  result: null,
};
vm.createContext(sandbox);

const code = `
  const fs = require('fs');
  result = fs.readFileSync('./data.txt', 'utf8');
`;

vm.runInContext(code, sandbox, { timeout: 1000 });
console.log(sandbox.result);

Что важно

  • runInNewContext — каждый раз создаёт новый context (дорого, но изоляция полная)
  • runInContext — переиспользует уже созданный (быстрее)
  • runInThisContext — исполнить в текущем context (только timeout, без изоляции глобала)
  • new vm.Script(code).runInContext(ctx) — скомпилировать раз, исполнять много раз
  • timeout — kill при превышении (но не от async-кода)

Подводные камни

  • Это не security boundary — код в vm может require('process').exit если ему дать require
  • Объекты из основного контекста, переданные в sandbox, остаются доступны → утечка ссылок
  • Перезагрузка кода через delete require.cache[...] + vm.runInContext — хак, не пользоваться без необходимости
  • Для настоящей изоляции — worker_threads, child_process или Deno permissions

🎓 Источники

  • 🎓 [Модули, слои, структура проекта, песочницы] · 2018-10-02 · YouTube · [Marp](../../../Documents/TimurShemsedinov/2018-10-02 — Модули, слои, структура проекта, песочницы в JavaScript и Node.js (O7A9chb573E).md)
    • Тезисы: создание контекста, обёртка require внутри песочницы, рекурсивная context.global, инжект API, защита парсинга таймаутом, чтение exports из песочницы, framework запускает application через vm
    • Цитата: «fs заблокирован, net доступен — мы решаем что доступно из application»
  • 🎓 [Логирование на Node.js] · 2019-04-25 · YouTube
    • Тезисы: перехват console.log через VM-песочницу, прозрачная подмена для приложения
  • 🎓 [Песочницы, IoC, DI, IPC (Летняя школа 2017)] · 2019-11-30 · YouTube
    • Тезисы: песочницы как механизм IoC/DI, перезагрузка приложения без рестарта процесса
  • 🎓 [Serverless Clouds (FaaS) и изоляция контекстов запросов] · 2019-12-05 · YouTube
    • Тезисы: vm как лёгкая FaaS-альтернатива контейнерам, изоляция per-request

См. также