mcpField
D
deno-sandbox

deno-sandbox

github.com/midudev开发7.0k58 次安装1 周前更新
代码仓库

关于 deno-sandbox

deno-sandbox 面向在 Deno 项目中构建代码执行功能的开发者,介绍如何使用 `@deno/sandbox`。内容涵盖在隔离的 Linux 微型虚拟机中运行不可信代码,以及资源生命周期和进程管理。

主要功能

  1. 01隔离的 Linux 微型虚拟机执行
  2. 02运行命令并管理进程
  3. 03流式处理进程输入与输出
  4. 04读写沙箱文件
  5. 05权限与超时设置指南

适用场景

  1. 01运行用户提交的代码
  2. 02执行 AI 生成的代码
  3. 03构建代码游乐场
  4. 04隔离运行自动化测试

安装

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

$npx skills add https://github.com/midudev/autoskills --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