资讯动态

claude code 源码分析之沙箱机制:从权限边界到执行隔离的工程实现

发布时间:2026/10/9 19:34:32 来源:尧图企业网站定制
1. 从一次被拦下的echo说起claude code 沙箱机制到底在防什么你可能遇到过这种场景让 Claude Code 帮忙改一下 hosts 文件做本地域名映射结果命令跑完直接报错stderr 里还多了一段sandbox_violations标签。第一反应往往是——这工具怎么连个echo重定向都搞不定其实这恰恰是 claude code 沙箱机制在正常工作。它要回答的不是“这条命令能不能跑”而是“就算跑了这个子进程最多能碰到哪些文件、哪些网络目标”。权限系统负责判断要不要执行沙箱负责限制执行之后的能力边界两者叠起来才是完整的纵深防御。这篇就按源码级拆解的思路走一遍先定位沙箱相关模块再复现一次受限命令执行然后对比开启与关闭隔离时的行为差异。适合已经用过 Claude Code、想搞清楚它安全设计取舍的人也适合正在给自己的 Agent 工具设计执行隔离的开发者。核心检索词先摆出来claude code 沙箱机制是一套基于 OS 级能力边界的 shell 执行隔离方案它不替代权限系统而是给 Bash / PowerShell 子进程再套一层文件系统与网络约束。理解它的关键是把“要不要执行”和“执行了能做什么”这两个判定点拆开看。我试过在同一个工作区里连续跑npm test、rg、git status全程没有弹窗但一旦把输出重定向到/etc/hosts命令立刻失败。这个对比非常直观地说明了沙箱的默认白名单范围——当前工作目录和 Claude 临时目录可以写系统路径默认写不了。2. 定位沙箱模块sandbox-adapter.ts与外部 runtime 的分工要读懂 claude code 沙箱机制第一步是找到源码里的边界线。仓库自己负责的是策略、配置转换、启停判断、命令包裹、清理和权限联动真正做 OS 级隔离的是外部运行时anthropic-ai/sandbox-runtime。在src/utils/sandbox/sandbox-adapter.ts里能看得很清楚项目导入SandboxManager as BaseSandboxManager、SandboxViolationStore等运行时对象然后在外面再包一层符合 Claude Code 自身权限模型的适配器。这个适配器就是“翻译层”把 Claude Code 的设置翻译成底层 runtime 能吃的配置。底层隔离在不同平台上的落地不是同一套实现。macOS 走sandbox-exec路径和网络规则通过 Seatbelt profile 落地Linux / WSL2 走bubblewrap seccomp会建立 mount / PID / network 等隔离Windows 原生不支持这套 shell 沙箱。所以如果只看这个仓库容易误以为沙箱都是它自己做的更准确的说法是这个仓库决定该不该启、该怎么配、该怎么接进工具链真正的 OS 级约束由外部 runtime 执行。启动阶段就会先判断“沙箱能不能用”。REPL / CLI 启动时会检查当前平台是否受底层 runtime 支持、依赖是否齐全、sandbox.enabled是否打开、当前平台是否落在enabledPlatforms范围内。如果用户显式开启了沙箱但环境不满足启动期会先给 warning如果同时配置了sandbox.failIfUnavailable则直接拒绝启动而不是悄悄降级成无沙箱模式。启动时不只是“看一眼能不能用”而是真的会调用初始化流程把当前设置转换成 runtime 配置并交给BaseSandboxManager.initialize(...)。后续如果设置变化还会通过updateConfig(...)热更新而不是要求重启整个会话。这一段回答的是当前会话里有没有一个可用、已初始化、能处理网络授权回调的沙箱 runtime。命令期的链路则是另一条线用户请求 -BashTool.checkPermissions()-shouldUseSandbox(input)-Shell.exec(command, { shouldUseSandbox: true/false })-SandboxManager.wrapWithSandbox(...)-spawn(wrapped command)- 运行结束后cleanupAfterCommand()。真正把命令“包进沙箱”的关键点是Shell.exec()它会在真正spawn(...)之前调用wrapWithSandbox(...)把原始命令改写成底层 runtime 可执行的沙箱命令串。这里有两个容易混淆的判定点。判定点 A 是shouldUseSandbox()的职责回答“这条命令要不要被 OS 级沙箱包起来执行”。判定点 B 是权限系统和 Bash 权限检查的职责回答“这条命令在应用层看来是 allow、ask 还是 deny”。这两个判定点并列协作不是互相替代。还有一个细节值得单独拎出来src/utils/bash/ast.ts开头就写得很明确Bash AST 分析不是沙箱它只是在判断我们能不能可靠地理解命令结构不能阻止危险命令真的运行。像bash script.sh、python -c ...、make、npm install这类命令真实副作用都要到运行时才完全展开。沙箱的价值就在这里——即使前面的分析漏了进程到了 OS 层以后仍然只能写允许目录、访问允许域名。3. 可复制配置把沙箱设置写进settings.json理解了模块分工接下来就是能直接抄的配置。Claude Code 的沙箱配置最终由convertToSandboxRuntimeConfig()生成它会把项目自己的设置、权限规则和安全加固逻辑转换成底层运行时需要的配置。这一步很关键因为沙箱配置不是一份静态表而是从 Claude Code 自己的权限系统里“翻译”出来的。下面是一份可以直接放进项目.claude/settings.json的片段路径和字段名与源码里的读取逻辑保持一致{ sandbox: { enabled: true, failIfUnavailable: false, enabledPlatforms: [darwin, linux, wsl2], autoAllowBashIfSandboxed: true, excludedCommands: [ git push, npm publish ], network: { allowedDomains: [ registry.npmjs.org, pypi.org, github.com ] }, filesystem: { allowWrite: [ ./dist, ./.cache ], allowRead: [ ./node_modules ], denyWrite: [ ./.claude/settings.json, ./.claude/skills ], denyRead: [] } } }几个字段的含义需要说清楚。enabled是总开关关掉之后所有命令都不进沙箱。failIfUnavailable决定环境不满足时是拒绝启动还是降级。enabledPlatforms控制哪些平台启用Windows 原生不在这个列表里所以原生 PowerShell 只能依赖权限系统。autoAllowBashIfSandboxed是沙箱设计里最值得注意的开关之一它表达的是这样一个信任假设如果命令已经被 OS 级沙箱约束在安全边界内那么应用层就没有必要再对大量低风险 Bash 命令逐条弹确认框。excludedCommands支持三类模式精确匹配、前缀匹配、通配符匹配。命中之后这条命令会直接跳过沙箱。network.allowedDomains会和WebFetch(domain:...)这类权限规则合并成网络白名单。filesystem.allowWrite会叠加到默认白名单上默认的allowWrite只有两类当前工作目录和 Claude 的临时目录。还有一个很容易漏掉的细节适配层会专门处理 worktree 主仓库和 bare git repo 这种仓库级特殊路径避免在隔离后把正常开发流程误伤或者反过来留下逃逸面。denyWrite里额外加固的settings.json、.claude/skills以及一些 bare git repo 相关路径意义不是“让更多命令通过”而是“即使命令已经执行也别让它顺手把护栏本身拆掉”。如果你用的是 Codex 或 Cline 这类工具配置思路类似但字段名不同。以 Codex 的auth.json为例接入时需要写全三件套Base URL、Key、Model ID。Base URL 填https://taotoken.net/apiKey 在控制台生成Model ID 按你实际要用的模型填。Cline 的 MCP 配置也是同样的三件套逻辑缺一个都会导致请求失败。配置改完之后不需要重启整个会话updateConfig(...)会热更新。但如果你改的是enabled这种总开关建议还是重启一次避免运行中的命令状态不一致。4. 验证请求复现一次受限命令执行并对比行为差异配置写好了接下来要验证它真的生效。最直接的办法是复现一次受限命令执行然后对比开启与关闭隔离时的行为差异。先在工作区里跑一条正常命令npm test如果autoAllowBashIfSandboxed开着这条命令应该直接跑完不弹权限确认框。因为工作区内的构建、测试、生成临时文件通常都在默认allowWrite范围内。然后跑一条越界命令echo 127.0.0.1 local.test | sudo tee -a /etc/hosts在 Linux 上这条命令通常会进沙箱但沙箱默认没有/etc写权限所以会在运行时失败。stderr 里会被附加sandbox_violations标签供模型理解UI 会清理这些标签再显示给用户同时SandboxViolationStore会记录违规事件。你通常能看到命令失败本身以及“最近有多少次 sandbox blocked”之类的界面提示。再跑一条网络越界命令curl https://example.com/data.json如果example.com不在network.allowedDomains里访问会被拦截或触发额外的网络授权流程。网络是个特例当沙箱外的 host 访问需要额外确认时项目会弹出一个专门的网络授权对话框例如Network request outside of sandbox。这里和文件系统运行时拦截不同它有明确的交互式授权 UI。现在把sandbox.enabled改成false重启会话再跑同样的命令。echo ... | sudo tee -a /etc/hosts会直接尝试执行是否成功取决于你的系统权限但至少不会再被沙箱拦。curl也会直接发出请求不再有网络白名单约束。这个对比非常直观沙箱开启时越界操作变成一次受限失败沙箱关闭时越界操作的真实副作用完全展开。还有一个验证点是excludedCommands。把git push加进排除列表后这条命令会跳过沙箱但仍然要遵守正常的 ask 规则因为它没有拿到 OS 级约束带来的那层安全兜底。这也是autoAllowBashIfSandboxed的边界条件它只对“真正会进沙箱的命令”生效。命中了excludedCommands、显式使用了dangerouslyDisableSandbox: true、或者当前平台根本不支持沙箱的命令都不能直接吃到这个 shortcut。如果你想验证模型侧的请求是否正常可以用模型对话功能发一条简单消息确认 Base URL 和 Key 配置无误。这一步和沙箱验证是独立的但建议一起做避免把网络问题和配置问题混在一起排查。5. 本篇常见错排查401、local proxy failed 与 reading choices配置和验证过程中最容易撞上的几类报错这里集中对照一下。第一类是 401。这个通常和沙箱无关而是 Key 或 Base URL 配错了。检查auth.json或环境变量里的 Key 是否完整Base URL 是否写成了https://taotoken.net/api。注意 API 地址不要加 UTM 参数加了反而可能导致鉴权失败。如果用的是 Coding Plan确认套餐状态正常Key 没有过期。第二类是local proxy failed。这个报错在沙箱场景下出现通常是因为底层 runtime 初始化失败。排查顺序是先确认当前平台在enabledPlatforms范围内再确认anthropic-ai/sandbox-runtime依赖装好了最后看sandbox.failIfUnavailable是不是设成了true导致直接拒绝启动。Linux 上还要确认bubblewrap和seccomp可用WSL 只支持 WSL2WSL1 会被视为不支持平台。第三类是reading choices相关报错。这个一般出现在模型返回结构不符合预期时和沙箱配置没有直接关系。检查 Model ID 是否填对请求体格式是否符合对应 API 的要求。如果用的是 Claude Code 的润色或补全类功能确认接入配置里的三件套齐全Base URL、Key、Model ID。第四类是 OAuth 相关报错。如果你用的是需要 OAuth 的接入方式确认回调地址和 token 没有过期。这类报错和沙箱的network.allowedDomains可能有关联——如果 OAuth 回调域名不在网络白名单里授权流程会被沙箱拦。把对应域名加进allowedDomains再试。第五类是命令被沙箱拦了但你不确定原因。这时候先看 stderr 里的sandbox_violations标签它会告诉你具体是文件系统违规还是网络违规。文件系统违规通常是写路径不在allowWrite里网络违规通常是域名不在allowedDomains里。如果是 bare git repo 逃逸相关的拦截检查一下denyWrite里那些加固路径是不是误伤了你的正常操作。排障时如果拿不准是配置问题还是环境问题可以先用模型对话发一条最简单的请求确认基础链路通不通。链路通了再回头查沙箱配置能省不少时间。接入文档里有各平台的依赖安装说明对着走一遍通常能解决大部分local proxy failed。6. 把沙箱接进你的工作流从验证到长期使用验证通过之后下一步是把它变成日常开发的一部分。这里有几个实用技巧。第一autoAllowBashIfSandboxed建议开着。它的核心思路不是“更大胆地信任模型”而是“既然命令已经被 OS 级边界收紧就没必要再让用户为大量低风险 Bash 命令反复点确认”。没有沙箱的话系统通常只剩两种都不太理想的选择频繁弹窗让工作流很碎或者更激进地信任应用层判断把风险全压在静态分析上。沙箱把 shell 的默认能力收缩到工作区和白名单之后项目才敢在应用层减少弹窗。第二excludedCommands要克制使用。这个列表里的命令会跳过沙箱只靠权限系统兜底。把git push、npm publish这类需要明确确认的操作放进去是合理的但不要把日常构建命令也塞进去否则沙箱的自动化收益就没了。第三网络白名单按需加。默认白名单之外的域名访问会触发授权对话框这在调试阶段很烦但长期来看是好事。把常用的包管理域名、代码托管域名加进allowedDomains不常用的保持默认拦截。第四理解“沙箱把/etc拦了”反而说明它有用。Claude Code 日常最常跑的不是系统管理命令而是npm test、npm install、cargo build、pytest、rg、git status这些开发命令。这些命令本来就应该只在工作区和少量临时目录里活动。沙箱把 shell 的默认能力收缩到这个范围后项目才敢提高自动化程度。所以这个问题的正确落点不是“它为什么不帮我改/etc”而是“它能不能在不碰/etc的前提下让大量正常开发命令更安全、更顺滑地运行”。如果你需要长期跑编码任务或 Agent 工作流Coding Plan 的额度模型比按次调用更适合高频使用。接入方式还是那三件套Base URL 填https://taotoken.net/apiKey 在控制台生成Model ID 按实际模型填。配置片段可以直接复用上面settings.json的结构把沙箱和接入配置放在同一个文件里管理。最后留一个阅读路径方便你继续顺着源码深入src/tools/BashTool/shouldUseSandbox.ts-src/utils/Shell.ts-src/utils/sandbox/sandbox-adapter.ts-src/utils/permissions/permissions.ts-src/tools/BashTool/bashPermissions.ts-src/utils/permissions/pathValidation.ts-src/utils/permissions/filesystem.ts。按这条线读会更容易把“权限系统”和“沙箱系统”在脑中拆开。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