アップデート一覧に戻る
New releaseAug 15, 2026

vm2 v3.11.6

Node.js向けの隔離されたJavaScriptサンドボックス。Proxyベースのインターセプトにより、組み込みモジュールやホストリソースへのアクセスを制限しながら、信頼できないコードを実行します。

共有

vm2 [![NPM Version][npm-image]][npm-url] [![NPM Downloads][downloads-image]][downloads-url] [![License][license-image]][license-url] Node.js CI [![Known Vulnerabilities][snyk-image]][snyk-url]

vm2 は、ホワイトリストに登録された Node の組み込みモジュールを使用して信頼できないコードを実行できるサンドボックスです。

重要なセキュリティ免責事項

vm2 を使用する前に、その仕組みと制限事項を理解しておいてください。

vm2 は、アプリケーションと同じ Node.js プロセス内で、信頼できない JavaScript コードをサンドボックス化しようとします。これは、サンドボックスとホスト環境の間のすべてのやり取りを傍受して仲介する プロキシ の複雑なネットワークを通じて実現されます。

根本的な課題

JavaScript は非常に動的な言語です。オブジェクトはプロトタイプチェーンを通じてアクセスでき、コンストラクタはエラーオブジェクト経由で到達でき、シンボルはプロトコルフックを提供し、非同期実行はタイミングウィンドウを生み出します。JavaScript でオブジェクト間を移動する方法が非常に多いため、堅牢なプロセス内サンドボックスを構築することは極めて困難です。

私たちはこの現実について正直です: 最善の努力にもかかわらず、研究者やセキュリティ専門家は vm2 サンドボックスから脱出する新しい方法を継続的に発見しています。報告されたこれらの脆弱性には積極的にパッチを適用していますが、プロセス内サンドボックス化のいたちごっこ的な性質上、次のことが言えます:

  1. 新しいバイパス手法が将来的に発見される可能性が高いです。 既知の脆弱性については、セキュリティアドバイザリ を確認してください。
  2. vm2 を最新の状態に保つ必要があります 最新のセキュリティ修正の恩恵を受けるために。セキュリティアドバイザリを購読し、迅速に更新してください。
  3. vm2 を唯一の防御線とすべきではありません。 信頼できないコードを実行する際には、多層防御が不可欠です。

より堅牢な代替案

より強力な分離保証が必要な場合は、真のプロセスまたはハードウェアレベルの分離を提供する以下の代替案を検討してください:

ソリューション方式パフォーマンストレードオフ
isolated-vm独立した V8 アイソレート(異なる V8 ヒープ)高速メンテナンスモード;V8 の手動更新が必要
別プロセス / Worker権限が制限された child_process または Worker スレッド中程度IPC オーバーヘッドが高い;データをシリアライズする必要がある
コンテナ / VMDocker、gVisor、Firecracker低速起動オーバーヘッド;リソース消費が大きい
マネージドサービスクラウドベースのコード実行(例:AWS Lambda、Cloudflare Workers)変動的ネットワークレイテンシ;外部依存

vm2 が依然として適している場合

vm2 は以下の場合に適しています:

  • ホストオブジェクトとの緊密な統合と高速な同期通信が必要な場合
  • 信頼できないコードが比較的信頼できるソースからのものである場合(例:内部ツール、審査済みの作者によるプラグインシステム)
  • vm2 を他のセキュリティレイヤー(ネットワーク分離、ファイルシステム制限、リソース制限)と組み合わせる場合
  • リスクを受け入れ、セキュリティアップデートを積極的に監視する場合

完全に信頼できないソース(例:任意のユーザー送信)からのコードを実行する場合は、より強力な分離保証を備えたソリューションの使用を強くお勧めします。

