mcpField
D
deno-sandbox

deno-sandbox

github.com/denolandセキュリティ102361 回インストール13 週間前に更新
リポジトリ

deno-sandbox について

deno-sandbox は、Deno でコード実行機能を構築する開発者向けのスキルです。信頼できないコードや AI 生成コードを実行する Linux マイクロ VM と、プロセス・ファイルシステム・ネットワークの制御を説明します。

主な機能

  1. 01分離された Linux マイクロ VM での実行
  2. 02コマンド実行とプロセス管理
  3. 03一時ファイルシステムへのアクセス
  4. 04設定可能なネットワーク制限
  5. 05サンドボックスの自動クリーンアップ

活用シーン

  1. 01ユーザー投稿コードの実行
  2. 02AI 生成コードの実行
  3. 03コードプレイグラウンドの構築
  4. 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");
}

コメント