关于 deno-sandbox
deno-sandbox 面向在 Deno 项目中构建代码执行功能的开发者,介绍如何使用 `@deno/sandbox`。内容涵盖在隔离的 Linux 微型虚拟机中运行不可信代码,以及资源生命周期和进程管理。
主要功能
- 01隔离的 Linux 微型虚拟机执行
- 02运行命令并管理进程
- 03流式处理进程输入与输出
- 04读写沙箱文件
- 05权限与超时设置指南
适用场景
- 01运行用户提交的代码
- 02执行 AI 生成的代码
- 03构建代码游乐场
- 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