機能

  • 信頼できないコードを、自身のコードと同じ単一プロセス内で安全に実行します
  • サンドボックスのコンソール出力を完全に制御できます
  • サンドボックスはプロセスのメソッドへのアクセスが制限されています
  • サンドボックスからモジュール(組み込みおよび外部)を require することが可能です
  • 特定の(またはすべての)組み込みモジュールへのアクセスを制限できます
  • サンドボックス間でメソッドを安全に呼び出し、データやコールバックを交換できます
  • 既知のエスケープ手法に対するパッチで積極的にメンテナンスされています(セキュリティ免責事項 を参照)
  • トランスパイラのサポート

仕組み

  • 内部の VM モジュールを使用して、安全なコンテキストを作成します。
  • プロキシ を使用して、サンドボックスからの脱出を防ぎます。
  • 組み込みの require をオーバーライドして、モジュールへのアクセスを制御します。

vm2 の内部構造の詳細については、CONTRIBUTING.md ファイルを参照してください。

Node の vm と vm2 の違いは何ですか?

実際に試してみてください:```js import { runInNewContext } from "node:vm";

runInNewContext('this.constructor.constructor("return process")().exit()'); console.log('Never gets executed.');

#!/bin/bash

# 環境を初期化しツールを起動するシェルスクリプト
# このスクリプトは必要なディレクトリを設定し、主要コンポーネントを起動します。

# 出力用のカラーコードを定義
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # 色なし

# 色付きメッセージを表示する関数
print_message() {
    local color="$1"
    local message="$2"
    echo -e "${color}${message}${NC}"
}

# ルート権限で実行されているか確認
if [ "$EUID" -ne 0 ]; then 
    print_message "$RED" "このスクリプトはルート権限で実行する必要があります"
    exit 1
fi

# 必要なディレクトリを作成
print_message "$YELLOW" "ディレクトリを作成しています..."
mkdir -p /var/log/kitploit /opt/kitploit/tools
print_message "$GREEN" "ディレクトリが正常に作成されました。"

# 環境を初期化
print_message "$YELLOW" "環境を初期化しています..."
export KITPLOIT_HOME="/opt/kitploit"
export PATH="$KITPLOIT_HOME/bin:$PATH"
print_message "$GREEN" "環境が初期化されました。"

# コアサービスを起動
print_message "$YELLOW" "コアサービスを起動しています..."
if systemctl start kitploit-api; then
    print_message "$GREEN" "APIサービスが起動しました。"
else
    print_message "$RED" "APIサービスの起動に失敗しました。"
    exit 1
fi

print_message "$GREEN" "すべてのサービスが正常に起動しました。"
exit 0```js
import { VM } from 'vm2';

new VM().run('this.constructor.constructor("return process")().exit()');
// Throws ReferenceError: process is not defined

インストール```sh

npm install vm2

## 簡単な例```js
import { VM } from 'vm2';

const vm = new VM();
vm.run(`process.exit()`); // TypeError: process.exit is not a function

