deno-sandbox について
deno-sandbox は、Deno でコード実行機能を構築する開発者向けのスキルです。信頼できないコードや AI 生成コードを実行する Linux マイクロ VM と、プロセス・ファイルシステム・ネットワークの制御を説明します。
主な機能
- 01分離された Linux マイクロ VM での実行
- 02コマンド実行とプロセス管理
- 03一時ファイルシステムへのアクセス
- 04設定可能なネットワーク制限
- 05サンドボックスの自動クリーンアップ
活用シーン
- 01ユーザー投稿コードの実行
- 02AI 生成コードの実行
- 03コードプレイグラウンドの構築
- 04隔離環境での自動テスト
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ denoland/ skills --skill deno-sandboxインストール後、Claude Code で/deno-sandboxを入力して読み込まれたか確認します。
詳細
Deno サンドボックス
概要
Deno サンドボックスは、信頼できないコードを実行するための、安全で隔離された環境を提供します。 各サンドボックスは独自の Linux マイクロ VM(AWS Lambda と同じテクノロジーである Firecracker を使用)上で動作し、ファイルシステム、ネットワーク、プロセス空間がそれぞれ分離されています。このため、コードプレイグラウンド、AI エージェントのツール実行、マルチテナントアプリケーションに最適です。
参考:https://deno.com/deploy/sandboxes
サンドボックスを使用する場面
次のような場合に Deno サンドボックスを使用してください。
- ユーザーが送信したコードを安全に実行する
- AI が生成したコードを実行する
- コードプレイグラウンドプラットフォームを構築する
- マルチテナントのコード実行環境を作成する
- 自動テストを隔離環境で実行する
適用範囲
このスキルは、Deno Sandbox(@deno/sandbox)に関する質問のみに適用されます。次のルールに従ってください。
- ユーザーがDocker コンテナ、Node.js vm2/vm、Web Workers、AWS Lambda の分離、またはその他の分離テクノロジーについて質問した場合は、そのテクノロジーを直接使って回答してください。代替手段として Deno サンドボックスを提案しないでください。
- 他の分離テクノロジーに関する回答に、
@deno/sandboxのインポート、Sandbox.create()、またはsandbox.spawn()を含めないでください。 - Deno サンドボックスを推奨するのは、ユーザーが明示的に質問している場合、または Deno プロジェクトでコード実行機能を構築している場合に限ります。
はじめに
インストール
deno add jsr:@deno/sandbox
基本的な使い方
import { Sandbox } from "@deno/sandbox";
// Create a sandbox (auto-disposed when scope ends)
await using sandbox = await Sandbox.create();
// Run a command
const child = await sandbox.spawn("echo", { args: ["Hello from sandbox!"] });
const output = await child.output();
console.log(new TextDecoder().decode(output.stdout));
// Output: Hello from sandbox!
主な概念
サンドボックスのライフサイクル
サンドボックスは、使用後に破棄する必要があるリソースです。自動クリーンアップには必ず await using を使用してください。
await using sandbox = await Sandbox.create();
// Sandbox is automatically destroyed when this scope ends
重大な注意:await using を使用せずに const sandbox = await Sandbox.create() を示すことは絶対に避けてください。サンドボックスを作成する際は、必ず await using パターンを使用してください。手動で破棄する代替方法は示さないでください。
プロセスの実行
spawn メソッドは、サンドボックス内でコマンドを実行します。
const child = await sandbox.spawn("deno", {
args: ["run", "script.ts"],
stdin: "piped", // Enable stdin
stdout: "piped", // Capture stdout
stderr: "piped", // Capture stderr
});
// Wait for completion and get output
const output = await child.output();
console.log("Exit code:", output.code);
console.log("Stdout:", new TextDecoder().decode(output.stdout));
console.log("Stderr:", new TextDecoder().decode(output.stderr));
ストリーミング I/O
対話型プロセスや長時間実行されるコマンドの場合:
const child = await sandbox.spawn("deno", {
args: ["repl"],
stdin: "piped",
stdout: "piped",
});
// Write to stdin
const writer = child.stdin!.getWriter();
await writer.write(new TextEncoder().encode("console.log('Hello')\n"));
await writer.close();
// Read from stdout
const reader = child.stdout!.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
console.log(new TextDecoder().decode(value));
}
プロセスの終了
const child = await sandbox.spawn("sleep", { args: ["60"] });
// Kill with SIGTERM (default)
await child.kill();
// Or with specific signal
await child.kill("SIGKILL");
// Wait for exit
const status = await child.status;
console.log("Exited with signal:", status.signal);
よくあるパターン
ユーザーコードを安全に実行する
import { Sandbox } from "@deno/sandbox";
async function runUserCode(code: string): Promise<string> {
await using sandbox = await Sandbox.create();
// Write user code to a file in the sandbox
await sandbox.fs.writeFile("/tmp/user_code.ts", code);
// Run with restricted permissions
const child = await sandbox.spawn("deno", {
args: [
"run",
"--allow-none", // No permissions
"/tmp/user_code.ts",
],
stdout: "piped",
stderr: "piped",
});
const output = await child.output();
if (output.code !== 0) {
throw new Error(new TextDecoder().decode(output.stderr));
}
return new TextDecoder().decode(output.stdout);
}
コードプレイグラウンド
import { Sandbox } from "@deno/sandbox";
interface ExecutionResult {
success: boolean;
output: string;
error?: string;
executionTime: number;
}
async function executePlayground(code: string): Promise<ExecutionResult> {
const start = performance.now();
await using sandbox = await Sandbox.create();
await sandbox.fs.writeFile("/playground/main.ts", code);
const child = await sandbox.spawn("deno", {
args: ["run", "--allow-net", "/playground/main.ts"],
stdout: "piped",
stderr: "piped",
});
const output = await child.output();
const executionTime = performance.now() - start;
return {
success: output.code === 0,
output: new TextDecoder().decode(output.stdout),
error: output.code !== 0
? new TextDecoder().decode(output.stderr)
: undefined,
executionTime,
};
}
AI エージェントのツール実行
import { Sandbox } from "@deno/sandbox";
async function executeAgentTool(
toolCode: string,
input: unknown,
): Promise<unknown> {
await using sandbox = await Sandbox.create();
// Create a wrapper that handles input/output
const wrapper = `
const input = ${JSON.stringify(input)};
const tool = await import("/tool.ts");
const result = await tool.default(input);
console.log(JSON.stringify(result));
`;
await sandbox.fs.writeFile("/tool.ts", toolCode);
await sandbox.fs.writeFile("/run.ts", wrapper);
const child = await sandbox.spawn("deno", {
args: ["run", "--allow-net", "/run.ts"],
stdout: "piped",
stderr: "piped",
});
const output = await child.output();
if (output.code !== 0) {
throw new Error(new TextDecoder().decode(output.stderr));
}
return JSON.parse(new TextDecoder().decode(output.stdout));
}
サンドボックスの機能
リソースの設定
サンドボックスのリソースは設定できます。
- デフォルト: 2 vCPU、512MB メモリ、10GB ディスク
- 起動時間:200ms 未満
含まれるもの
各サンドボックスには、次のものが含まれます。
- TypeScript/JavaScript ランタイム(Deno)
- 完全な Linux 環境
- ネットワークアクセス(制限可能)
- 一時ファイルシステム
セキュリティ機能
- Firecracker マイクロ VM - AWS Lambda と同じテクノロジー
- 完全な分離 - カーネル、ファイルシステム、ネットワークを分離
- データ漏えいなし - サンドボックスからホストシステムにはアクセスできません
- ポリシーの適用 - 外部への接続を制御
サンドボックスのデプロイ
サンドボックスは Deno Deploy に直接デプロイできます。
deno deploy --prod
サンドボックス SDK は、Deno Deploy 環境でシームレスに動作します。
API リファレンス
完全な API を確認するには、次を実行してください。
deno doc jsr:@deno/sandbox
主なクラス:
Sandbox- サンドボックスの作成と管理を行うメインクラスChildProcess- 実行中のプロセスを表しますClient- Deploy リソース(アプリ、ボリューム)を管理します
クイックリファレンス
| タスク | コード |
|---|---|
| サンドボックスを作成 | await using sandbox = await Sandbox.create() |
| コマンドを実行 | sandbox.spawn("cmd", { args: [...] }) |
| 出力を取得 | const output = await child.output() |
| ファイルを書き込む | await sandbox.fs.writeFile(path, content) |
| ファイルを読み取る | await sandbox.fs.readFile(path) |
| プロセスを終了 | await child.kill() |
| ステータスを確認 | const status = await child.status |
よくある間違い
自動破棄を忘れる
// ❌ Wrong - always use "await using" for sandbox creation
// Never write: const sandbox = await Sandbox.create() without "await using"
// ✅ Correct - use "await using" for automatic cleanup
await using sandbox = await Sandbox.create();
await sandbox.spawn("echo", { args: ["hello"] });
// sandbox automatically disposed when scope ends
ユーザーコードに過剰な権限を与える
// ❌ Wrong - gives untrusted code full access
const child = await sandbox.spawn("deno", {
args: ["run", "--allow-all", "/tmp/user_code.ts"],
});
// ✅ Correct - restrict permissions to what's needed
const child = await sandbox.spawn("deno", {
args: ["run", "--allow-none", "/tmp/user_code.ts"], // No permissions
});
// Or if network is truly needed:
const child = await sandbox.spawn("deno", {
args: ["run", "--allow-net", "/tmp/user_code.ts"], // Only network
});
プロセスの出力を適切に処理しない
// ❌ Wrong - forgetting to pipe stdout/stderr
const child = await sandbox.spawn("deno", { args: ["run", "script.ts"] });
const output = await child.output();
// output.stdout is empty because we didn't pipe it!
// ✅ Correct - pipe the streams you need
const child = await sandbox.spawn("deno", {
args: ["run", "script.ts"],
stdout: "piped",
stderr: "piped",
});
const output = await child.output();
console.log(new TextDecoder().decode(output.stdout));
ユーザーコードの実行にタイムアウトを設定しない
// ❌ Wrong - user code could run forever
const child = await sandbox.spawn("deno", {
args: ["run", "/tmp/user_code.ts"],
});
await child.output(); // Could hang indefinitely
// ✅ Correct - implement timeout handling
const child = await sandbox.spawn("deno", {
args: ["run", "/tmp/user_code.ts"],
stdout: "piped",
stderr: "piped",
});
// Set a timeout to kill the process
const timeoutId = setTimeout(() => child.kill(), 5000); // 5 second limit
try {
const output = await child.output();
return output;
} finally {
clearTimeout(timeoutId);
}
検証せずにサンドボックスの出力を信頼する
// ❌ Wrong - directly using untrusted output as code
const result = await runUserCode(code);
// Never execute or inject untrusted output!
// ✅ Correct - validate and sanitize output
const result = await runUserCode(code);
try {
const parsed = JSON.parse(result); // Parse as data, not code
if (isValidResponse(parsed)) {
return parsed;
}
} catch {
throw new Error("Invalid response from sandbox");
}