Claude Code 源码解析(四):权限与沙箱如何约束工具调用
本文是 Claude Code 源码逆向系列 的第四篇,聚焦权限系统与沙箱在工具调用前的门控机制。 第三块是"安全边界"核心:工具不是想调就调,必须经过权限判定。这也是 Claude Code 敢于在用户本地机器上运行 rm -rf 或 curl 的底气所在。 1. 架构总览:双层防御体系 在恢复代码的过程中,我发现 Claude Code 的安全机制并非铁板一块,而是清晰地分成了两个层级: Sandbox(沙箱):系统级的硬约束。例如"绝对禁止读取 /etc/passwd“或"只允许访问 github.com"。这是一道不可逾越的红线。 Permissions(权限):用户意图的软确认。例如"可以运行这个命令吗?“或"确认写入这个文件吗?"。这通过 Human-in-the-Loop(人机回环)来实现安全兜底。 主要涉及的代码目录: src/core/sandbox/:沙箱策略、路径标准化、网络白名单。 src/core/permissions/:权限决策引擎、上下文状态、规则匹配。 src/core/agent/runtime.ts:执行循环中的拦截点。 2. Sandbox:绝对的系统边界 沙箱的核心逻辑在 src/core/sandbox/policy.ts。它不关心"用户同不同意”,只关心"系统允不允许”。 文件系统限制 最基本的防御是文件路径检查。SandboxPolicy 类中有一个关键的细节:路径标准化。 1 2 3 4 5 6 // src/core/sandbox/policy.ts private resolvePath(input: string) { if (input === ".") return resolve(this.cwd); if (input.startsWith("/")) return resolve(input); return resolve(this.cwd, input); // 相对路径转绝对路径 } 这一点非常重要。如果没有这一步,攻击者(或幻觉中的模型)可能会尝试用 ../../ 逃逸出工作目录。恢复后的代码显示,所有的 checkRead 和 checkWrite 都会先调用 resolvePath,然后与 denyRead / denyWrite 列表进行比对。 网络访问控制 对于 WebFetch 和 WebSearch 工具,沙箱检查的是域名: 1 2 3 4 5 6 7 8 9 10 11 // src/core/sandbox/policy.ts (简化) checkNetwork(target: string): SandboxDecision { const hostname = this.extractHostname(target); // 1. 黑名单检查 if (this.matchesDomain(hostname, denied)) return { allowed: false, ... }; // 2. 白名单检查 (如果配置了白名单) if (allowed.length > 0 && !this.matchesDomain(hostname, allowed)) { return { allowed: false, reason: "allowedDomains" }; } return { allowed: true }; } 这意味着企业用户可以通过配置 allowedDomains 来强制 Claude Code 只能访问内网文档或特定的 API 服务,杜绝数据外泄风险。 ...