Appearance
08 — 权限与安全
概述
权限系统是 Claude Code 最重要的安全机制。它的设计原则是:AI 的所有写操作都必须经过用户授权。但在不同场景下,"授权"的含义不同——手动确认每个操作太慢,全自动又不安全。因此系统提供了多级权限模式和多源决策机制来平衡安全性和效率。
权限检查全流程
当模型调用一个 Tool 时,权限系统按以下顺序执行检查:
权限模式
| 模式 | 行为 | 适用场景 |
|---|---|---|
default | 只读自动允许,写操作检查规则 | 日常使用(默认) |
plan | 大部分写操作需确认 | 需要谨慎审查时 |
auto | 大部分操作自动允许(基于 ML 分类器) | 信任模型、追求效率 |
bypassPermissions | 所有操作自动允许 | 完全信任(需明确 opt-in) |
模式存储在 AppState.toolPermissionContext.mode 中,用户可通过 /permissions 切换。
权限规则 (ToolPermissionContext)
除了模式,还有精细的 规则集 控制单个工具的行为:
typescript
type ToolPermissionContext = {
mode: PermissionMode
alwaysAllowRules: ToolPermissionRulesBySource // 始终允许
alwaysDenyRules: ToolPermissionRulesBySource // 始终拒绝
alwaysAskRules: ToolPermissionRulesBySource // 始终询问
// ...
}规则来自多个来源(按优先级从高到低):
- MDM 受管设置 — 企业管理员强制下发
- 远程受管设置 — 通过 API 下发
- 用户全局设置 —
~/.claude/settings.json - 项目设置 —
.claude/settings.json - 运行时授权 — 用户在弹窗中选择"Always allow"
四方竞速决策 — 核心创新
当权限结果是 ask(需要确认)时,系统 同时启动四个决策源,第一个返回结果的胜出。这是一个 "race" 模式,通过原子性的 claim() 函数确保只有一个决策生效。
为什么是竞速而不是串行?
- 用户在终端: 弹窗出现,用户按 y/n 确认
- 用户在 IDE: 同时在 VS Code 侧边栏看到权限请求,可以直接点击按钮
- 自动化场景: Hook 脚本自动判断是否安全(如检查命令是否在白名单中)
- ML 分类器: 对 Bash 命令进行自动安全评估
这四种来源可能同时活跃,用户可以从任何一个入口响应。竞速模式确保最快的响应立即生效,不会互相阻塞。
claim() 原子操作
typescript
const { resolve: resolveOnce, isResolved, claim } = createResolveOnce(resolve)
// 在每个决策回调中:
onAllow: async () => {
if (!claim()) return // 已经有人先响应了,忽略
resolveOnce({ decision: 'accept', source: 'user' })
}claim() 返回 true 表示"我是第一个响应的",返回 false 表示"已经有其他人先响应了"。这保证了即使四个源同时返回,也不会出现重复决策。
ML 分类器 (Bash 专用)
分类器特别处理:
- 收到 Bash 命令后,在后台启动分类
- 如果 200ms 内 用户没有交互,分类器结果可以自动生效
- 如果分类器批准,显示 ✓ 标记 1-3 秒后自动消失
- 如果分类器拒绝,更新 UI 但用户仍可手动覆盖
200ms 的 "grace period" 是为了避免用户正在输入时被分类器的自动决策打断。
BashTool 安全层
BashTool 是最高风险的工具,因此有 独立于通用权限系统的多层安全检查:
每一层都可以独立拒绝命令,提供不同粒度的安全保护。
沙箱 (utils/sandbox/)
macOS 上使用 sandbox-exec (Seatbelt) 隔离不可信命令:
| 限制 | 说明 |
|---|---|
| 文件系统 | 只允许访问工作目录及其子目录 |
| 网络 | 限制网络访问(可配置) |
| 进程 | 限制子进程创建 |
| 系统调用 | 禁止危险系统调用 |
沙箱的启用条件由 shouldUseSandbox.ts 判断,通常对未明确授权的 Bash 命令启用。
用户 Hooks (utils/hooks/)
Hooks 允许用户在工具执行前后运行自定义 Shell 脚本。这是一种 外挂式安全检查 机制。
配置格式
在 settings.json 中配置:
json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"command": "python3 ~/scripts/check-bash-safety.py"
}
],
"PostToolUse": [
{
"matcher": "FileWrite",
"command": "prettier --write $CLAUDE_FILE_PATH"
}
]
}
}Hook 事件类型
| 事件 | 触发时机 | 可以做什么 |
|---|---|---|
PreToolUse | 工具执行前 | 阻止执行 / 修改输入 |
PostToolUse | 工具执行后 | 格式化文件 / 注入上下文 |
SessionStart | 会话开始时 | 初始化环境 |
SessionEnd | 会话结束时 | 清理资源 |
Notification | 收到通知时 | 自定义通知 |
Hook 执行模型
Hook 可以阻止工具执行:如果 Hook 脚本以退出码 2 退出,并在 stdout 输出 {"decision": "block"},工具调用会被阻止,模型会收到阻止原因。
权限持久化
当用户在弹窗中选择 "Always allow for this tool" 时,规则会被写入 ~/.claude/settings.json,下次会话自动恢复。持久化通过 onChangeAppState.ts 的状态变更监听实现。
拒绝追踪 (denialTracking.ts): 记录用户最近拒绝过的操作,避免在同一会话中反复弹出相同的权限请求。
权限相关文件索引
逻辑层
| 文件 | 说明 |
|---|---|
utils/permissions/PermissionMode.ts | 模式定义 |
utils/permissions/denialTracking.ts | 拒绝追踪 |
hooks/toolPermission/PermissionContext.ts | 权限上下文 |
hooks/toolPermission/permissionLogging.ts | 权限日志 |
hooks/toolPermission/handlers/interactiveHandler.ts | 交互式处理(四方竞速核心实现) |
hooks/toolPermission/handlers/coordinatorHandler.ts | Coordinator 模式处理 |
hooks/toolPermission/handlers/swarmWorkerHandler.ts | Swarm Worker 处理 |
hooks/useCanUseTool.tsx | canUseTool() Hook 主入口 |
UI 层
| 文件 | 说明 |
|---|---|
components/permissions/PermissionRequest.tsx | 权限弹窗框架 |
components/permissions/BashPermissionRequest/ | Bash 专用审批 |
components/permissions/FileEditPermissionRequest/ | 文件编辑 Diff + 审批 |
components/permissions/FileWritePermissionRequest/ | 文件写入审批 |
components/permissions/WebFetchPermissionRequest/ | 网络请求审批 |
Bash 安全层
| 文件 | 说明 |
|---|---|
tools/BashTool/bashSecurity.ts | 注入检测 |
tools/BashTool/bashPermissions.ts | 权限判断 |
tools/BashTool/commandSemantics.ts | 语义分析 |
tools/BashTool/destructiveCommandWarning.ts | 危险检测 |
tools/BashTool/pathValidation.ts | 路径校验 |
tools/BashTool/shouldUseSandbox.ts | 沙箱决策 |