mcpField
D
deno-sandbox

deno-sandbox

github.com/denoland安全102361 次安装13 周前更新
代码仓库

关于 deno-sandbox

deno-sandbox 是面向使用 Deno 构建代码执行功能的开发者的技能,介绍用于运行不可信或 AI 生成代码的隔离 Linux microVM,以及进程、文件系统和网络控制。

主要功能

  1. 01隔离的 Linux microVM 执行环境
  2. 02运行命令并管理进程
  3. 03临时文件系统访问
  4. 04可配置网络限制
  5. 05自动清理沙箱

适用场景

  1. 01运行用户提交的代码
  2. 02执行 AI 生成的代码
  3. 03构建代码 Playground
  4. 04在隔离环境中运行自动化测试

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/denoland/skills --skill deno-sandbox

安装后在 Claude Code 中输入/deno-sandbox确认已加载。

详细介绍

Deno 沙箱

概述

Deno 沙箱提供安全、隔离的环境,用于运行不可信代码。 每个沙箱都运行在自己的 Linux 微型虚拟机中(使用 Firecracker,与 AWS Lambda 使用相同的技术),并拥有独立的文件系统、网络和进程空间。这使其非常适合代码游乐场、AI Agent 工具执行和多租户应用。

参考: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 沙箱。

入门指南

安装
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 Agent 工具执行
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 微型虚拟机——与 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");
}

评论

支持 Markdown