Input content is missing — no text was provided to translate. Please provide the chunk content.```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', );

## Documentation

-   [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 の組み込みオブジェクトと Node の `Buffer` のみが利用可能です。スケジューリング関数(`setInterval`、`setTimeout`、`setImmediate`)はデフォルトでは利用できません。

**オプション:**

-   `timeout` - スクリプトのタイムアウト(ミリ秒)。**警告**: このオプションは `allowAsync=false` と一緒に使うことをお勧めします。さらに、サンドボックスから返されたオブジェクトに対する操作は任意のコードを実行でき、タイムアウトを回避する可能性があります。返されたオブジェクトがプリミティブかどうかを `typeof` で確認し、そうでない場合は完全に破棄する必要があります(そのようなオブジェクトを使ってログを取ったりエラーメッセージを作成したりすることも、再び任意のコードを実行する可能性があります)。
-   `sandbox` - VM のグローバルオブジェクト。
-   `compiler` - `javascript`(デフォルト)、`typescript`、`coffeescript`、またはカスタムコンパイラ関数。値が `typescript` または `coffeescript` に設定されている場合、ライブラリはコンパイラが事前にインストールされていることを前提としています。
-   `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)で信頼されていないコードを実行する組み込み側は、階層型 DoS 防御の一環として、`timeout` を選択するのと同じように、有限の上限(例: `32 * 1024 * 1024`)を選択する必要があります。以下の [ハードニングの推奨事項](#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

VMから値を取得することもできます。```js let number = vm.run('1337'); // returns 1337

**TIP**: 使用例の詳細はテストを参照してください。

## NodeVM

`VM` とは異なり、`NodeVM` を使用すると、通常の Node のコンテキストと同じ方法でモジュールを require できます。

**オプション:**

-   `console` - `inherit` は console を有効に、`redirect` はイベントへのリダイレクト、`off` は console を無効にします (デフォルト: `inherit`)。
-   `sandbox` - VM のグローバルオブジェクト。
-   `compiler` - `javascript` (デフォルト)、`typescript`、`coffeescript`、またはカスタムコンパイラ関数 (コードとそのファイルパスを受け取ります)。値を `typescript` または `coffeescript` に設定する場合、ライブラリはコンパイラが事前にインストールされていることを前提とします。
-   `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 が許可されます。
-   `require.external.modules` - 許可された外部モジュールの配列。ワイルドカードもサポートされるため、たとえば `['@scope/*-ver-??']` を指定すると、`@scope/something-ver-aa`、`@scope/other-ver-11` などの形式の名前を持つすべてのモジュールを許可します。`*` ワイルドカードはパスセパレータには一致しません。
-   `require.external.transitive` - 外部モジュールの推移的依存関係を許可するかどうかを示すブール値 (デフォルト: `false`)。**警告**: モジュールが推移的に require されると、ロード前にそれが不可能であった場合でも、その後はどのモジュールもそれを通常どおり require できるようになります。
-   `require.builtin` - 許可された組み込みモジュールの配列。すべてを許可するには `["\*"]` を受け入れます (デフォルト: なし)。**警告**: 新しい組み込みが追加される可能性があるため、`"\*"` は危険です。
-   `require.root` - ローカルモジュールを require できる制限付きパス (デフォルト: すべてのパス)。
-   `require.mock` - モックモジュールのコレクション (外部または組み込みの両方)。
-   `require.context` - `host` (デフォルト) はホスト内でモジュールを require し、サンドボックスにプロキシします。`sandbox` はサンドボックス内でモジュールをロード、コンパイル、require します。`callback(moduleFilename, ext)` はモジュールごとにコンテキストを動的に選択します。何も指定しない場合のデフォルトはサンドボックスです。`events` を除き、組み込みモジュールは常にホスト内で require され、サンドボックスにプロキシされます。
-   `require.import` - 起動時に NodeVM にロードされるモジュールの配列。
-   `require.resolve` - 従来の Node ルックアップパスのいずれでもモジュールが見つからなかった場合の追加のルックアップ関数。
-   `require.customRequire` - ホストからモジュールをロードするために `require` 関数の代わりに使用します。
-   `require.strict` - `false` にすると、require によってロードされたモジュールに厳格モードを強制しません (デフォルト: `true`)。
-   `require.fs` - カスタムファイルシステム実装。
-   `nesting` - **警告**: スクリプトが任意のホストモジュールを require できる 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);
});

When wrapper is set to none, NodeVM behaves more like VM for synchronous code.

wrappernone に設定されている場合、NodeVM は同期コードでは VM により近い動作をします。```js assert.ok(vm.run('return true') === true);

**ヒント**: 詳細な使用例については、テストを参照してください。

### 相対パスによるモジュールの読み込み

相対パスでモジュールを読み込むには、スクリプトが文字列の場合、実行中のスクリプトの完全パスを vm の `run` メソッドの2番目の引数として渡す必要があります。そうすると、そのファイル名がスクリプトによって生成されたスタックトレースに表示されます。```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() を使用すれば、いつでもコードをコンパイルできます。コードがコンパイルされた後は、このメソッドは効果を持ちません。

