656 lines
17 KiB
Markdown
656 lines
17 KiB
Markdown
# Claude Code 权限系统详解
|
||
|
||
## 概述
|
||
|
||
Claude Code 的权限系统管理工具调用的授权流程。当 Claude 尝试执行危险操作(如运行 Bash 命令、写入文件)时,系统会请求用户确认。权限系统支持多种授权模式和来源,确保用户对工具执行有完全控制。
|
||
|
||
---
|
||
|
||
## 核心架构
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ 权限请求入口 │
|
||
│ │
|
||
│ hasPermissionsToUseTool(tool, input, context, message, id) │
|
||
└─────────────────────────────────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ 权限模式检查 (PermissionMode) │
|
||
│ │
|
||
│ default | plan | bypassPermissions | auto | localAssistant │
|
||
└─────────────────────────────────────────────────────────────────┘
|
||
│
|
||
┌───────────────┼───────────────┐
|
||
▼ ▼ ▼
|
||
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
|
||
│ Coordinator │ │ Interactive │ │ Swarm Worker │
|
||
│ Handler │ │ Handler │ │ Handler │
|
||
└──────────────┘ └──────────────┘ └──────────────┘
|
||
│ │ │
|
||
└───────────────┼───────────────┘
|
||
▼
|
||
┌──────────────────────────────────────┐
|
||
│ 权限决策结果 │
|
||
│ │
|
||
│ allow | deny | ask | skip │
|
||
└──────────────────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## 1. 权限模式 (PermissionMode)
|
||
|
||
**文件位置**: `src/utils/permissions/PermissionMode.ts`
|
||
|
||
### 模式类型
|
||
|
||
```typescript
|
||
type PermissionMode =
|
||
| 'default' // 标准交互式权限确认
|
||
| 'plan' // 计划模式,只读操作
|
||
| 'bypassPermissions' // 跳过所有权限检查
|
||
| 'auto' // 自动批准基于分类器
|
||
| 'localAssistant' // 本地助手模式
|
||
```
|
||
|
||
### 模式转换
|
||
|
||
```typescript
|
||
export function transitionPermissionMode(
|
||
from: PermissionMode,
|
||
to: PermissionMode,
|
||
options?: { skipAutoGate?: boolean }
|
||
): { ok: true } | { ok: false; error: string }
|
||
```
|
||
|
||
---
|
||
|
||
## 2. PermissionContext - 权限上下文
|
||
|
||
**文件位置**: `src/hooks/toolPermission/PermissionContext.ts`
|
||
|
||
### 创建权限上下文
|
||
|
||
```typescript
|
||
function createPermissionContext(
|
||
tool: ToolType,
|
||
input: Record<string, unknown>,
|
||
toolUseContext: ToolUseContext,
|
||
assistantMessage: AssistantMessage,
|
||
toolUseID: string,
|
||
setToolPermissionContext: (context: ToolPermissionContext) => void,
|
||
queueOps?: PermissionQueueOps,
|
||
)
|
||
```
|
||
|
||
### 上下文方法
|
||
|
||
```typescript
|
||
type PermissionContext = {
|
||
// 工具信息
|
||
tool: ToolType
|
||
input: Record<string, unknown>
|
||
toolUseContext: ToolUseContext
|
||
assistantMessage: AssistantMessage
|
||
messageId: string
|
||
toolUseID: string
|
||
|
||
// 日志记录
|
||
logDecision(args: PermissionDecisionArgs, opts?: {...}): void
|
||
logCancelled(): void
|
||
|
||
// 权限持久化
|
||
async persistPermissions(updates: PermissionUpdate[]): Promise<boolean>
|
||
|
||
// 决策构建
|
||
buildAllow(
|
||
updatedInput: Record<string, unknown>,
|
||
opts?: {...}
|
||
): PermissionAllowDecision
|
||
|
||
buildDeny(
|
||
message: string,
|
||
decisionReason: PermissionDecisionReason
|
||
): PermissionDenyDecision
|
||
|
||
// 用户处理
|
||
async handleUserAllow(
|
||
updatedInput: Record<string, unknown>,
|
||
permissionUpdates: PermissionUpdate[],
|
||
feedback?: string,
|
||
...
|
||
): Promise<PermissionAllowDecision>
|
||
|
||
// Hook 执行
|
||
async runHooks(
|
||
permissionMode: string | undefined,
|
||
suggestions: PermissionUpdate[] | undefined,
|
||
updatedInput?: Record<string, unknown>,
|
||
...
|
||
): Promise<PermissionDecision | null>
|
||
|
||
// 分类器尝试
|
||
async tryClassifier(
|
||
pendingClassifierCheck: PendingClassifierCheck | undefined,
|
||
updatedInput: Record<string, unknown> | undefined
|
||
): Promise<PermissionDecision | null>
|
||
|
||
// 队列操作
|
||
pushToQueue(item: ToolUseConfirm): void
|
||
removeFromQueue(): void
|
||
updateQueueItem(patch: Partial<ToolUseConfirm>): void
|
||
|
||
// 中止处理
|
||
resolveIfAbort(resolve: (decision: PermissionDecision) => void): boolean
|
||
cancelAndAbort(
|
||
feedback?: string,
|
||
isAbort?: boolean,
|
||
contentBlocks?: ContentBlockParam[]
|
||
): PermissionDecision
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 3. 权限决策类型
|
||
|
||
**文件位置**: `src/types/permissions.ts`
|
||
|
||
### 决策类型
|
||
|
||
```typescript
|
||
type PermissionDecision =
|
||
| { behavior: 'allow'; updatedInput?: Record<string, unknown>; ... }
|
||
| { behavior: 'deny'; message: string; ... }
|
||
| { behavior: 'ask' }
|
||
| { behavior: 'skip' }
|
||
```
|
||
|
||
### 决策来源
|
||
|
||
```typescript
|
||
type PermissionDecisionSource =
|
||
| { type: 'hook'; permanent?: boolean }
|
||
| { type: 'user'; permanent: boolean }
|
||
| { type: 'classifier' }
|
||
| 'config' // 配置自动允许
|
||
```
|
||
|
||
### 待处理分类器检查
|
||
|
||
```typescript
|
||
type PendingClassifierCheck = {
|
||
input: Record<string, unknown>
|
||
command: string
|
||
// ...
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 4. 权限处理器
|
||
|
||
### 4.1 Coordinator Handler
|
||
|
||
**文件位置**: `src/hooks/toolPermission/handlers/coordinatorHandler.ts`
|
||
|
||
用于协调者工作进程(coordinator worker)的权限处理流程。
|
||
|
||
```typescript
|
||
async function handleCoordinatorPermission(
|
||
params: CoordinatorPermissionParams
|
||
): Promise<PermissionDecision | null>
|
||
```
|
||
|
||
**处理流程**:
|
||
|
||
1. **尝试权限 Hooks**(快速,本地)
|
||
```typescript
|
||
const hookResult = await ctx.runHooks(
|
||
permissionMode,
|
||
suggestions,
|
||
updatedInput
|
||
)
|
||
if (hookResult) return hookResult
|
||
```
|
||
|
||
2. **尝试分类器**(慢速,推理)
|
||
```typescript
|
||
const classifierResult = feature('BASH_CLASSIFIER')
|
||
? await ctx.tryClassifier?.(pendingClassifierCheck, updatedInput)
|
||
: null
|
||
if (classifierResult) return classifierResult
|
||
```
|
||
|
||
3. **降级到交互式对话框**
|
||
```typescript
|
||
return null // 交给交互式处理
|
||
```
|
||
|
||
### 4.2 Interactive Handler
|
||
|
||
**文件位置**: `src/hooks/toolPermission/handlers/interactiveHandler.ts`
|
||
|
||
用于主代理的交互式权限处理。
|
||
|
||
```typescript
|
||
function handleInteractivePermission(
|
||
params: InteractivePermissionParams,
|
||
resolve: (decision: PermissionDecision) => void,
|
||
): void
|
||
```
|
||
|
||
**处理流程**:
|
||
|
||
1. **推送确认队列项**
|
||
```typescript
|
||
ctx.pushToQueue({
|
||
assistantMessage,
|
||
tool,
|
||
description,
|
||
input: displayInput,
|
||
toolUseID,
|
||
permissionResult: result,
|
||
permissionPromptStartTimeMs,
|
||
onUserInteraction: () => { ... },
|
||
onAbort: () => { ... },
|
||
onAllow: () => { ... },
|
||
onReject: () => { ... },
|
||
recheckPermission: () => { ... },
|
||
})
|
||
```
|
||
|
||
2. **桥接权限转发**(远程控制)
|
||
```typescript
|
||
if (bridgeCallbacks && bridgeRequestId) {
|
||
bridgeCallbacks.sendRequest(
|
||
bridgeRequestId,
|
||
ctx.tool.name,
|
||
displayInput,
|
||
ctx.toolUseID,
|
||
description,
|
||
result.suggestions,
|
||
result.blockedPath
|
||
)
|
||
}
|
||
```
|
||
|
||
3. **通道权限转发**(KAIROS)
|
||
```typescript
|
||
if (channelCallbacks && !ctx.tool.requiresUserInteraction?.()) {
|
||
// 发送到通道(Telegram, iMessage 等)
|
||
client.notification({
|
||
method: CHANNEL_PERMISSION_REQUEST_METHOD,
|
||
params
|
||
})
|
||
}
|
||
```
|
||
|
||
4. **异步 Hook 执行**
|
||
```typescript
|
||
void (async () => {
|
||
const hookDecision = await ctx.runHooks(...)
|
||
if (hookDecision && claim()) {
|
||
resolveOnce(hookDecision)
|
||
}
|
||
})()
|
||
```
|
||
|
||
5. **异步分类器执行**
|
||
```typescript
|
||
if (feature('BASH_CLASSIFIER') && result.pendingClassifierCheck) {
|
||
setClassifierChecking(ctx.toolUseID)
|
||
void executeAsyncClassifierCheck(...)
|
||
}
|
||
```
|
||
|
||
### 4.3 Swarm Worker Handler
|
||
|
||
**文件位置**: `src/hooks/toolPermission/handlers/swarmWorkerHandler.ts`
|
||
|
||
用于 Swarm Worker 的权限处理(简化的仅拒绝处理)。
|
||
|
||
---
|
||
|
||
## 5. 权限回调 (Bridge Permission Callbacks)
|
||
|
||
**文件位置**: `src/bridge/bridgePermissionCallbacks.ts`
|
||
|
||
### 回调接口
|
||
|
||
```typescript
|
||
type BridgePermissionCallbacks = {
|
||
sendRequest(
|
||
requestId: string,
|
||
toolName: string,
|
||
input: Record<string, unknown>,
|
||
toolUseId: string,
|
||
description: string,
|
||
permissionSuggestions?: PermissionUpdate[],
|
||
blockedPath?: string,
|
||
): void
|
||
|
||
sendResponse(requestId: string, response: BridgePermissionResponse): void
|
||
|
||
cancelRequest(requestId: string): void
|
||
|
||
onResponse(
|
||
requestId: string,
|
||
handler: (response: BridgePermissionResponse) => void,
|
||
): () => void
|
||
}
|
||
```
|
||
|
||
### 权限响应
|
||
|
||
```typescript
|
||
type BridgePermissionResponse = {
|
||
behavior: 'allow' | 'deny'
|
||
updatedInput?: Record<string, unknown>
|
||
updatedPermissions?: PermissionUpdate[]
|
||
message?: string
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 6. 权限规则管理
|
||
|
||
### 6.1 PermissionUpdate
|
||
|
||
**文件位置**: `src/utils/permissions/PermissionUpdateSchema.ts`
|
||
|
||
```typescript
|
||
type PermissionUpdate = {
|
||
destination: 'alwaysAllowRules' | 'alwaysDenyRules' | 'command'
|
||
rule: {
|
||
tool: string
|
||
input?: Record<string, unknown>
|
||
matchers?: InputMatcher[]
|
||
}
|
||
Permanente: boolean
|
||
}
|
||
```
|
||
|
||
### 6.2 规则应用
|
||
|
||
```typescript
|
||
export function applyPermissionUpdates(
|
||
context: ToolPermissionContext,
|
||
updates: PermissionUpdate[]
|
||
): ToolPermissionContext
|
||
```
|
||
|
||
### 6.3 规则持久化
|
||
|
||
```typescript
|
||
export function persistPermissionUpdates(updates: PermissionUpdate[]): void
|
||
export function supportsPersistence(
|
||
destination: 'alwaysAllowRules' | 'alwaysDenyRules' | 'command'
|
||
): boolean
|
||
```
|
||
|
||
---
|
||
|
||
## 7. Bash 分类器
|
||
|
||
**文件位置**: `src/tools/BashTool/bashPermissions.ts`
|
||
|
||
### 分类器决策
|
||
|
||
```typescript
|
||
type ClassifierDecision =
|
||
| { type: 'classifier'; classifier: 'auto-mode'; reason: string }
|
||
| { type: 'classifier'; classifier: 'pattern-match'; reason: string }
|
||
| { type: 'user-defined'; reason: string }
|
||
| { type: 'deny'; reason: string }
|
||
```
|
||
|
||
### 分类器检查
|
||
|
||
```typescript
|
||
export async function awaitClassifierAutoApproval(
|
||
pendingClassifierCheck: PendingClassifierCheck,
|
||
signal: AbortSignal,
|
||
isNonInteractiveSession: boolean,
|
||
): Promise<ClassifierDecision | null>
|
||
```
|
||
|
||
---
|
||
|
||
## 8. 权限日志
|
||
|
||
**文件位置**: `src/hooks/toolPermission/permissionLogging.ts`
|
||
|
||
### 日志函数
|
||
|
||
```typescript
|
||
export function logPermissionDecision(
|
||
args: PermissionDecisionArgs,
|
||
decision: {
|
||
decision: 'accept' | 'reject'
|
||
source: PermissionApprovalSource | PermissionRejectionSource
|
||
},
|
||
permissionPromptStartTimeMs?: number
|
||
): void
|
||
```
|
||
|
||
### 事件类型
|
||
|
||
```typescript
|
||
// 接受
|
||
logEvent('tengu_tool_permission_accepted', {...})
|
||
logEvent('tengu_tool_permission_auto_approved', {...})
|
||
|
||
// 拒绝
|
||
logEvent('tengu_tool_permission_rejected', {...})
|
||
logEvent('tengu_tool_use_cancelled', {...})
|
||
```
|
||
|
||
---
|
||
|
||
## 9. 权限检查入口
|
||
|
||
**文件位置**: `src/utils/permissions/permissions.ts`
|
||
|
||
### 主检查函数
|
||
|
||
```typescript
|
||
export async function hasPermissionsToUseTool(
|
||
tool: Tool,
|
||
input: Record<string, unknown>,
|
||
toolUseContext: ToolUseContext,
|
||
assistantMessage: AssistantMessage,
|
||
toolUseID: string,
|
||
): Promise<PermissionDecision>
|
||
```
|
||
|
||
**决策流程**:
|
||
|
||
1. **模式检查**
|
||
- `bypassPermissions`: 直接允许
|
||
- `plan`: 计划模式规则
|
||
- `auto`: 分类器自动决策
|
||
|
||
2. **规则匹配**
|
||
- `alwaysAllowRules`: 匹配则允许
|
||
- `alwaysDenyRules`: 匹配则拒绝
|
||
|
||
3. **会话状态**
|
||
- 非交互式会话可能有不同行为
|
||
|
||
4. **分类器检查**
|
||
- 仅 Bash 工具支持
|
||
- 异步执行
|
||
|
||
5. **Hook 检查**
|
||
- 执行 PermissionRequest hooks
|
||
|
||
6. **降级到交互式**
|
||
- 返回 `{ behavior: 'ask' }`
|
||
|
||
---
|
||
|
||
## 10. 权限队列
|
||
|
||
### ToolUseConfirm
|
||
|
||
```typescript
|
||
type ToolUseConfirm = {
|
||
assistantMessage: AssistantMessage
|
||
tool: Tool
|
||
description: string
|
||
input: Record<string, unknown>
|
||
toolUseContext: ToolUseContext
|
||
toolUseID: string
|
||
permissionResult: PermissionDecision & { behavior: 'ask' }
|
||
permissionPromptStartTimeMs: number
|
||
classifierCheckInProgress?: boolean
|
||
classifierAutoApproved?: boolean
|
||
classifierMatchedRule?: string
|
||
|
||
onUserInteraction(): void
|
||
onDismissCheckmark(): void
|
||
onAbort(): void
|
||
onAllow(
|
||
updatedInput: Record<string, unknown>,
|
||
permissionUpdates: PermissionUpdate[],
|
||
feedback?: string,
|
||
contentBlocks?: ContentBlockParam[]
|
||
): Promise<void>
|
||
onReject(feedback?: string, contentBlocks?: ContentBlockParam[]): void
|
||
recheckPermission(): Promise<void>
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 11. 权限处理流程图
|
||
|
||
```
|
||
用户执行工具
|
||
│
|
||
▼
|
||
hasPermissionsToUseTool()
|
||
│
|
||
▼
|
||
┌─────────────────────┐
|
||
│ 权限模式检查 │
|
||
│ bypassPermissions? │ ───是──▶ 直接允许
|
||
└─────────────────────┘
|
||
│否
|
||
▼
|
||
┌─────────────────────┐
|
||
│ 规则检查 │
|
||
│ alwaysAllowRules? │ ───是──▶ 直接允许
|
||
│ alwaysDenyRules? │ ───是──▶ 直接拒绝
|
||
└─────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────┐
|
||
│ 分类器检查 │
|
||
│ BASH_CLASSIFIER? │ ───是──▶ 异步推理
|
||
└─────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────┐
|
||
│ Hook 检查 │
|
||
│ executePermission │ ───允许──▶ 返回允许
|
||
│ RequestHooks │ ───拒绝──▶ 返回拒绝
|
||
└─────────────────────┘
|
||
│
|
||
▼
|
||
交互式确认对话框
|
||
│
|
||
├─── 用户允许 ───▶ handleUserAllow()
|
||
│ │
|
||
│ ▼
|
||
│ persistPermissions()
|
||
│ │
|
||
│ ▼
|
||
│ logPermissionDecision()
|
||
│ │
|
||
│ ▼
|
||
│ 返回 PermissionAllowDecision
|
||
│
|
||
├─── 用户拒绝 ───▶ onReject()
|
||
│ │
|
||
│ ▼
|
||
│ cancelAndAbort()
|
||
│ │
|
||
│ ▼
|
||
│ 返回 PermissionDenyDecision
|
||
│
|
||
└─── 用户中断 ───▶ Esc 按键
|
||
│
|
||
▼
|
||
abortController.abort()
|
||
│
|
||
▼
|
||
返回 PermissionDenyDecision
|
||
```
|
||
|
||
---
|
||
|
||
## 12. 远程权限流程
|
||
|
||
```
|
||
CLI 端 (handleInteractivePermission)
|
||
│
|
||
▼
|
||
bridgeCallbacks.sendRequest(bridgeRequestId, ...)
|
||
│
|
||
│ WebSocket / HTTP
|
||
▼
|
||
IDE Extension / Web UI
|
||
│
|
||
▼
|
||
显示权限对话框
|
||
│
|
||
▼
|
||
用户允许/拒绝
|
||
│
|
||
▼
|
||
bridgeCallbacks.sendResponse(bridgeRequestId, response)
|
||
│
|
||
│ WebSocket / HTTP
|
||
▼
|
||
CLI 端 (onResponse 回调)
|
||
│
|
||
▼
|
||
resolveOnce(decision)
|
||
```
|
||
|
||
---
|
||
|
||
## 13. 关键常量
|
||
|
||
```typescript
|
||
// 200ms 宽限期后用户交互取消分类器
|
||
const GRACE_PERIOD_MS = 200
|
||
|
||
// 分类器自动批准后显示 ✓ 的时间
|
||
const CHECKMARK_DISPLAY_MS = 3000 // 终端聚焦时
|
||
const CHECKMARK_DISPLAY_MS = 1000 // 终端未聚焦时
|
||
```
|
||
|
||
---
|
||
|
||
## 相关源码文件
|
||
|
||
| 文件 | 功能 |
|
||
|------|------|
|
||
| `PermissionContext.ts` | 权限上下文创建和管理 |
|
||
| `handlers/coordinatorHandler.ts` | 协调者处理器 |
|
||
| `handlers/interactiveHandler.ts` | 交互式处理器 |
|
||
| `handlers/swarmWorkerHandler.ts` | Swarm Worker 处理器 |
|
||
| `permissionLogging.ts` | 权限日志记录 |
|
||
| `src/utils/permissions/permissions.ts` | 权限检查入口 |
|
||
| `src/utils/permissions/PermissionUpdate.ts` | 权限更新管理 |
|
||
| `src/utils/permissions/PermissionMode.ts` | 权限模式定义 |
|
||
| `src/bridge/bridgePermissionCallbacks.ts` | 桥接权限回调 |
|
||
| `src/tools/BashTool/bashPermissions.ts` | Bash 分类器 |
|
||
| `src/types/permissions.ts` | 权限类型定义 |
|