随笔
Claude Code 深度源码解读 08:安全边界是分层系统,不是权限弹窗
从 settings trust、tool pool、input validation、permissions、path validation、sandbox、MCP 与 remote 读安全治理。
简化版入口:安全与信任边界。
我的设问与回应
设问 1:安全边界是不是权限弹窗?
回应答案: 不是。权限 UI 只是 ask 分支。真实边界包括工具可见性、输入校验、PreToolUse、deny-first 权限、path validation、sandbox、MCP approval、bridge/remote gate、workspace trust、managed settings。
证据与证明路径: 证据来自第 8 期的核心流程图、关键源码入口和 5.1 安全不是只靠权限弹窗。
还值得继续学习或反思: 进一步要追的是每个外部连接面是否都有独立 auth、approval、policy 和审计。
设问 2:auto / bypass 会不会破坏安全?
回应答案: 源码把 auto/bypass 放在 deny、ask、tool check、requiresUserInteraction、safetyCheck 之后,并排除部分高风险工具或模式。projectSettings 也不能接受高风险 opt-in。
证据与证明路径: 证据来自第 8 期 5.2 与 5.3,对 permissions.ts、settings.ts 排除 projectSettings 的说明。
还值得继续学习或反思: 自动化审批只能是受限优化,不能成为最高优先级。
源码调研主体
本组解读基于冻结源码快照进行教育、防御和架构研究。
本期主题:从 permissions、sandbox、MCP、bridge、managed settings、workspace trust、hooks 和多 agent 角度复盘 Harness 如何防止 agent 稳定地做错事。
1. 研究问题
本期围绕一个问题展开:
Claude Code Harness 的安全边界到底在哪里?哪些机制负责防止模型把错误推理放大成真实副作用?
前几期已经分别分析了 Tools / Permissions、Query Loop、Hooks、Memory 和多 Agent。第 8 期不重复每条链路,而是横向复盘信任边界:
- 模型能看到什么工具。
- 模型产生的工具输入如何被校验。
- 权限规则如何合并 deny / ask / allow。
- auto / bypass / headless 场景如何避免无交互误放行。
- 文件路径、sandbox、MCP、bridge 和 remote control 如何建立外部边界。
- managed settings 与 workspace trust 如何防止项目配置自我提权。
本期只做防御性架构总结,不提供绕过步骤或复用实现。
2. 对应总览节点
对应第 1 期总览图中的节点:
PermissionsTool HooksTool.callMCPBridge / RemoteManaged SettingsSandboxTranscript / Audit
这部分机制参考前文「安全与信任边界机制图」。
3. 核心流程图
路线图中的代表链路是:
permissions / sandbox / MCP / bridge / managed settings下图按颜色区分边界:蓝色是模型到工具主链,红色是权限/拒绝边界,绿色是路径与 sandbox,紫色是外部连接,橙色是管理策略。
这部分机制参考前文「安全与信任边界机制图」。
4. 关键源码入口
| 职责 | 文件 |
|---|---|
| 工具接口与 ToolUseContext | src/Tool.ts |
| 工具池组装与 deny 过滤 | src/tools.ts |
| 工具执行控制链 | src/services/tools/toolExecution.ts |
| 权限规则和模式 | src/utils/permissions/permissions.ts |
| 权限结果类型 | src/utils/permissions/PermissionResult.ts |
| 权限规则解析 | src/utils/permissions/PermissionRule.ts、src/utils/permissions/permissionRuleParser.ts |
| 文件路径权限 | src/utils/permissions/pathValidation.ts、src/utils/permissions/filesystem.ts |
| sandbox adapter | src/utils/sandbox/sandbox-adapter.ts |
| Bash sandbox 判定 | src/tools/BashTool/shouldUseSandbox.ts |
| Bash 权限细分 | src/tools/BashTool/bashPermissions.ts |
| 权限 UI / React 决策 | src/hooks/useCanUseTool.tsx、src/components/permissions/PermissionRequest.tsx |
| PermissionRequest structured IO | src/cli/structuredIO.ts |
| MCP config / approval / auth | src/services/mcp/config.ts、src/services/mcpServerApproval.tsx、src/services/mcp/auth.ts |
| bridge 启动与 remote control gate | src/bridge/initReplBridge.ts |
| bridge permission callbacks | src/bridge/bridgePermissionCallbacks.ts |
| session ingress auth | src/utils/sessionIngressAuth.ts |
| managed settings | src/utils/settings/settings.ts |
| plugin-only policy | src/utils/settings/pluginOnlyPolicy.ts |
| workspace trust | src/components/TrustDialog/TrustDialog.tsx |
| hook trust / managed-only snapshot | src/utils/hooks.ts、src/utils/hooks/hooksConfigSnapshot.ts |
4.1 主线与场景特殊处理分类
| 机制/模块 | 分类 | 为什么这样归类 |
|---|---|---|
assembleToolPool() / filterToolsByDenyRules() | 安全/治理横切 | 在模型看到工具前收缩工具面,减少误调用可能 |
schema / validateInput() | 主线机制 + 安全横切 | 工具执行前的结构和业务参数校验,防止无效输入进入副作用点 |
runPreToolUseHooks() | 安全/治理横切 | 工具执行前的外部策略插入点,可 deny/ask/updateInput/add context |
hasPermissionsToUseTool() | 安全/治理横切 | 权限决策核心,合并规则、工具专属权限、mode、auto/headless 行为 |
| deny-first 规则 | 安全/治理横切 | deny 优先于 ask/allow/bypass,是权限系统的底线之一 |
| auto mode classifier | 场景增强 + 安全横切 | 只在 feature 和 auto/plan-auto 模式下触发;部分 safetyCheck 不可被 classifier 取代 |
| trusted settings source | 安全/治理横切 | 高风险 opt-in 排除 projectSettings,防止恶意项目自我提权 |
| path validation | 安全/治理横切 | 解析真实路径、glob base、tilde、UNC/path traversal、工作区和 sandbox allowlist |
| sandbox write allowlist | 场景增强 + 安全横切 | sandbox 开启时把允许写目录并入路径权限,但仍尊重 denyWithinAllow |
| MCP approval | 场景增强 + 安全横切 | 只在 MCP server / tool 存在时触发,负责外部工具来源治理 |
| bridge remote control gate | 场景增强 + 安全横切 | 只在 bridge enabled、OAuth 可用、组织策略允许时启动 |
| session ingress auth | 场景增强 + 安全横切 | remote / bridge 通道的认证头构造与 token 更新 |
| workspace trust | 安全/治理横切 | 对项目 hook、allowed tools 等可执行扩展面做信任确认 |
| managed hooks / plugin-only policy | 安全/治理横切 | 组织策略可限制 hook/customization 来源 |
| telemetry / transcript | 可观测/恢复支撑 | 不直接阻止风险,但提供决策来源、错误、审计和复盘基础 |
5. 机制拆解
5.1 安全不是只靠“权限弹窗”
工具调用主链在第 3 期已展开。安全复盘时要强调:权限 UI 只是 ask 分支的一部分。真正的边界包括:
- 工具是否被放进模型可见 tool pool。
- 工具输入是否通过 schema 和 validateInput。
- PreToolUse Hook 是否阻断或改写。
- 权限规则是否 deny / ask / allow。
- 工具自己的
checkPermissions()是否要求内容级确认。 - 当前 mode 是否允许 auto / bypass / dontAsk。
- UI、SDK、bridge、headless Hook 是否能给出最终决策。
因此不能把安全性等同于“用户点了一次允许”。
5.2 deny 优先,auto/bypass 不是无条件通行
permissions.ts 的权限流程先处理 deny、ask、工具专属 check、requiresUserInteraction、content ask 和 safetyCheck,再进入 bypass / allow / mode 逻辑。
auto mode 分支还有额外边界:
- 非 classifier-approvable safetyCheck 不能被 acceptEdits fast-path、safe-tool allowlist 或 classifier 自动批准。
- PowerShell 在
POWERSHELL_AUTO_MODE未开启时要求显式用户权限。 - Agent 和 REPL 被排除在 acceptEdits fast-path 之外,因为它们可能包含更复杂的内层工具或 VM 逃逸风险。
- classifier 出错会记录 error dump,不能默默当成 allow。
这些源码分支说明 auto mode 是“受限自动审批”,不是 YOLO 式绕过。
5.3 projectSettings 被排除在高风险 opt-in 之外
settings.ts 中多个函数显式排除 projectSettings:
hasSkipDangerousModePermissionPrompt():projectSettings 不能接受 bypass permissions mode dialog。hasAutoModeOptIn():projectSettings 不能接受 auto mode opt-in。getUseAutoModeDuringPlan():projectSettings 不能控制 plan mode 是否使用 auto semantics。getAutoModeConfig():projectSettings 不能注入 classifier allow/deny/environment 规则。
注释给出的原因很明确:恶意项目不应通过本地项目设置自我提权或注入 classifier 规则。这是 workspace trust 之外的另一层配置来源边界。
5.4 path validation 把“字符串路径”转成安全判断对象
pathValidation.ts 处理 glob base、tilde expansion、resolved path、UNC/path traversal、sandbox allowlist 等问题。
isPathInSandboxWriteAllowlist() 只有在 SandboxManager.isSandboxingEnabled() 时返回 true。即使路径在 sandbox allowOnly 中,也会先检查 denyWithinAllow,例如某些配置文件仍可被拒绝。这说明 sandbox allowlist 不是绝对覆盖 deny 规则。
isPathAllowed() 会按 operation type 区分 read/edit,并先检查 deny rules。路径权限不是简单字符串前缀匹配,而是结合 realpath/symlink 解析和工作路径判断。
5.5 sandbox 是权限系统的增强,不是所有 Bash 的默认执行模型
Bash 工具还有自己的权限和 sandbox 判断文件,例如 BashTool/bashPermissions.ts 与 BashTool/shouldUseSandbox.ts。sandbox 可让某些命令在受限环境中更安全地运行,也可影响 auto allow。但第 3 期已确认 sandbox auto allow 只对 Bash、sandbox 开启且命令适合 sandbox 时生效。
因此,研究中不能写“所有 shell 都在 sandbox 里执行”。正确表述是:sandbox 是按配置、平台、命令形态和工具判断介入的安全增强。
5.6 MCP 是外部工具边界,需要配置、审批和权限规则共同治理
MCP 工具进入 tool pool 前会经过 MCP client 状态、server tool 可用性、配置和 deny filtering。agent 选择也会检查 required MCP servers 是否真的有 tools available,连接但未认证的 server 不算可用。
services/mcpServerApproval.tsx 会为 pending project servers 展示 approval dialogs。settings permission validation 中 MCP rules 支持 server-level、tool-level 和 wildcard,但不允许带括号 pattern,这避免把非 MCP 的内容匹配语义错误套到 MCP server/tool 名称上。
MCP 的风险不是“本地工具风险”的重复,而是外部 server / channel / auth / elicitation 的组合边界。
5.7 bridge / remote control 必须过 runtime、OAuth 和组织策略
bridge/initReplBridge.ts 的启动顺序很明确:
isBridgeEnabledBlocking()runtime gate。getBridgeAccessToken(),没有 Claude.ai OAuth token 则失败并提示/login。waitForPolicyLimitsToLoad()后检查isPolicyAllowed('allow_remote_control')。- 对过期且刷新失败的 OAuth token 做 skip 和跨进程 backoff,避免 401 无限循环。
bridge 不是本地默认能力,而是受账号、组织策略和 token 状态控制的 remote-control 通道。structuredIO.ts 还处理 bridge control_response / control_cancel_request 与 SDK permission prompt 的竞争,避免 stale prompt 悬挂。
5.8 workspace trust 与 Hook trust 保护可执行扩展面
第 5 期已经确认 Hook 可以执行 command/prompt/http/agent/function 等 handler。utils/hooks.ts 在交互模式下要求 workspace trust;hooksConfigSnapshot.ts 处理 disableAllHooks、allowManagedHooksOnly 等 managed-only 策略。
TrustDialog.tsx 还会检查项目/本地 settings 或插件 command 中是否有 allowedTools 等风险内容,并询问用户是否信任当前 folder。它保护的是“项目配置可影响模型和工具行为”的边界。
5.9 多 agent 扩大风险面,靠权限冒泡和隔离收缩
第 7 期确认:
- Agent 类型受 deny rule 控制。
- fork agent 使用
permissionMode: 'bubble'。 - worktree isolation 可隔离文件副作用。
- in-process teammate 不能 spawn background agents。
- remote agent 要先过 eligibility 和 remote session 注册。
多 agent 的安全重点不是禁止拆分,而是让拆分后的子执行仍可见、可中断、可恢复,并把权限请求带回父会话或宿主。
6. 对稳定解决问题能力的贡献
6.1 降低模型幻觉造成真实副作用的概率
工具可见性、schema、validateInput、permission、sandbox 和 Hook 分层拦截,把“模型想做”变成“经过验证后才能做”。
6.2 防止项目配置自我提权
高风险 opt-in 排除 projectSettings,workspace trust 保护项目 hooks / allowedTools,managed settings 可以限制非 managed hooks。这些都针对“打开陌生仓库即被配置劫持”的风险。
6.3 让无交互场景仍有明确拒绝路径
headless / async / bridge 场景无法弹 UI 时,权限系统会尝试 PermissionRequest Hook 或宿主回调;没有可用决策时应 deny,而不是默认 allow。
6.4 外部连接有独立信任边界
MCP、bridge、remote agent、session ingress 都不是普通本地函数调用。源码为它们设置 auth、approval、policy、channel 和恢复逻辑。
6.5 安全事件可审计
权限来源、auto mode decision、API errors、bridge skips、hook events、tool result 和 transcript 都会进入日志/telemetry/session storage,为复盘提供证据。
7. 风险、边界与待验证问题
| 风险/边界 | 影响 | 防御性解读 |
|---|---|---|
| 权限路径高度分支化 | 新增工具或 mode 容易漏接 deny/ask 语义 | 应优先复用 checkPermissionsAndCallTool() 和 hasPermissionsToUseTool() |
| auto classifier 依赖模型判断 | 分类错误可能误判风险 | 源码通过 safetyCheck 免疫、PowerShell guard、Agent/REPL fast-path 排除、denial tracking 降低风险 |
| sandbox 条件化 | 误以为所有 Bash 都被隔离会低估风险 | 文档和 UI 应持续区分 sandboxed allow 与普通 permission allow |
| MCP server 是外部信任域 | server 工具描述、auth、elicitation、channel 都可能影响行为 | MCP approval、rule validation、server/tool filtering 是必要但不等于完整供应链审计 |
| bridge/remote control 依赖账号与策略 | OAuth/组织策略/网络状态变化会改变能力 | bridge 启动 gate 和 token backoff 避免默认开放和认证死循环 |
8. 下一期衔接
第 9 期进入“可观测性、审计与持久化”。安全边界只有在可复盘时才真正可治理。下一期将分析 transcript JSONL、parentUuid chain、content replacement、session restore、task sidecar、progress state、analytics、OTel 和 debug logs 如何支撑恢复、审计和调试。
机制主线收束
站在架构师视角,我会把这组源码机制收束为三个问题:为什么需要它,什么时候必须引入它,以及真正要设计的是什么。
- why:安全边界解决的是模型错误、恶意项目配置、外部连接和多 agent 放大真实副作用的问题。
- when:只要 agent 接触文件系统、shell、MCP、remote、hooks、项目配置,就必须做分层安全。
- what:配置源可信、工具可见、输入合法、权限通过、路径安全、外部连接可审批、执行可审计,副作用才应该发生。
安全与信任边界这组源码最值得带走的,不是某个函数或某个配置项,而是它如何把模型能力放进一组可验证、可拒绝、可恢复、可审计的工程边界里。读源码时,如果只记住名词,会很快散;如果抓住 why、when、what,就能把这组机制迁移到自己的 agent 架构判断中。