Appearance
第9章 多模式权限模型
开篇引言
在一个能够读写文件、执行 Shell 命令、启动子进程的 AI 编程助手中,权限控制是安全的最后一道防线。Claude Code 面临的核心挑战是:如何在保障安全的前提下,尽可能减少用户的审批疲劳?
传统的权限模型往往走向两个极端:要么对每一步操作都弹出确认对话框,导致用户体验极度割裂;要么一旦授权就不再过问,留下巨大的安全隐患。Claude Code 选择了第三条路径------一个多层次、多维度的权限决策框架。它融合了静态规则匹配、动态 AI 分类器、多种权限模式、以及对拒绝行为的智能追踪,构建出一套既灵活又安全的权限治理体系。
本章将从源码层面深入剖析这套权限模型的完整设计。我们将看到,一个看似简单的"是否允许执行"的判断,背后是一条精心编排的多阶段决策流水线。从静态规则的解析匹配,到动态分类器的语义推理,再到多通道竞争的用户交互,每一个环节都经过了深思熟虑的工程设计。
理解这套权限模型,不仅有助于我们更好地使用 Claude Code,也为我们在其他 AI 系统中设计类似的安全治理机制提供了一个极具参考价值的工程范本。
本章要点
- Claude Code 定义了七种权限模式(default、acceptEdits、plan、bypassPermissions、dontAsk、auto、bubble),每种模式对应不同的安全与效率平衡点
useCanUseToolHook 是权限系统的中枢调度器,将静态规则判定、动态分类器评估、用户交互三个阶段串联为一条完整的决策流水线- 静态规则系统采用三类规则(allowRules、denyRules、alwaysAskRules)与多层来源优先级机制,实现细粒度的权限声明
- Bash 分类器对命令进行语义级安全分析,Transcript 分类器(auto mode)基于完整对话上下文进行 AI 推理决策
- 拒绝追踪机制通过连续拒绝计数和总拒绝计数,在自动模式下实现"安全降级"------超过阈值后自动回退到人工审批
- 多 Agent 场景下,swarm worker 通过邮箱机制将权限请求转发给 leader,coordinator 则在显示交互对话框前先行运行自动化检查
9.1 权限模式总览
Claude Code 的七种权限模式构成了一个从最严格到最宽松的安全频谱,下图展示了各模式之间的信任层级关系:
9.1.1 模式类型定义
Claude Code 的权限模式定义在 src/types/permissions.ts 中,分为外部模式和内部模式两个层次:
typescript
// 文件: src/types/permissions.ts
export const EXTERNAL_PERMISSION_MODES = [
'acceptEdits',
'bypassPermissions',
'default',
'dontAsk',
'plan',
] as const
export type ExternalPermissionMode = (typeof EXTERNAL_PERMISSION_MODES)[number]
// 内部模式 = 外部模式 + auto + bubble
export type InternalPermissionMode = ExternalPermissionMode | 'auto' | 'bubble'
export type PermissionMode = InternalPermissionMode这里有一个精妙的分层设计:外部模式(ExternalPermissionMode)是面向用户的、可以在设置文件和 CLI 参数中指定的模式;内部模式(InternalPermissionMode)则额外包含了 auto 和 bubble 两个仅在特定构建条件下存在的模式。auto 模式依赖 TRANSCRIPT_CLASSIFIER 功能开关,仅在 Anthropic 内部版本中可用。
为什么要区分内部模式和外部模式?核心原因是功能门控(feature gating)和构建时消除(dead code elimination)。Bun 的打包器会在构建时评估 feature() 调用,对于外部发布版本,TRANSCRIPT_CLASSIFIER 返回 false,因此 auto 模式相关的代码在编译阶段就被完全移除,不会增加外部版本的包体积。PERMISSION_MODES 常量使用 satisfies 类型约束确保运行时数组与编译时类型保持一致,这是 TypeScript 高级类型编程的一个典型应用。
9.1.2 各模式的行为语义
每种权限模式在 src/utils/permissions/PermissionMode.ts 中配置了对应的显示信息:
typescript
// 文件: src/utils/permissions/PermissionMode.ts
const PERMISSION_MODE_CONFIG: Partial<
Record<PermissionMode, PermissionModeConfig>
> = {
default: {
title: 'Default',
shortTitle: 'Default',
symbol: '',
color: 'text',
external: 'default',
},
plan: {
title: 'Plan Mode',
shortTitle: 'Plan',
symbol: PAUSE_ICON,
color: 'planMode',
external: 'plan',
},
bypassPermissions: {
title: 'Bypass Permissions',
shortTitle: 'Bypass',
symbol: '⏵⏵',
color: 'error',
external: 'bypassPermissions',
},
// ...
}以下是各模式的核心行为差异:
default 模式 是最标准的工作模式,也是大多数用户日常使用的模式。每次工具调用都会经过完整的权限检查流程:先匹配静态规则(deny/ask/allow),然后根据工具自身的 checkPermissions 方法判断,最后对未匹配的操作弹出用户确认对话框。在这个模式下,用户对每一个未被规则覆盖的操作都拥有完整的审批权。系统会展示操作详情、提供"始终允许"选项(生成持久化的 allow 规则),以及"拒绝并提供反馈"选项。这是安全性与可用性的基础平衡点。
acceptEdits 模式 是 default 的宽松变体,专为代码编写密集型任务设计。它在 default 的基础上,自动放行文件编辑类操作(Edit、Write、NotebookEdit 工具在工作目录内的修改)以及特定的文件系统 Bash 命令。这大幅减少了日常编码场景中的权限弹窗数量,同时仍然对网络请求、进程管理等高风险操作保持审批要求。在 src/tools/BashTool/modeValidation.ts 中,可以看到被自动允许的命令列表:
typescript
// 文件: src/tools/BashTool/modeValidation.ts
const ACCEPT_EDITS_ALLOWED_COMMANDS = [
'mkdir', 'touch', 'rm', 'rmdir', 'mv', 'cp', 'sed',
] as constplan 模式 是一种"只读规划"模式。在此模式下,Claude 只进行思考和规划,不实际执行任何修改操作。这对于复杂任务的前期分析阶段非常有用------用户可以先让 Claude 输出一个详细的执行计划,确认方案合理后再切换到执行模式。plan 模式还有一个特殊行为:如果用户进入 plan 模式之前是 bypassPermissions 模式,那么退出 plan 模式时会恢复到 bypassPermissions 模式而非 default 模式,这通过 prePlanMode 字段来记录。
bypassPermissions 模式 跳过绝大多数权限检查,使 Claude 能够不受中断地连续执行操作。然而,"绕过"并非"无视"------它仍然尊重三类不可绕过的安全约束:deny 规则(步骤 1a 的明确拒绝)、content-specific ask 规则(步骤 1f,如 Bash(npm publish:*) 这类用户刻意设置的审批点)、以及安全检查(步骤 1g,对 .git/、.claude/、.vscode/、shell 配置文件等敏感路径的保护)。这一设计确保了即使在最宽松的模式下,核心安全底线仍然不可突破。此模式需要用户通过 --dangerously-skip-permissions 命令行参数显式启用,名称中的"dangerously"前缀本身就是一种风险提示。
dontAsk 模式 将所有本应弹出用户确认的 ask 决策自动转换为 deny。这意味着 Claude 不会中断用户的工作流,但也不会执行任何未被规则明确允许的操作。这个模式非常适合 CI/CD 等自动化管道场景------在这些场景中没有人可以回答权限弹窗,与其让进程挂起等待,不如安全地拒绝并让 Claude 尝试其他方案。
auto 模式 是权限系统中最复杂也最具创新性的模式,它用 AI 分类器完全替代人工审批。当权限检查的初步结果为 ask 时,auto 模式不会弹出对话框,而是调用 Transcript 分类器对当前操作在完整对话上下文中进行安全评估。分类器批准则自动执行,拒绝则自动阻止。这种设计的核心理念是:AI 可以理解操作的意图和上下文,比简单的规则匹配做出更精准的安全判断。不过,auto 模式也配备了多层安全网,包括拒绝追踪、危险权限剥离、以及不可分类器审批的安全检查,这些将在后续章节详述。
bubble 模式 是内部使用的特殊模式,用于多 Agent 架构中权限请求的向上传递场景。当子 Agent 遇到需要权限审批的操作时,可以将请求"冒泡"到父 Agent 或最终的用户界面。
9.1.3 模式切换机制
用户可以通过 Shift+Tab 快捷键循环切换权限模式。切换逻辑定义在 src/utils/permissions/getNextPermissionMode.ts 中:
typescript
// 文件: src/utils/permissions/getNextPermissionMode.ts
export function getNextPermissionMode(
toolPermissionContext: ToolPermissionContext,
): PermissionMode {
switch (toolPermissionContext.mode) {
case 'default':
return 'acceptEdits'
case 'acceptEdits':
return 'plan'
case 'plan':
if (toolPermissionContext.isBypassPermissionsModeAvailable) {
return 'bypassPermissions'
}
if (canCycleToAuto(toolPermissionContext)) {
return 'auto'
}
return 'default'
case 'bypassPermissions':
if (canCycleToAuto(toolPermissionContext)) {
return 'auto'
}
return 'default'
default:
return 'default'
}
}注意两个关键的条件守卫:isBypassPermissionsModeAvailable 确保 bypassPermissions 只在用户主动启用时可用,防止普通用户意外进入高风险模式;canCycleToAuto 同时检查 auto 模式的 feature gate 状态和运行时可用性标志,确保只有在分类器服务正常可用时才允许切换。模式切换不是简单的循环列表,而是一个基于当前上下文动态计算的有向图------每个节点的出边取决于当前的运行环境配置。