
vm2 v3.11.6
Изолированная JavaScript-песочница для Node.js, которая выполняет недоверенный код с ограниченным доступом к встроенным модулям и ресурсам хоста посредством перехвата на основе Proxy.
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 — это песочница, которая может запускать недоверенный код с использованием разрешённых встроенных модулей Node.
Установка```sh
npm install vm2
## Quick Examples```js
import { VM } from 'vm2';
const vm = new VM();
vm.run(`process.exit()`); // TypeError: process.exit is not a function
I need the actual content of chunk 5 to translate it. Please provide the Markdown text you want translated.```js 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',
);
## Важное предупреждение о безопасности
**Прежде чем использовать vm2, вы должны понимать, как он работает и каковы его ограничения.**
vm2 пытается изолировать недоверенный JavaScript-код **в том же процессе Node.js**, что и ваше приложение. Это достигается за счёт сложной сети [Proxy](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Proxy), которые перехватывают и опосредуют каждое взаимодействие между песочницей и хост-средой.
### Фундаментальная проблема
JavaScript — чрезвычайно динамичный язык. Доступ к объектам можно получить через цепочки прототипов, до конструкторов можно добраться через объекты ошибок, символы предоставляют протокольные хуки, а асинхронное выполнение создаёт временные окна. Огромное количество способов перехода от одного объекта к другому в JavaScript делает создание герметичной внутрипроцессной песочницы крайне сложной задачей.
**Мы честны в отношении этой реальности:** несмотря на все наши усилия, исследователи и специалисты по безопасности постоянно обнаруживают новые способы обхода песочницы vm2. Мы активно исправляем эти уязвимости по мере их поступления, но природа «кошки-мышки» внутрипроцессной изоляции означает, что:
1. **В будущем, скорее всего, будут обнаружены новые обходы.** Проверьте наши [бюллетени безопасности](https://github.com/patriksimek/vm2/security/advisories) на предмет известных уязвимостей.
2. **Вы должны поддерживать vm2 в актуальном состоянии**, чтобы получать последние исправления безопасности. Подпишитесь на бюллетени безопасности и своевременно обновляйтесь.
3. **vm2 не должен быть вашей единственной линией обороны.** Эшелонированная защита необходима при запуске недоверенного кода.
### Более надёжные альтернативы
Если вам требуются более строгие гарантии изоляции, рассмотрите следующие альтернативы, обеспечивающие **настоящую изоляцию на уровне процесса или оборудования**:
| Решение | Подход | Производительность | Компромиссы |
|----------|----------|-------------|------------|
| **[isolated-vm](https://github.com/laverdet/isolated-vm)** | Отдельные изоляты V8 (отдельная куча V8) | Высокая | В режиме сопровождения; требует ручного обновления V8 |
| **Отдельный процесс / Worker** | `child_process` или Worker-потоки с ограниченными правами | Средняя | Более высокие накладные расходы на IPC; данные должны быть сериализованы |
| **Контейнеры / ВМ** | Docker, gVisor, Firecracker | Низкая | Накладные расходы на запуск; ресурсоёмкость |
| **Управляемые сервисы** | Облачное выполнение кода (например, AWS Lambda, Cloudflare Workers) | Переменная | Сетевая задержка; внешняя зависимость |
### Когда vm2 всё ещё может быть уместен
vm2 может подходить, когда:
- Вам нужна тесная интеграция с объектами хоста и быстрое синхронное взаимодействие
- Недоверенный код поступает из относительно надёжного источника (например, внутренние инструменты, плагинные системы с проверенными авторами)
- Вы комбинируете vm2 с другими уровнями безопасности (сетевая изоляция, ограничения файловой системы, лимиты ресурсов)
- Вы принимаете риск и активно отслеживаете обновления безопасности
**Если вы запускаете код из полностью недоверенных источников (например, произвольные пользовательские отправки), мы настоятельно рекомендуем использовать решение с более строгими гарантиями изоляции.**
## Среды выполнения
| Среда | Статус |
|---------|--------|
| Node.js | Поддерживается. Песочница является границей безопасности. |
| Bun | **Экспериментально.** Частичная функциональная совместимость — **не** является границей безопасности. |
К Bun применяются два отдельных ограничения, и ни одно из них не подразумевает другое.
**Это не граница безопасности.** Модель угроз vm2, каталог атак в
[`docs/ATTACKS.md`](https://github.com/patriksimek/vm2/blob/main/docs/ATTACKS.md) и каждый регрессионный тест в `test/ghsa/`
основаны на внутренностях V8. JavaScriptCore, который использует Bun, имеет свои
собственные аналоги, и ни один из них не был проверен на соответствие мосту vm2. Прохождение
набора тестов под Bun демонстрирует совместимость, а не то, что песочница там удерживается. **Не
используйте vm2 на Bun для изоляции недоверенного кода.**
**Совместимость частичная, а не полная.** Зелёный прогон Bun охватывает только те тесты,
которые фактически выполняются там. `test/bun-skips.js` перечисляет, что исключено и почему,
а известные поведенческие пробелы включают:
- `Buffer.from(arrayLike)` возвращает буфер нулевой длины
- Метаданные `VMScript` `filename` / `lineOffset` / `columnOffset` не
наблюдаемы, поскольку объекты CallSite в JSC не содержат методов
- `Object.freeze` на замороженном объекте хоста с неконфигурируемым аксессором
выбрасывает `TypeError` нарушения инварианта прокси там, где V8 этого не делает
- некоторые операции `Buffer` через границу песочницы выполняются значительно медленнее —
`allocUnsafe` на 64 МБ занимает более 400 секунд против 1,7 на Node, что достаточно медленно,
чтобы восприниматься как зависание
Относитесь к поддержке Bun как к совместимости «наилучшим образом» для доверенного кода и проверяйте
список исключений, прежде чем полагаться на какое-либо конкретное поведение.
## Возможности
- Безопасно выполняет недоверенный код в одном процессе рядом с вашим кодом
- Полный контроль над выводом консоли песочницы
- Песочница имеет ограниченный доступ к методам процесса
- Есть возможность подключать модули (встроенные и внешние) из песочницы
- Можно ограничить доступ к определённым (или всем) встроенным модулям
- Можно безопасно вызывать методы и обмениваться данными и обратными вызовами между песочницами
- Активно поддерживается с исправлениями известных методов обхода (см. [Предупреждение о безопасности](#important-security-disclaimer))
- Поддержка транспилятора
## Как это работает
- Использует внутренний модуль VM для создания безопасного контекста.
- Использует [Proxy](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Proxy) для предотвращения выхода из песочницы.
- Переопределяет встроенный require для контроля доступа к модулям.
Для подробного изучения внутренностей vm2 см. [docs/ATTACKS.md](https://github.com/patriksimek/vm2/blob/main/docs/ATTACKS.md).
## В чём разница между vm из Node и vm2?
Попробуйте сами:```js
import { runInNewContext } from "node:vm";
runInNewContext('this.constructor.constructor("return process")().exit()');
console.log('Never gets executed.');
3.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.```js
import { VM } from 'vm2';
new VM().run('this.constructor.constructor("return process")().exit()'); // Throws ReferenceError: process is not defined
## Документация
- [VM](#vm)
- [NodeVM](#nodevm)
- [VMScript](#vmscript)
- [Обработка ошибок](#error-handling)
- [Отладка кода в песочнице](#debugging-a-sandboxed-code)
- [Объекты только для чтения](#read-only-objects-experimental)
- [Защищённые объекты](#protected-objects-experimental)
- [Связи между песочницами](#cross-sandbox-relationships)
- [CLI](#cli)
- [Изменения с 2.x на 3.x](https://github.com/patriksimek/vm2/wiki/2.x-to-3.x-changes)
- [Документация 1.x и 2.x](https://github.com/patriksimek/vm2/wiki/1.x-and-2.x-docs)
- [Участие в разработке](https://github.com/patriksimek/vm2/wiki/Contributing)
## VM
VM — это простая песочница для синхронного выполнения недоверенного кода без функции `require`. Доступны только встроенные объекты JavaScript и `Buffer` из Node. Функции планирования (`setInterval`, `setTimeout` и `setImmediate`) по умолчанию недоступны.
**Параметры:**
- `timeout` — тайм-аут выполнения скрипта в миллисекундах. **ВНИМАНИЕ**: Возможно, вам стоит использовать этот параметр вместе с `allowAsync=false`. Кроме того, работа с объектами, возвращёнными из песочницы, может выполнять произвольный код и обходить тайм-аут. Следует проверять, является ли возвращённый объект примитивом с помощью `typeof`, и полностью отбрасывать его (ведение журнала или создание сообщений об ошибках с таким объектом также может снова выполнить произвольный код) в противном случае.
- `sandbox` — глобальный объект VM.
- `compiler` — `javascript` (по умолчанию), `typescript`, `coffeescript` или пользовательская функция компилятора. Библиотека ожидает, что компилятор будет предустановлен, если значение установлено в `typescript` или `coffeescript`. **`typescript` требует `typescript@6` или более раннюю версию** — см. [Компиляторы](#compilers).
- `eval` — если установлено в `false`, любые вызовы `eval` или конструкторов функций (`Function`, `GeneratorFunction` и т. д.) будут вызывать `EvalError` (по умолчанию: `true`).
- `wasm` — если установлено в `false`, любая попытка скомпилировать модуль WebAssembly вызовет `WebAssembly.CompileError` (по умолчанию: `true`). Примечание: `WebAssembly.JSTag` удаляется внутри песочницы по соображениям безопасности, поэтому код wasm не может перехватывать исключения JavaScript.
- `allowAsync` — если установлено в `false`, любая попытка выполнить код с использованием `async` вызовет `VMError` (по умолчанию: `true`).
- `bufferAllocLimit` — максимальный размер в байтах для одного запроса `Buffer.alloc` / `Buffer.allocUnsafe` / `Buffer.allocUnsafeSlow` / `Buffer(N)` / `new Buffer(N)` изнутри песочницы. Запросы, превышающие этот предел, синхронно вызывают `RangeError` без выполнения выделения памяти на хосте. По умолчанию: `Infinity` (без ограничения, полностью обратно совместимо). Разработчикам, запускающим недоверенный код в средах с ограниченной памятью (Docker / Kubernetes / Lambda / serverless), следует выбирать конечный предел (например, `32 * 1024 * 1024`) как часть многоуровневой защиты от DoS, так же как они выбирают `timeout`. См. [Рекомендации по усилению защиты](#hardening-recommendations) ниже.
**ВАЖНО**: Тайм-аут действует только на синхронный код, который вы запускаете через `run`. Тайм-аут **НЕ** работает с любыми методами, возвращаемыми VM. Существуют ситуации, когда тайм-аут не работает — см. [#244](https://github.com/patriksimek/vm2/pull/244).```js
import { VM } from 'vm2';
const vm = new VM({
timeout: 1000,
allowAsync: false,
sandbox: {},
});
vm.run('process.exit()'); // throws ReferenceError: process is not defined
Вы также можете извлекать значения из виртуальной машины.```js let number = vm.run('1337'); // returns 1337
**СОВЕТ**: Дополнительные примеры использования см. в тестах.
## NodeVM
В отличие от `VM`, `NodeVM` позволяет подключать модули так же, как это делается в обычном контексте Node.
**Параметры:**
- `console` — `inherit` для включения консоли, `redirect` для перенаправления в события, `off` для отключения консоли (по умолчанию: `inherit`).
- `sandbox` — глобальный объект VM.
- `compiler` — `javascript` (по умолчанию), `typescript`, `coffeescript` или пользовательская функция компилятора (которая получает код и путь к его файлу). Библиотека ожидает, что компилятор будет предустановлен, если значение установлено в `typescript` или `coffeescript`. **`typescript` требует `typescript@6` или более раннюю версию** — см. [Компиляторы](#compilers).
- `eval` — если установлено в `false`, любые вызовы `eval` или конструкторов функций (`Function`, `GeneratorFunction` и т. д.) будут вызывать `EvalError` (по умолчанию: `true`).
- `wasm` — если установлено в `false`, любая попытка скомпилировать модуль WebAssembly вызовет `WebAssembly.CompileError` (по умолчанию: `true`). Примечание: `WebAssembly.JSTag` удаляется внутри песочницы по соображениям безопасности, поэтому код wasm не может перехватывать исключения JavaScript.
- `bufferAllocLimit` — та же семантика, что и в `VM` — максимальный размер в байтах для одного запроса семейства `Buffer.alloc` изнутри песочницы. По умолчанию: `Infinity`. См. [Рекомендации по усилению защиты](#hardening-recommendations).
- `sourceExtensions` — массив расширений файлов, которые следует рассматривать как исходный код (по умолчанию: `['js']`).
- `require` — `true`, объект или Resolver для включения метода `require` (по умолчанию: `false`).
- `require.external` — значения могут быть `true`, массивом разрешённых внешних модулей или объектом (по умолчанию: `false`). Все пути, соответствующие `/node_modules/${any_allowed_external_module}/(?!/node_modules/)`, разрешены для подключения.
- `require.external.modules` — массив разрешённых внешних модулей. Также поддерживает подстановочные знаки, поэтому указание, например, `['@scope/*-ver-??]`, позволит использовать все модули с именем вида `@scope/something-ver-aa`, `@scope/other-ver-11` и т. д. Подстановочный знак `*` не соответствует разделителям путей.
- `require.external.transitive` — логическое значение, указывающее, разрешены ли транзитивные зависимости внешних модулей (по умолчанию: `false`). **ПРЕДУПРЕЖДЕНИЕ**: Когда модуль подключается транзитивно, любой модуль затем может подключить его обычным образом, даже если это было невозможно до его загрузки.
- `require.builtin` — массив разрешённых встроенных модулей, принимает ["\*"] для всех (по умолчанию: нет). **ПРЕДУПРЕЖДЕНИЕ**: "\*" может быть опасен, так как могут быть добавлены новые встроенные модули.
- `require.root` — ограниченный путь(и), где можно подключать локальные модули (по умолчанию: любой путь).
- `require.mock` — коллекция имитируемых модулей (как внешних, так и встроенных).
- `require.context` — `host` (по умолчанию) для подключения модулей в хосте и проксирования их в песочницу. `sandbox` для загрузки, компиляции и подключения модулей в песочнице. `callback(moduleFilename, ext)` для динамического выбора контекста для каждого модуля. По умолчанию используется песочница, если ничего не указано. За исключением `events`, встроенные модули всегда подключаются в хосте и проксируются в песочницу.
- `require.import` — массив модулей для загрузки в NodeVM при запуске.
- `require.resolve` — дополнительная функция поиска на случай, если модуль не был найден в одном из традиционных путей поиска node.
- `require.customRequire` — используется вместо функции `require` для загрузки модулей из хоста.
- `require.strict` — `false`, чтобы не принудительно включать строгий режим для модулей, загружаемых через require (по умолчанию: `true`).
- `require.fs` — пользовательская реализация файловой системы.
- `nesting` — **ПРЕДУПРЕЖДЕНИЕ**: Разрешение этого является риском для безопасности, так как скрипты могут создать NodeVM, который сможет подключать любой модуль хоста. `true` для включения вложенности VM (по умолчанию: `false`).
- `wrapper` — `commonjs` (по умолчанию) для обёртывания скрипта в обёртку CommonJS, `none` для получения значения, возвращаемого скриптом.
- `argv` — массив, передаваемый в `process.argv`.
- `env` — объект, передаваемый в `process.env`.
- `strict` — `true` для загрузки модулей в строгом режиме (по умолчанию: `false`).
**ВАЖНО**: Тайм-аут не действует для NodeVM, поэтому он не защищён от `while (true) {}` или подобных злонамеренных конструкций.
**ПОМНИТЕ**: Чем больше модулей вы разрешаете, тем более уязвимой становится ваша песочница.```js
import { NodeVM } from 'vm2';
const vm = new NodeVM({
console: 'inherit',
sandbox: {},
require: {
external: true,
builtin: ['fs', 'path'],
root: './',
mock: {
fs: {
readFileSync: () => 'Nice try!',
},
},
},
});
// Sync
let functionInSandbox = vm.run('module.exports = function(who) { console.log("hello "+ who); }');
functionInSandbox('world');
// Async
let functionWithCallbackInSandbox = vm.run('module.exports = function(who, callback) { callback("hello "+ who); }');
functionWithCallbackInSandbox('world', greeting => {
console.log(greeting);
});
Когда wrapper установлен в none, NodeVM ведёт себя больше как VM для синхронного кода.```js
assert.ok(vm.run('return true') === true);
**СОВЕТ**: Дополнительные примеры использования см. в тестах.
### Загрузка модулей по относительному пути
Чтобы загружать модули по относительному пути, необходимо передать полный путь запускаемого скрипта вторым аргументом методу `run` объекта vm, если скрипт является строкой. Имя файла затем отображается в любых трассировках стека, создаваемых скриптом.```js
vm.run('require("foobar")', '/data/myvmscript.js');
Если скрипт, который вы запускаете, является VMScript, путь указывается в конструкторе VMScript.```js const script = new VMScript('require("foobar")', { filename: '/data/myvmscript.js' }); vm.run(script);
### Resolver
Резолвер можно создать с помощью `makeResolverFromLegacyOptions` и использовать для нескольких экземпляров `NodeVM`, что позволяет совместно использовать скомпилированный код модулей и потенциально ускорить время загрузки. Первый пример `NodeVM` можно переписать с использованием `makeResolverFromLegacyOptions` следующим образом.```js
const resolver = makeResolverFromLegacyOptions({
external: true,
builtin: ['fs', 'path'],
root: './',
mock: {
fs: {
readFileSync: () => 'Nice try!',
},
},
});
const vm = new NodeVM({
console: 'inherit',
sandbox: {},
require: resolver,
});
VMScript
Вы можете повысить производительность, используя предварительно скомпилированные скрипты. Предварительно скомпилированный VMScript можно запускать несколько раз. Важно отметить, что код не привязан к какой-либо VM (контексту); скорее, он привязывается перед каждым запуском, только для этого запуска.```js import { VM, VMScript } from 'vm2';
const vm = new VM(); const script = new VMScript('Math.random()'); console.log(vm.run(script)); console.log(vm.run(script));
Работает как для `VM`, так и для `NodeVM`.```js
import { NodeVM, VMScript } from 'vm2';
const vm = new NodeVM();
const script = new VMScript('module.exports = Math.random()');
console.log(vm.run(script));
console.log(vm.run(script));
Код компилируется автоматически при первом запуске. Скомпилировать код можно в любой момент с помощью script.compile(). После компиляции метод не оказывает никакого эффекта.
Компиляторы
compiler принимает javascript (по умолчанию), typescript, coffeescript или вашу собственную функцию. Компиляторы typescript и coffeescript являются опциональными — установите пакет самостоятельно; vm2 не зависит ни от одного из них.
TypeScript
Встроенный компилятор typescript требует typescript@6 или более раннюю версию.
vm2 выполняет транспиляцию через API transpileModule() TypeScript. TypeScript 7 удалил его из точки входа пакета — require('typescript') там теперь возвращает только { version, versionMajorMinor }, а заменяющий API находится за явно нестабильными подпутями typescript/unstable/*, ни один из которых не предоставляет эквивалента однофайловой транспиляции. Поэтому vm2 не на что опереться в версии 7.x.
Выбор compiler: 'typescript' с установленным TypeScript 7 вызывает исключение при new VMScript(...) / new VM(...):```
VMError: The installed TypeScript (7.0.2) does not expose the transpileModule() API that
vm2's built-in TypeScript compiler uses; it was removed from the package entry point in
TypeScript 7. Install typescript@6 or earlier, or pass your own transpiler as a function:
{ compiler: (code, filename) => javaScriptSource }.
Либо закрепите `typescript@6`, либо укажите собственный транспилятор — подойдёт любая функция, возвращающая JavaScript, поэтому `tsc` из TypeScript 7, esbuild, swc или type-stripper — всё это допустимо:```js
import { VM, VMScript } from 'vm2';
import { transformSync } from 'esbuild';
const script = new VMScript('const x: number = 1; x', {
compiler: (code, filename) => transformSync(code, { loader: 'ts', format: 'cjs' }).code,
});
new VM().run(script);
A custom compiler receives (code, filename) and must return JavaScript source. It runs in the host realm, before sandboxing — treat it as trusted code and never build one out of untrusted input.
CoffeeScript
Requires coffee-script to be installed. Compiled with { header: false, bare: true }; any compilerOptions you pass are merged over those.
Error handling
Errors in code compilation and synchronous code execution can be handled by try-catch. Errors in asynchronous code execution can be handled by attaching uncaughtException event handler to Node's process.```js
try {
var script = new VMScript('Math.random()').compile();
} catch (err) {
console.error('Failed to compile script.', err);
}
try { vm.run(script); } catch (err) { console.error('Failed to execute script.', err); }
process.on('uncaughtException', err => { console.error('Asynchronous error caught.', err); });
## Отладка кода в песочнице
Вы можете отлаживать или проверять код, выполняемый в песочнице, так же, как если бы он выполнялся в обычном процессе.
- Вы можете использовать точки останова (для этого необходимо указать имя файла скрипта)
- Вы можете использовать ключевое слово `debugger`.
- Вы можете использовать шаг с заходом, чтобы войти внутрь кода, выполняемого в песочнице.
### Пример
/tmp/main.js:```js
import { VM, VMScript } from 'vm2';
import { readFileSync } from 'node:fs';
const file = `${__dirname}/sandbox.js`;
// By providing a file name as second argument you enable breakpoints
const script = new VMScript(readFileSync(file), file);
new VM().run(script);
/tmp/sandbox.js```js const foo = 'ahoj';
// The debugger keyword works just fine everywhere. // Even without specifying a file name to the VMScript object. debugger;
## Объекты только для чтения (экспериментально)
Чтобы предотвратить добавление, изменение или удаление свойств проксированных объектов из песочницы, можно использовать методы `freeze` для того, чтобы сделать объект доступным только для чтения. Это работает только внутри VM. Замороженные объекты затрагиваются глубоко. Примитивные типы заморозить нельзя.
**Пример без использования `freeze`:**```js
const util = {
add: (a, b) => a + b,
};
const vm = new VM({
sandbox: { util },
});
vm.run('util.add = (a, b) => a - b');
console.log(util.add(1, 1)); // returns 0
Пример использования freeze:```js
const vm = new VM(); // Objects specified in the sandbox cannot be frozen.
vm.freeze(util, 'util'); // Second argument adds object to global.
vm.run('util.add = (a, b) => a - b'); // Fails silently when not in strict mode. console.log(util.add(1, 1)); // returns 2
**ВАЖНО:** Невозможно заморозить объекты, которые уже были переданы в VM через прокси.
## Защищённые объекты (экспериментально)
В отличие от `freeze`, этот метод позволяет скриптам в песочнице добавлять, изменять или удалять свойства объектов, с одним исключением — невозможно прикреплять функции. Таким образом, скрипты в песочнице не могут изменять такие методы, как `toJSON`, `toString` или `inspect`.
**ВАЖНО:** Невозможно защитить объекты, которые уже были переданы в VM через прокси.
## Связи между песочницами```js
const assert = require('assert');
const { VM } = require('vm2');
const sandbox = {
object: new Object(),
func: new Function(),
buffer: new Buffer([0x01, 0x05]),
};
const vm = new VM({ sandbox });
assert.ok(vm.run(`object`) === sandbox.object);
assert.ok(vm.run(`object instanceof Object`));
assert.ok(vm.run(`object`) instanceof Object);
assert.ok(vm.run(`object.__proto__ === Object.prototype`));
assert.ok(vm.run(`object`).__proto__ === Object.prototype);
assert.ok(vm.run(`func`) === sandbox.func);
assert.ok(vm.run(`func instanceof Function`));
assert.ok(vm.run(`func`) instanceof Function);
assert.ok(vm.run(`func.__proto__ === Function.prototype`));
assert.ok(vm.run(`func`).__proto__ === Function.prototype);
assert.ok(vm.run(`new func() instanceof func`));
assert.ok(vm.run(`new func()`) instanceof sandbox.func);
assert.ok(vm.run(`new func().__proto__ === func.prototype`));
assert.ok(vm.run(`new func()`).__proto__ === sandbox.func.prototype);
assert.ok(vm.run(`buffer`) === sandbox.buffer);
assert.ok(vm.run(`buffer instanceof Buffer`));
assert.ok(vm.run(`buffer`) instanceof Buffer);
assert.ok(vm.run(`buffer.__proto__ === Buffer.prototype`));
assert.ok(vm.run(`buffer`).__proto__ === Buffer.prototype);
assert.ok(vm.run(`buffer.slice(0, 1) instanceof Buffer`));
assert.ok(vm.run(`buffer.slice(0, 1)`) instanceof Buffer);
CLI
Прежде чем использовать vm2 в командной строке, установите его глобально с помощью npm install vm2 -g.```sh
vm2 ./script.js
## Рекомендации по усилению безопасности
vm2 предотвращает побеги из песочницы (получение недоверенным кодом доступа к окружению хоста). Сама по себе она **не** предотвращает все формы исчерпания ресурсов или отказа в обслуживании. Разработчикам, запускающим недоверенный код, следует добавить следующие многоуровневые защиты вокруг песочницы.
### 1. Ограничьте выделение памяти с помощью `bufferAllocLimit`
Один вызов `Buffer.alloc(N)` с контролируемым атакующим `N` выполняется как одно синхронное выделение памяти в C++ на хосте, которое `timeout` V8 не может прервать. В средах с ограниченной памятью полезная нагрузка песочницы размером ~100 байт может вызвать скачок RSS хоста на 100 МБ+ и привести к сбою процесса хоста из-за нехватки памяти. Установите `bufferAllocLimit` (например, `32 * 1024 * 1024`), чтобы ограничить отдельные выделения памяти:```js
const vm = new VM({
timeout: 1000,
bufferAllocLimit: 32 * 1024 * 1024,
allowAsync: false,
});
Ограничение также распространяется на устаревшие пути Buffer(N) и new Buffer(N). Обратите внимание, что совокупное исчерпание (множество мелких выделений, Buffer.concat, Uint8Array, String.repeat, Array(n).fill() и т. д.) не покрывается этим ограничением — комбинируйте его с ограничением памяти на стороне хоста (--max-old-space-size, лимит контейнера, cgroup) для полного покрытия.
2. Установите обработчик unhandledRejection на стороне хоста
Существует класс DoS-атак с аварийным завершением хост-процесса, когда код в песочнице создает async function, async function* или await using, тело которого выбрасывает значение, вызывающее ошибку в области хоста при форматировании стека (например, e.name = Symbol(); e.stack). V8 создает promise отклонения через внутренний Promise области, что обходит обертку подкласса Promise в vm2, поэтому отклонение уходит на хост как unhandledRejection. На Node 15+ поведение по умолчанию — завершение процесса.
Закрытие этой уязвимости требует изменения наблюдаемого поведения хоста, поэтому vm2 не поставляет исправление по умолчанию. Встраивающие системы должны установить обработчик на уровне процесса, который подавляет (или логирует) отклонения, исходящие из песочницы:```js // Recommended: filter rejections that originated inside vm2 and swallow them, // while letting your own host-side rejections propagate. process.on('unhandledRejection', (reason, promise) => { // Heuristic: rejections from the sandbox frequently surface as values // without proper Error semantics, or with stacks pointing at vm.js. // Adjust the predicate to match your application. if (looksLikeSandboxOrigin(reason)) { return; // swallow — don't terminate the process } // Otherwise: handle (or rethrow) as normal for your host code. yourLogger.error('unhandled rejection', reason); });
Если в вашем приложении нет других источников необработанных отклонений, допустимо глобальное поглощение + логирование:```js
process.on('unhandledRejection', reason => {
yourLogger.warn('swallowed sandbox rejection', reason);
});
3. Запуск с ограничением памяти на уровне процесса
Даже при установленном bufferAllocLimit запускайте хост-процесс с флагом --max-old-space-size (или эквивалентным ограничением памяти контейнера), рассчитанным на рабочую нагрузку. Ограничение защищает от примитива единичного выделения памяти; ограничение на уровне ОС защищает от совокупного исчерпания памяти и от любых будущих примитивов выделения, которые vm2 ещё не ограничил.
4. Относитесь к require.builtin: ['*'] как к не-песочной конфигурации
Подстановочный знак '*' раскрывается в большинство встроенных модулей Node, включая child_process, fs, dgram, net, http и dns. Это полноценные примитивы с возможностями хост-системы — require('child_process').execSync('id') доступен из песочницы при '*'. Семантика '*' в vm2 намеренная (некоторые интеграторы запускают доверенный, но изолированный код), но её не следует использовать по умолчанию для недоверенного кода. Предпочтительнее явный белый список минимального набора модулей, которые действительно нужны вашей песочнице.
5. nesting: true — это запасной выход
nesting: true позволяет коду песочницы выполнять require('vm2') и создавать вложенные NodeVM. Конфигурация require вложенной VM выбирается кодом песочницы, который её создаёт, а не ограничивается внешней VM. Конкретно:```js
const vm = new NodeVM({ nesting: true, require: { builtin: [] } });
vm.run( const { NodeVM: NVM } = require('vm2'); // Inner VM's config is whatever the sandbox writes here: const inner = new NVM({ require: { builtin: ['child_process'] } }); inner.run('require("child_process").execSync("id")'); // RCE);
Если вы установите `nesting: true`, вы фактически предоставите песочнице тот же уровень доверия, что и у вас. **Не включайте `nesting: true` для недоверенного кода.** Используйте его только тогда, когда вы доверяете самому изолированному коду, но хотите семантику выполнения в стиле VM (свежий глобальный объект, контролируемые таймауты) по причинам, не связанным с безопасностью.
`nesting: true` **требует явного объекта конфигурации `require`** (например, `require: { builtin: [] }` или `require: {}`). Любая другая форма — `require: false`, `require: undefined`, `require: null` или полное опускание `require` — вызывает `VMError` при создании (GHSA-m4wx-m65x-ghrr, заменяет GHSA-8hg8-63c5-gwmx). Все эти формы создают резолвер только для NESTING_OVERRIDE: песочница может выполнить `require('vm2')`, но ничего больше, что является чистым примитивом для побега без какого-либо легитимного применения. Чтобы запретить все require, удалите `nesting: true`. Чтобы разрешить вложенные VM, предоставьте явную конфигурацию `require`, чтобы компромисс был виден в месте вызова.
## Известные проблемы
- Невозможно определить класс, который расширяет проксированный класс. Это включает использование проксированного класса в `Object.create`.
- Прямой `eval` не работает.
- При логировании массивов песочницы массив будет повторяться в свойствах.
- Трансформации исходного кода могут привести к другой строке исходника для функции.
- Существуют способы вызвать сбой процесса node изнутри песочницы. См. [Рекомендации по усилению защиты](#hardening-recommendations).
- Встроенный компилятор `typescript` не работает с TypeScript 7 или новее, поскольку в нём удалён API `transpileModule()`, который использует vm2. Используйте `typescript@6` или передайте собственный транспилятор — см. [Компиляторы](#compilers).
[npm-image]: https://img.shields.io/npm/v/vm2.svg
[npm-url]: https://www.npmjs.com/package/vm2
[license-image]: https://img.shields.io/npm/l/vm2.svg
[license-url]: https://raw.githubusercontent.com/patriksimek/vm2/resurrection/LICENSE.md
[downloads-image]: https://img.shields.io/npm/dm/vm2.svg
[downloads-url]: https://www.npmjs.com/package/vm2
[snyk-image]: https://snyk.io/test/github/patriksimek/vm2/badge.svg
[snyk-url]: https://snyk.io/test/github/patriksimek/vm2