在前面的文章中,我们解析了 Claude Code 的命令解析、输入处理和状态管理。本文将深入探讨 Claude Code 最为关键的安全基础设施——权限与审批系统(Permission & Approval System)。作为一个能够在用户本地执行 Shell 命令、读写文件、发起网络请求的 AI Agent,Claude Code 必须在"智能自动化"与"安全可控"之间找到精确的平衡点。这套权限系统就是维持这一平衡的核心机制。
一、权限架构总览
Claude Code 的权限系统采用了分层规则 + 多模式控制的架构设计。整个权限模块分布在两个核心目录中:
src/types/permissions.ts(约 13KB):纯类型定义层,打破循环依赖src/utils/permissions/:24 个实现文件,涵盖规则解析、权限检查、分类器、状态管理等src/components/permissions/:40+ 个 UI 组件,负责各类审批对话框的渲染与交互
flowchart TD
subgraph TypeLayer["类型定义层"]
PT[src/types/permissions.ts]
end
subgraph UtilsLayer["权限逻辑层"]
PERM[src/utils/permissions/permissions.ts]
RULE[src/utils/permissions/PermissionRule.ts]
RESULT[src/utils/permissions/PermissionResult.ts]
SHELL[src/utils/permissions/shellRuleMatching.ts]
FS[src/utils/permissions/filesystem.ts]
CLASS[src/utils/permissions/classifierDecision.ts]
YOLO[src/utils/permissions/yoloClassifier.ts]
DENIAL[src/utils/permissions/denialTracking.ts]
end
subgraph UILayer["审批 UI 层"]
PREQ[src/components/permissions/PermissionRequest.tsx]
PDIA[src/components/permissions/PermissionDialog.tsx]
BPREQ[src/components/permissions/BashPermissionRequest/]
FPREQ[src/components/permissions/FileEditPermissionRequest/]
end
PT --> PERM
PT --> RULE
PT --> RESULT
PERM --> SHELL
PERM --> FS
PERM --> CLASS
PERM --> YOLO
PERM --> DENIAL
PERM --> PREQ
PREQ --> PDIA
PREQ --> BPREQ
PREQ --> FPREQ1.1 权限模式(Permission Mode)
Claude Code 定义了 7 种权限模式,按安全等级从低到高排列:
| 模式 | 说明 | 安全等级 |
|---|---|---|
bypassPermissions | 绕过所有权限检查 | ⚠️ 最低 |
dontAsk | 静默拒绝所有需要审批的操作 | 低 |
auto | 自动模式,由 AI 分类器决定是否允许 | 中 |
default | 默认模式,每次需要审批时弹出提示 | 中 |
acceptEdits | 自动接受工作目录内的文件编辑 | 中高 |
plan | 计划模式,所有操作需要显式确认 | 高 |
bubble | 内部调试用 | - |
这些模式定义在 src/types/permissions.ts 第 15~33 行:
export const EXTERNAL_PERMISSION_MODES = [
'acceptEdits',
'bypassPermissions',
'default',
'dontAsk',
'plan',
] as const
export type ExternalPermissionMode = (typeof EXTERNAL_PERMISSION_MODES)[number]
export type InternalPermissionMode = ExternalPermissionMode | 'auto' | 'bubble'用户可通过 Shift+Tab 在部分模式之间循环切换,切换逻辑由 src/utils/permissions/getNextPermissionMode.ts 第 23~72 行实现。对于 Ant 内部用户,acceptEdits 和 plan 被跳过,直接提供 auto 和 bypassPermissions 选项。
1.2 权限行为三元组
每条权限规则的行为只能是三种之一(src/types/permissions.ts 第 39 行):
export type PermissionBehavior = 'allow' | 'deny' | 'ask'allow:无条件允许该工具/操作执行deny:无条件拒绝该工具/操作执行ask:强制弹出审批对话框,要求用户确认
1.3 权限规则的结构
权限规则由来源(Source)、行为(Behavior)和值(Value)三部分组成(src/types/permissions.ts 第 48~62 行):
export type PermissionRuleSource =
| 'userSettings' | 'projectSettings' | 'localSettings'
| 'flagSettings' | 'policySettings' | 'cliArg' | 'command' | 'session'
export type PermissionRuleValue = {
toolName: string
ruleContent?: string
}
export type PermissionRule = {
source: PermissionRuleSource
ruleBehavior: PermissionBehavior
ruleValue: PermissionRuleValue
}ruleContent 是可选的精细化匹配内容。例如 Bash(prefix:*) 表示匹配以 prefix 开头的 Bash 命令,而 Bash(无 ruleContent)则表示匹配所有 Bash 命令。
二、分类审批机制
Claude Code 对不同类型的操作实施了差异化的审批策略。每种工具都有独立的 checkPermissions() 方法,实现了细粒度的安全检查。
2.1 文件读写权限
文件操作是最频繁也是风险最高的操作之一。文件权限检查集中在 src/utils/permissions/filesystem.ts 和各个文件工具的 checkPermissions 方法中。
敏感文件保护(src/utils/permissions/filesystem.ts 第 37~57 行):
export const DANGEROUS_FILES = [
'.gitconfig', '.gitmodules', '.bashrc', '.bash_profile',
'.zshrc', '.zprofile', '.profile', '.ripgreprc',
'.mcp.json', '.claude.json',
] as const
export const DANGEROUS_DIRECTORIES = [
'.git', '.vscode', '.idea', '.claude',
] as const对这些敏感路径的编辑会触发 safetyCheck 类型的审批要求,且即使在 bypassPermissions 模式下也无法绕过(src/utils/permissions/permissions.ts 第 1273~1283 行)。这是系统最后的防线之一。
审批粒度:
- 路径级:检查操作是否在允许的工作目录内
- 工具级:
FileReadTool、FileWriteTool、FileEditTool各自有独立的审批规则 - 内容级:Notebook 编辑、Sed 替换等特殊操作有额外的安全校验
2.2 Shell 执行权限
Shell 命令的审批是权限系统中最复杂的部分,涉及多层检查机制。
规则匹配语法(src/utils/permissions/shellRuleMatching.ts 第 22~47 行):
export type ShellPermissionRule =
| { type: 'exact'; command: string }
| { type: 'prefix'; prefix: string }
| { type: 'wildcard'; pattern: string }- 精确匹配:
npm install只匹配该命令 - 前缀匹配:
npm:*匹配所有以npm开头的命令 - 通配符匹配:
npm *使用*匹配任意字符序列(支持转义\*匹配字面星号)
危险命令检测:src/utils/permissions/permissionSetup.ts 第 82~137 行的 isDangerousBashPermission() 函数会阻止用户配置过于宽泛的 Bash 权限规则。例如 python:*、node:* 等解释器前缀规则在 auto 模式下会被标记为危险并移除,防止分类器被绕过。
沙箱自动授权:当 SandboxManager.isAutoAllowBashIfSandboxedEnabled() 启用且命令将在沙箱中执行时,部分审批可被自动跳过(src/utils/permissions/permissions.ts 第 1193~1207 行)。
2.3 网络请求权限
网络请求通过 WebFetchTool 实现,其审批对话框为 WebFetchPermissionRequest。网络权限的粒度相对粗粒度:
- 工具级:允许/拒绝所有
WebFetchTool调用 - URL 级:可在规则中指定特定 URL 前缀或模式
与本地文件操作不同,网络请求没有 safetyCheck 级别的硬保护,完全依赖规则系统和审批对话框。
2.4 MCP Server 权限
MCP(Model Context Protocol)工具具有特殊的权限命名空间。在 src/utils/permissions/permissions.ts 第 233~261 行的 toolMatchesRule() 函数中,MCP 工具的权限匹配逻辑如下:
const ruleInfo = mcpInfoFromString(rule.ruleValue.toolName)
const toolInfo = mcpInfoFromString(nameForRuleMatch)
return (
ruleInfo !== null &&
toolInfo !== null &&
(ruleInfo.toolName === undefined || ruleInfo.toolName === '*') &&
ruleInfo.serverName === toolInfo.serverName
)这意味着:
- 规则
mcp__server1可授权 Server1 的所有工具 - 规则
mcp__server1__*同样匹配 Server1 的所有工具 - 规则
mcp__server1__tool1仅匹配特定工具
MCP 工具的权限审批通过通用的 PermissionRequest 机制处理,没有单独的 MCP 特殊审批对话框。
三、审批对话框组件
当权限检查返回 ask 行为时,系统会渲染对应的审批对话框。所有对话框组件位于 src/components/permissions/ 目录。
3.1 组件架构
flowchart LR
A[PermissionRequest.tsx] --> B[PermissionDialog.tsx]
A --> C[PermissionPrompt.tsx]
A --> D[BashPermissionRequest]
A --> E[FileWritePermissionRequest]
A --> F[FileEditPermissionRequest]
A --> G[WebFetchPermissionRequest]
A --> H[FallbackPermissionRequest.tsx]
B --> I[PermissionRequestTitle.tsx]
B --> J[WorkerBadge.tsx]
C --> K[Select 组件]
C --> L[Feedback 输入框]
D --> M[PermissionRuleExplanation.tsx]
D --> N[PermissionExplanation.tsx]核心组件职责:
PermissionRequest.tsx(第 78~128 行):入口分发器,根据工具类型选择对应的审批组件PermissionDialog.tsx:统一的对话框容器,提供边框、标题、工作区徽章等通用布局PermissionPrompt.tsx:通用选项提示组件,处理 "允许/拒绝/总是允许/总是拒绝" 等标准交互BashPermissionRequest/:最复杂的 Shell 审批组件,集成分类器状态显示、破坏性命令警告、规则解释等
3.2 Bash 审批对话框的特殊设计
BashPermissionRequest(src/components/permissions/BashPermissionRequest/BashPermissionRequest.tsx)是审批 UI 中最复杂的组件,因为它需要处理:
- Sed 编辑检测:如果命令是
sed替换操作,自动跳转到SedEditPermissionRequest - 分类器检查动画:当 Bash Classifier 正在评估命令安全性时,显示闪烁的 "Attempting to auto-approve…" 提示(
ClassifierCheckingSubtitle组件) - 破坏性命令警告:集成
getDestructiveCommandWarning(),对rm、drop table等危险操作加红警示 - 权限解释器:通过
PermissionExplanation.tsx提供按 Ctrl+E 触发的 AI 生成的风险解释(低/中/高风险分级)
3.3 用户交互与记住选择
当用户在审批对话框中做出选择时,系统会将选择转化为 PermissionUpdate 并持久化。更新操作定义在 src/types/permissions.ts 第 68~106 行:
export type PermissionUpdate =
| { type: 'addRules'; destination: PermissionUpdateDestination; rules: PermissionRuleValue[]; behavior: PermissionBehavior }
| { type: 'replaceRules'; destination: PermissionUpdateDestination; rules: PermissionRuleValue[]; behavior: PermissionBehavior }
| { type: 'removeRules'; destination: PermissionUpdateDestination; rules: PermissionRuleValue[]; behavior: PermissionBehavior }
| { type: 'setMode'; destination: PermissionUpdateDestination; mode: ExternalPermissionMode }
| { type: 'addDirectories'; destination: PermissionUpdateDestination; directories: string[] }
| { type: 'removeDirectories'; destination: PermissionUpdateDestination; directories: string[] }用户可以选择:
- Allow once:仅允许本次执行
- Always allow:添加
allow规则到会话或设置 - Deny:拒绝本次执行
- Always deny:添加
deny规则
"记住选择"通过 applyPermissionUpdate() 和 persistPermissionUpdates() 两个步骤完成:先更新内存中的权限上下文,再写入到对应的设置文件(用户设置、项目设置或本地设置)。
3.4 权限规则解释器
PermissionRuleExplanation.tsx 负责向用户展示为什么某个操作需要审批。它支持多种决策原因类型的文本生成(第 22~73 行):
rule:显示触发审批的具体规则及其来源配置hook:显示 PreToolUse Hook 拦截的原因safetyCheck:显示敏感路径或安全检查失败的原因classifier:显示分类器(bash或auto-mode)的拦截原因mode:显示当前权限模式导致的审批要求
四、Bypass 模式与安全阀
bypassPermissions 是权限系统中最激进的模式,它跳过大部分审批检查,让用户获得近乎无限制的自动化体验。但这也带来了最大的安全风险。
4.1 Bypass 何时生效
在 src/utils/permissions/permissions.ts 第 1285~1299 行,bypassPermissions 模式的生效逻辑如下:
const shouldBypassPermissions =
appState.toolPermissionContext.mode === 'bypassPermissions' ||
(appState.toolPermissionContext.mode === 'plan' &&
appState.toolPermissionContext.isBypassPermissionsModeAvailable)
if (shouldBypassPermissions) {
return {
behavior: 'allow',
updatedInput: getUpdatedInputOrFallback(toolPermissionResult, input),
decisionReason: { type: 'mode', mode: appState.toolPermissionContext.mode },
}
}注意:即使 bypass 模式也无法绕过以下检查:
- Deny 规则(第 1167~1177 行):显式拒绝规则始终优先
- Safety Check(第 1273~1283 行):
.git/、.claude/等敏感路径的编辑仍需审批 - 显式 Ask 规则(第 1257~1271 行):用户配置的
ask规则仍会被尊重 - 需要用户交互的工具(第 1245~1255 行):如
AskUserQuestionTool等必须人工介入的工具
4.2 Bypass Kill Switch
为了防止 bypass 模式在组织层面被滥用,Claude Code 实现了 Kill Switch 机制(src/utils/permissions/bypassPermissionsKillswitch.ts)。
该模块通过 checkAndDisableBypassPermissionsIfNeeded() 在会话启动时检查 GrowthBook Feature Gate shouldDisableBypassPermissions。如果组织策略禁用了 bypass 模式,系统会自动将用户的权限模式降级为 default,且 isBypassPermissionsModeAvailable 被置为 false。
export async function checkAndDisableBypassPermissionsIfNeeded(
toolPermissionContext: ToolPermissionContext,
setAppState: (f: (prev: AppState) => AppState) => void,
): Promise<void> {
if (bypassPermissionsCheckRan) return
bypassPermissionsCheckRan = true
if (!toolPermissionContext.isBypassPermissionsModeAvailable) return
const shouldDisable = await shouldDisableBypassPermissions()
if (!shouldDisable) return
setAppState(prev => ({
...prev,
toolPermissionContext: createDisabledBypassPermissionsContext(prev.toolPermissionContext),
}))
}此检查只运行一次,并在用户重新登录(/login)时通过 resetBypassPermissionsCheck() 重置,确保组织策略变更能及时生效。
4.3 进入 Auto 模式时的权限清理
当用户从 bypassPermissions 切换到 auto 模式时,permissionSetup.ts 中的 transitionPermissionMode() 会自动剥离危险权限规则。这包括:
- 过于宽泛的 Bash 规则(如
Bash无内容、python:*) - 危险的 PowerShell 规则(如
iex相关模式) - 不受管理的权限规则(如果组织策略要求仅允许托管规则)
五、权限检查链路
工具调用前的权限检查是整个系统的核心安全链路。主入口函数 hasPermissionsToUseTool 位于 src/utils/permissions/permissions.ts 第 473 行,它是一个超过 500 行的复杂函数, orchestrates 了完整的审批决策流程。
5.1 检查链路流程图
flowchart TD
Start([工具调用请求]) --> Step1[Step 1: 规则检查]
Step1 --> Step1a[1a. Deny规则匹配?]
Step1a -->|是| Deny1[返回 deny]
Step1a -->|否| Step1b[1b. Ask规则匹配?]
Step1b -->|是| Step1bSandbox[沙箱可自动授权?]
Step1bSandbox -->|是| Step1c
Step1bSandbox -->|否| Ask1[返回 ask]
Step1b -->|否| Step1c[1c. 工具checkPermissions]
Step1c --> Step1d[1d. 工具拒绝?]
Step1d -->|是| Deny2[返回 deny]
Step1d -->|否| Step1e[1e. 需要用户交互?]
Step1e -->|是且ask| Ask2[返回 ask]
Step1e -->|否| Step1f[1f. 显式Ask规则?]
Step1f -->|是| Ask3[返回 ask]
Step1f -->|否| Step1g[1g. SafetyCheck?]
Step1g -->|是| Ask4[返回 ask
bypass也无法跳过]
Step1g -->|否| Step2[Step 2: 模式检查]
Step2 --> Step2a[2a. bypassPermissions?]
Step2a -->|是| Allow1[返回 allow]
Step2a -->|否| Step2b[2b. AlwaysAllow规则?]
Step2b -->|是| Allow2[返回 allow]
Step2b -->|否| Step3[Step 3: 模式转换]
Step3 --> Step3a[dontAsk模式?]
Step3a -->|是| Deny3[返回 deny]
Step3a -->|否| Step3b[auto/plan模式?]
Step3b -->|是| Classifier[运行分类器]
Classifier -->|blocked| Deny4[返回 deny]
Classifier -->|allowed| Allow3[返回 allow]
Classifier -->|unavailable| Ask5[回退到 ask]
Step3b -->|否| Step3c[headless代理?]
Step3c -->|是| Hook[运行Permission Hook]
Hook -->|有决策| ReturnHook[返回 hook 决策]
Hook -->|无决策| Deny5[返回 deny]
Step3c -->|否| Ask6[返回 ask
弹出审批对话框]5.2 内层检查:hasPermissionsToUseToolInner
hasPermissionsToUseToolInner(permissions.ts 第 1158~1317 行)是链路的核心,它按严格顺序执行以下检查:
Step 1 — 规则与工具检查(不可被 bypass):
- 1a Deny 规则:全局 deny 规则最先检查,立即拒绝
- 1b Ask 规则:全局 ask 规则,除非沙箱可自动授权
- 1c 工具自定义检查:调用
tool.checkPermissions(parsedInput, context) - 1d 工具拒绝:工具层面拒绝(如 Bash 子命令规则不匹配)
- 1e 用户交互需求:
tool.requiresUserInteraction?.()为真时必须人工介入 - 1f 内容级 Ask 规则:如
Bash(npm publish:*)这种精细化 ask 规则 - 1g Safety Check:敏感路径安全检查,bypass 免疫
Step 2 — 模式级快速通道:
- 2a bypassPermissions:如果模式为 bypass 且通过 Step 1,直接允许
- 2b Always Allow 规则:全局 allow 规则,如
Bash授权所有命令
Step 3 — 结果转换:
- 将
passthrough转换为ask - 返回带有建议(suggestions)的决策结果
5.3 外层包装:hasPermissionsToUseTool
hasPermissionsToUseTool 在内层结果基础上增加了模式相关的转换逻辑(permissions.ts 第 473~640 行):
dontAsk 模式转换:
所有 ask 决策被强制转换为 deny,并附带拒绝消息:
if (appState.toolPermissionContext.mode === 'dontAsk') {
return {
behavior: 'deny',
decisionReason: { type: 'mode', mode: 'dontAsk' },
message: DONT_ASK_REJECT_MESSAGE(tool.name),
}
}auto 模式分类器:
当处于 auto 或 plan 模式且 autoModeStateModule.isAutoModeActive() 为真时,系统不会立即弹出对话框,而是将决策委托给 YOLO Classifier(yoloClassifier.ts)。
分类器的调用流程:
- 首先尝试
acceptEdits快速通道:如果该操作在acceptEdits模式下会被允许,则跳过分类器(减少 API 调用开销) - 检查安全工具白名单(
classifierDecision.ts第 64~98 行的SAFE_YOLO_ALLOWLISTED_TOOLS):只读工具如FileReadTool、GrepTool、GlobTool等直接允许 - 调用
classifyYoloAction()发送当前会话上下文和工具调用描述给分类器模型 - 根据分类器返回的
shouldBlock决定允许或拒绝
Denial Tracking 回退机制:
当分类器连续拒绝(maxConsecutive: 3)或总计拒绝过多(maxTotal: 20)时,系统会回退到人工审批模式(denialTracking.ts 第 8~33 行)。这防止了分类器"卡死"导致用户完全无法进行任何操作。
5.4 分类器审批追踪
src/utils/classifierApprovals.ts 维护了一个全局的 CLASSIFIER_APPROVALS Map,用于追踪哪些工具调用是被分类器自动批准的:
type ClassifierApproval = {
classifier: 'bash' | 'auto-mode'
matchedRule?: string
reason?: string
}
const CLASSIFIER_APPROVALS = new Map<string, ClassifierApproval>()这在 UI 层(UserToolSuccessMessage.tsx)用于向用户展示 "已由 auto-mode 分类器自动批准" 的提示,增强系统透明度。
六、关键安全设计原则
纵观整个权限系统,Claude Code 遵循了几条核心的安全设计原则:
- Deny-by-Default:没有明确授权的操作默认需要审批
- Defense in Depth:多层检查(规则 → 工具 → 模式 → 分类器),任何一层都可以拦截危险操作
- Immutable Safety Checks:敏感路径检查是 bypass 免疫的,作为最后防线
- Transparency:通过
PermissionRuleExplanation和PermissionExplanation向用户清晰解释每一次审批的原因 - Graceful Degradation:分类器不可用时可以 fail-open 或 fail-closed(通过
tengu_iron_gate_closedFeature Gate 控制),并且超过拒绝阈值后自动回退到人工审批 - Least Privilege:
auto模式会自动剥离危险的宽泛权限规则,确保自动化运行在最小权限原则下
七、总结
Claude Code 的权限与审批系统是一个工程复杂度极高的安全子系统。它需要在"让 AI 自由发挥以提升效率"和"确保用户系统安全不被破坏"之间走钢丝。
从 src/types/permissions.ts 中纯类型定义的设计(打破循环依赖),到 src/utils/permissions/ 中 24 个模块的精密协作,再到 src/components/permissions/ 中 40 多个 UI 组件的人性化交互,每一层都体现了对安全与体验的深度思考。
最核心的 hasPermissionsToUseTool 函数虽然长达数百行,但其检查顺序经过精心编排:Deny 规则 → Ask 规则 → 工具自定义检查 → Safety Check → bypass/allow 快速通道 → 模式转换 → 分类器。这个顺序确保了拒绝总是优先于允许,安全总是优先于便利。
对于正在构建 AI Agent 的开发者来说,Claude Code 的权限系统提供了一个优秀的参考范式:任何能够执行代码、访问文件、连接网络的 Agent,都必须拥有同等严格甚至更严格的权限控制机制。权限不是可选功能,而是 Agent 安全架构的基石。