关于 deno-sandbox
deno-sandbox 是面向使用 Deno 构建代码执行功能的开发者的技能,介绍用于运行不可信或 AI 生成代码的隔离 Linux microVM,以及进程、文件系统和网络控制。
主要功能
- 01隔离的 Linux microVM 执行环境
- 02运行命令并管理进程
- 03临时文件系统访问
- 04可配置网络限制
- 05自动清理沙箱
适用场景
- 01运行用户提交的代码
- 02执行 AI 生成的代码
- 03构建代码 Playground
- 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