エラーハンドリング

コードのコンパイルおよび同期コード実行におけるエラーは、try-catch で処理できます。非同期コード実行におけるエラーは、Node の processuncaughtException イベントハンドラをアタッチすることで処理できます。```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` とは異なり、このメソッドはサンドボックス化されたスクリプトがオブジェクトのプロパティを追加、変更、または削除することを許可します。ただし、1つの例外があります - 関数をアタッチすることはできません。したがって、サンドボックス化されたスクリプトは `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` でメモリ割り当てを制限する

攻撃者が制御する `N` を指定した単一の `Buffer.alloc(N)` 呼び出しは、V8 の `timeout` では中断できない同期ホスト C++ 割り当てとして実行されます。メモリが制約された環境では、約 100 バイトのサンドボックスペイロードが 100 MB 以上のホスト RSS の急上昇を引き起こし、OOM でホストプロセスをクラッシュさせる可能性があります。個々の割り当てを制限するには、`bufferAllocLimit`(例: `32 * 1024 * 1024`)を設定してください:```js
const vm = new VM({
	timeout: 1000,
	bufferAllocLimit: 32 * 1024 * 1024,
	allowAsync: false,
});

The cap also applies to the deprecated Buffer(N) and new Buffer(N) paths. Note that aggregate exhaustion (many small allocations, Buffer.concat, Uint8Array, String.repeat, Array(n).fill(), etc.) is not covered by this cap — combine with a host-side memory limit (--max-old-space-size, container limit, cgroup) for full coverage.

2. Install a host-side unhandledRejection handler

A class of host-process abort DoS exists where sandbox code creates an async function, async function*, or await using whose body throws a value that triggers a host-realm error during stack formatting (e.g. e.name = Symbol(); e.stack). V8 creates the rejection promise via the realm's intrinsic Promise, which bypasses vm2's Promise subclass wrap, so the rejection escapes to the host as unhandledRejection. On Node 15+ the default behavior is to terminate the process.

Closing this requires changing observable host behavior, so vm2 does not ship a fix by default. Embedders should install a process-level handler that swallows (or logs) sandbox-originating rejections:```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);
});

限定的な修正は、将来のマイナーリリースでオプトインの swallowSandboxUnhandledRejections フラグの背後で提供される可能性があります。それまでは、ホスト側のハンドラが推奨される緩和策です。

3. プロセスレベルのメモリ上限で実行する

bufferAllocLimit を設定していても、ワークロードに合わせて --max-old-space-size(または同等のコンテナメモリ制限)を指定してホストプロセスを実行してください。この上限は単一割り当てプリミティブから保護します。OSレベルの制限は、総計での枯渇と、vm2 がまだ上限を設定していない将来の割り当てプリミティブから保護します。

4. require.builtin: ['*'] を非サンドボックス構成として扱う

'*' ワイルドカードは、child_processfsdgramnethttpdns を含むほとんどの Node 組み込みモジュールに展開されます。これらは完全なホスト機能プリミティブであり、'*' の下では require('child_process').execSync('id') がサンドボックスから到達可能です。vm2 の '*' セマンティクスは意図的なものです(一部の組み込み者は信頼済みだが隔離されたコードを実行します)が、信頼できないコードのデフォルトとして使用すべきではありません。サンドボックスが実際に必要とする最小限のモジュールの明示的な許可リストを優先してください。

5. nesting: true はエスケープハッチです

nesting: true を使用すると、サンドボックスコードは require('vm2') を呼び出してネストされた NodeVM を構築できます。ネストされた VM の require 設定は、それを構築するサンドボックスコードによって選択され、外部 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.js プロセスをクラッシュさせる方法があります。[ハードニングの推奨事項](#hardening-recommendations) を参照してください。

[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

カテゴリ