# 权限控制系统

<cite>
**本文档引用的文件**
- [permission-bridge.ts](file://src/lib/sdk/permission-bridge.ts)
- [client.ts](file://src/lib/sdk/client.ts)
- [permission/route.ts](file://src/app/api/chat/permission/route.ts)
- [chat-view.tsx](file://src/components/views/chat-view.tsx)
- [browser-tools.ts](file://src/lib/browser-tools.ts)
- [message-mapper.ts](file://src/lib/sdk/message-mapper.ts)
- [permission-broker.ts](file://src/lib/im/permission-broker.ts)
- [settings-view.tsx](file://src/components/views/settings-view.tsx)
- [use-settings.ts](file://src/hooks/use-settings.ts)
- [settings/route.ts](file://src/app/api/settings/route.ts)
</cite>

## 目录
1. [简介](#简介)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构概览](#架构概览)
5. [详细组件分析](#详细组件分析)
6. [依赖关系分析](#依赖关系分析)
7. [性能考量](#性能考量)
8. [故障排除指南](#故障排除指南)
9. [结论](#结论)
10. [附录](#附录)

## 简介
本文件全面解析 Forge 权限控制系统，重点涵盖以下方面：
- .browsersafetyCheck 的安全限制与 .browsersafetyCheck 返回 'ask' 的处理机制
- canUseTool 回调函数的设计与实现，特别是 .browsersafetyDir 写入的自动批准机制
- permissionMode 与 allowDangerouslySkipPermissions 的使用场景与安全考量
- settingSources 配置对权限规则加载的影响
- 权限控制的调试方法与常见问题解决方案
- 安全最佳实践与合规性指导

## 项目结构
权限控制系统由三层协作构成：
- SDK 层：负责工具调用拦截、权限决策桥接与会话状态管理
- 前端层：通过 SSE 接收权限请求，展示 UI 并提交决策
- 后端 API 层：接收前端决策，解析并恢复等待中的权限请求

```mermaid
graph TB
subgraph "SDK 层"
SDK["SDK 客户端<br/>client.ts"]
Bridge["权限桥接<br/>permission-bridge.ts"]
Tools["浏览器工具 MCP<br/>browser-tools.ts"]
end
subgraph "前端层"
ChatView["聊天视图 UI<br/>chat-view.tsx"]
Mapper["消息映射器<br/>message-mapper.ts"]
end
subgraph "后端 API 层"
ApiRoute["权限决策 API<br/>permission/route.ts"]
SettingsApi["设置 API<br/>settings/route.ts"]
end
SDK --> Bridge
Bridge --> ApiRoute
ApiRoute --> ChatView
ChatView --> ApiRoute
Mapper --> ChatView
Tools --> SDK
```

**图表来源**
- [client.ts:378-537](file://src/lib/sdk/client.ts#L378-L537)
- [permission-bridge.ts:62-152](file://src/lib/sdk/permission-bridge.ts#L62-L152)
- [permission/route.ts:6-41](file://src/app/api/chat/permission/route.ts#L6-L41)
- [chat-view.tsx:1437-1700](file://src/components/views/chat-view.tsx#L1437-L1700)
- [browser-tools.ts:99-198](file://src/lib/browser-tools.ts#L99-L198)

**章节来源**
- [client.ts:378-537](file://src/lib/sdk/client.ts#L378-L537)
- [permission-bridge.ts:62-152](file://src/lib/sdk/permission-bridge.ts#L62-L152)
- [permission/route.ts:6-41](file://src/app/api/chat/permission/route.ts#L6-L41)
- [chat-view.tsx:1437-1700](file://src/components/views/chat-view.tsx#L1437-L1700)
- [browser-tools.ts:99-198](file://src/lib/browser-tools.ts#L99-L198)

## 核心组件
- 权限桥接（createPermissionBridge）：将 SDK 的 canUseTool 回调与前端 SSE 流对接，负责创建待决请求、等待用户决策、记录审计日志与清理过期会话
- SDK 客户端（createForgeQuery）：构建 SDK 查询实例，注入 canUseTool 与 settingSources，处理 .claude/ 目录写入的安全检查绕过
- 权限决策 API（POST /api/chat/permission）：接收前端决策，解析并恢复对应请求
- 聊天视图 UI：渲染权限请求卡片、显示倒计时、提供允许/拒绝按钮
- 消息映射器：将 SDK 消息转换为前端可理解的 SSE 事件
- 权限日志与超时：全局状态共享、会话活动跟踪、30 分钟自动清理

**章节来源**
- [permission-bridge.ts:62-152](file://src/lib/sdk/permission-bridge.ts#L62-L152)
- [client.ts:451-498](file://src/lib/sdk/client.ts#L451-L498)
- [permission/route.ts:6-41](file://src/app/api/chat/permission/route.ts#L6-L41)
- [chat-view.tsx:1564-1700](file://src/components/views/chat-view.tsx#L1564-L1700)
- [message-mapper.ts:53-85](file://src/lib/sdk/message-mapper.ts#L53-L85)

## 架构概览
下图展示了从 SDK 触发工具调用到前端决策的完整流程，以及 .browsersafetyCheck 的特殊处理路径。

```mermaid
sequenceDiagram
participant SDK as "SDK 客户端<br/>client.ts"
participant Bridge as "权限桥接<br/>permission-bridge.ts"
participant SSE as "SSE 事件流"
participant UI as "聊天视图 UI<br/>chat-view.tsx"
participant API as "权限决策 API<br/>permission/route.ts"
SDK->>Bridge : 调用 canUseTool(toolName, input)
Bridge->>Bridge : 构建 updatedPermissions会话级规则
Bridge->>Bridge : 检查会话是否已允许该工具
alt 已允许
Bridge-->>SDK : 返回允许behavior=allow
else 未允许
Bridge->>SSE : 发送 permission_request 事件
SSE-->>UI : 前端接收并渲染权限卡片
UI->>API : POST /api/chat/permission {requestId, decision}
API->>Bridge : resolvePermission(requestId, decision)
Bridge-->>SDK : 返回允许或拒绝结果
end
```

**图表来源**
- [permission-bridge.ts:62-152](file://src/lib/sdk/permission-bridge.ts#L62-L152)
- [permission/route.ts:6-41](file://src/app/api/chat/permission/route.ts#L6-L41)
- [chat-view.tsx:1564-1700](file://src/components/views/chat-view.tsx#L1564-L1700)

## 详细组件分析

### .browsersafetyCheck 与 .browsersafetyDir 写入自动批准机制
- .browsersafetyCheck 是 SDK 对 .claude/ 目录的内置安全检查，即使在 bypassPermissions 模式下也会阻拦编辑/写入操作
- 为解决此问题，系统在 canUseTool 中检测输入中是否包含 .claude/ 路径，若是则直接允许并返回 SDK 建议的权限更新
- 当提供自定义 canUseTool 时，系统会包装该回调，在 .claude/ 写入场景下自动批准，同时将 SDK suggestions 传回以解锁会话

```mermaid
flowchart TD
Start(["进入 canUseTool"]) --> CheckClaude["检测输入是否指向 .claude/ 目录"]
CheckClaude --> |是| AutoAllow["自动批准并返回 SDK suggestions"]
CheckClaude --> |否| CallCaller["调用用户提供的 canUseTool"]
AutoAllow --> ReturnResult["返回允许结果"]
CallCaller --> ReturnResult
```

**图表来源**
- [client.ts:459-488](file://src/lib/sdk/client.ts#L459-L488)

**章节来源**
- [client.ts:453-488](file://src/lib/sdk/client.ts#L453-L488)

### canUseTool 回调设计与实现
- 创建权限桥接：createPermissionBridge 返回一个 canUseTool 实现，负责：
  - 构建 updatedPermissions：优先使用 SDK 提供的 suggestions，否则构造一条将工具添加到会话允许列表的规则
  - 检查会话级允许：若当前会话已允许该工具，则直接返回允许并触会话活动时间
  - 发送 permission_request 事件：通过 SSE 将请求发送到前端；若发送失败（客户端断开），立即拒绝
  - 等待决策：120 秒超时，超时后返回 timeout
  - 记录审计：将决策写入全局日志，上限 500 条
  - 返回结果：根据决策映射为 SDK 的 PermissionResult，并携带 updatedPermissions

```mermaid
classDiagram
class PermissionBridge {
+createPermissionBridge(sessionId, emit) CanUseTool
+resolvePermission(requestId, decision) boolean
+clearSessionAllowances(sessionId) void
+cleanupStaleSessionAllowances() number
+getPermissionLog(limit) PermissionLogEntry[]
}
class SDKClient {
+createForgeQuery(opts) Query
-buildSdkEnv(apiKey, baseUrl, authType) Record~string,string~
-mapThinkingMode(mode, providerType) Options
}
PermissionBridge <-- SDKClient : "注入 canUseTool"
```

**图表来源**
- [permission-bridge.ts:62-285](file://src/lib/sdk/permission-bridge.ts#L62-L285)
- [client.ts:378-537](file://src/lib/sdk/client.ts#L378-L537)

**章节来源**
- [permission-bridge.ts:62-152](file://src/lib/sdk/permission-bridge.ts#L62-L152)
- [permission-bridge.ts:158-182](file://src/lib/sdk/permission-bridge.ts#L158-L182)
- [permission-bridge.ts:187-214](file://src/lib/sdk/permission-bridge.ts#L187-L214)
- [permission-bridge.ts:238-256](file://src/lib/sdk/permission-bridge.ts#L238-L256)
- [permission-bridge.ts:263-281](file://src/lib/sdk/permission-bridge.ts#L263-L281)

### permissionMode 与 allowDangerouslySkipPermissions 使用场景
- permissionMode（桌面/IM 设置项）：
  - confirm：需要用户确认每个权限请求（默认）
  - full：完全信任，不弹窗确认（存在安全风险）
- allowDangerouslySkipPermissions：
  - 当启用 bypassPermissions 时，该标志允许跳过 SDK 的权限检查
  - 但 .claude/ 写入仍需通过 canUseTool 自动批准，因为 SDK 的 .browsersafetyCheck 会先于 bypassPermissions 执行
- settingSources：
  - 默认加载顺序：['user', 'project', 'local']
  - 当提供自定义 canUseTool 时，强制限制为 ['project']，防止用户/本地预设规则绕过权限桥接

```mermaid
flowchart TD
A["选择权限模式"] --> B{"模式为 confirm?"}
B --> |是| C["弹窗确认每个工具调用"]
B --> |否| D{"模式为 full?"}
D --> |是| E["跳过弹窗，直接执行"]
D --> |否| F["未知模式，按默认处理"]
G["bypassPermissions=true"] --> H["permissionMode=bypassPermissions"]
H --> I["allowDangerouslySkipPermissions=true"]
I --> J[".claude/ 写入仍需 canUseTool 自动批准"]
```

**图表来源**
- [client.ts:464-498](file://src/lib/sdk/client.ts#L464-L498)
- [settings-view.tsx:359-390](file://src/components/views/settings-view.tsx#L359-L390)

**章节来源**
- [client.ts:464-498](file://src/lib/sdk/client.ts#L464-L498)
- [settings-view.tsx:359-390](file://src/components/views/settings-view.tsx#L359-L390)

### 权限决策 API 与前端交互
- 后端 API：
  - 校验请求体：requestId 与 decision 必填
  - 校验决策值：仅允许 'allow'、'allow_session'、'deny'
  - 调用 resolvePermission 解析请求，即使重复调用也返回成功（前端可能向所有同类请求发送决策）
- 前端 UI：
  - 渲染权限卡片，显示工具名称、输入摘要与倒计时
  - 提供 Deny、Allow Session、Allow 三种操作
  - 允许会话级授权时，自动解决同一工具在同一会话下的其他待决请求

```mermaid
sequenceDiagram
participant UI as "聊天视图 UI"
participant API as "权限决策 API"
participant Bridge as "权限桥接"
UI->>API : POST /api/chat/permission {requestId, decision}
API->>Bridge : resolvePermission(requestId, decision)
alt decision='allow_session'
Bridge->>Bridge : 将该工具加入会话允许列表
Bridge->>Bridge : 自动解决同会话同工具的其他待决请求
end
Bridge-->>API : 返回 resolved=true
API-->>UI : {ok : true, resolved : true}
```

**图表来源**
- [permission/route.ts:6-41](file://src/app/api/chat/permission/route.ts#L6-L41)
- [permission-bridge.ts:187-214](file://src/lib/sdk/permission-bridge.ts#L187-L214)

**章节来源**
- [permission/route.ts:6-41](file://src/app/api/chat/permission/route.ts#L6-L41)
- [chat-view.tsx:1537-1581](file://src/components/views/chat-view.tsx#L1537-L1581)
- [chat-view.tsx:1443-1477](file://src/components/views/chat-view.tsx#L1443-L1477)

### settingSources 对权限规则加载的影响
- 默认行为：SDK 从 ['user', 'project', 'local'] 加载设置，可能导致预设规则绕过权限桥接
- 当提供自定义 canUseTool 时，强制设置为 ['project']，确保：
  - 仅加载项目级别的 .claude/CLAUDE.md 与项目设置
  - 不加载用户/本地预设的自动批准规则
- 这一限制保证了所有工具调用都经过权限桥接，避免安全漏洞

**章节来源**
- [client.ts:429-497](file://src/lib/sdk/client.ts#L429-L497)

### 浏览器工具与安全边界
- 浏览器工具通过 MCP 服务器提供，支持导航、截图、点击、填写、提取等操作
- 为避免资源泄漏，系统实现了空闲自动关闭（15 分钟无活动）与定时检查
- 该模块与权限系统协同工作：当 SDK 请求浏览器工具时，同样需要通过 canUseTool 决策

**章节来源**
- [browser-tools.ts:99-198](file://src/lib/browser-tools.ts#L99-L198)
- [browser-tools.ts:13-34](file://src/lib/browser-tools.ts#L13-L34)

## 依赖关系分析
- SDK 客户端依赖权限桥接模块以提供 canUseTool 实现
- 权限桥接模块依赖全局状态（待决请求、会话允许列表、活动时间、审计日志）
- 前端聊天视图依赖消息映射器将 SDK 消息转换为 UI 可渲染的事件
- 权限决策 API 与前端 UI 协作完成用户决策收集

```mermaid
graph TB
Client["SDK 客户端<br/>client.ts"] --> Bridge["权限桥接<br/>permission-bridge.ts"]
Bridge --> Global["全局状态<br/>pending/session/log"]
Bridge --> API["权限决策 API<br/>permission/route.ts"]
API --> UI["聊天视图 UI<br/>chat-view.tsx"]
UI --> Mapper["消息映射器<br/>message-mapper.ts"]
Client --> Browser["浏览器工具<br/>browser-tools.ts"]
```

**图表来源**
- [client.ts:378-537](file://src/lib/sdk/client.ts#L378-L537)
- [permission-bridge.ts:41-56](file://src/lib/sdk/permission-bridge.ts#L41-L56)
- [permission/route.ts:6-41](file://src/app/api/chat/permission/route.ts#L6-L41)
- [chat-view.tsx:1437-1700](file://src/components/views/chat-view.tsx#L1437-L1700)
- [message-mapper.ts:53-85](file://src/lib/sdk/message-mapper.ts#L53-L85)
- [browser-tools.ts:99-198](file://src/lib/browser-tools.ts#L99-L198)

**章节来源**
- [client.ts:378-537](file://src/lib/sdk/client.ts#L378-L537)
- [permission-bridge.ts:41-56](file://src/lib/sdk/permission-bridge.ts#L41-L56)
- [permission/route.ts:6-41](file://src/app/api/chat/permission/route.ts#L6-L41)
- [chat-view.tsx:1437-1700](file://src/components/views/chat-view.tsx#L1437-L1700)
- [message-mapper.ts:53-85](file://src/lib/sdk/message-mapper.ts#L53-L85)
- [browser-tools.ts:99-198](file://src/lib/browser-tools.ts#L99-L198)

## 性能考量
- 会话活动跟踪：每允许一次工具调用都会更新会话活动时间，用于 30 分钟后自动清理，防止内存泄漏
- 待决请求超时：120 秒自动超时，避免 UI 长时间挂起
- 全局状态共享：使用 globalThis 存储 Map/Set，确保不同路由处理器间状态一致
- 浏览器工具空闲关闭：15 分钟无活动自动关闭，降低资源占用

**章节来源**
- [permission-bridge.ts:226-256](file://src/lib/sdk/permission-bridge.ts#L226-L256)
- [permission-bridge.ts:56](file://src/lib/sdk/permission-bridge.ts#L56)
- [browser-tools.ts:13-34](file://src/lib/browser-tools.ts#L13-L34)

## 故障排除指南
- 症状：.browsersafetyCheck 返回 'ask' 且无法继续
  - 原因：SDK 在 bypassPermissions 模式下仍会先执行 .claude/ 安全检查
  - 解决：确保提供了 canUseTool 或使用默认桥接，以便在 .claude/ 写入时自动批准
  - 参考：[client.ts:453-488](file://src/lib/sdk/client.ts#L453-L488)
- 症状：前端无权限弹窗
  - 原因：SSE 发送失败或客户端断开
  - 解决：检查网络连接与 SSE 通道；查看后端日志中的警告信息
  - 参考：[permission-bridge.ts:109-120](file://src/lib/sdk/permission-bridge.ts#L109-L120)
- 症状：权限决策无效或重复提交
  - 原因：前端可能向所有同类请求发送决策
  - 解决：后端已处理重复调用；确认 Allow Session 是否正确触发批量解决
  - 参考：[permission/route.ts:33-40](file://src/app/api/chat/permission/route.ts#L33-L40)、[permission-bridge.ts:193-210](file://src/lib/sdk/permission-bridge.ts#L193-L210)
- 症状：权限规则被用户/本地预设覆盖
  - 原因：未限制 settingSources 导致加载了预设规则
  - 解决：提供自定义 canUseTool 时，系统会强制设置为 ['project']
  - 参考：[client.ts:489-497](file://src/lib/sdk/client.ts#L489-L497)
- 症状：浏览器工具异常或资源未释放
  - 原因：长时间无活动未关闭
  - 解决：等待空闲自动关闭或手动重启应用
  - 参考：[browser-tools.ts:23-34](file://src/lib/browser-tools.ts#L23-L34)

**章节来源**
- [client.ts:453-488](file://src/lib/sdk/client.ts#L453-L488)
- [permission-bridge.ts:109-120](file://src/lib/sdk/permission-bridge.ts#L109-L120)
- [permission/route.ts:33-40](file://src/app/api/chat/permission/route.ts#L33-L40)
- [permission-bridge.ts:193-210](file://src/lib/sdk/permission-bridge.ts#L193-L210)
- [client.ts:489-497](file://src/lib/sdk/client.ts#L489-L497)
- [browser-tools.ts:23-34](file://src/lib/browser-tools.ts#L23-L34)

## 结论
Forge 权限控制系统通过 SDK 桥接、前端 UI 与后端 API 的协同，实现了细粒度的工具调用控制。针对 .claude/ 目录写入的安全限制，系统在 canUseTool 中提供了自动批准机制，确保 bypassPermissions 模式下的可用性。通过限制 settingSources 与严格的审计日志，系统在功能与安全之间取得了平衡。建议在生产环境中保持 confirm 模式，并定期审查权限日志。

## 附录
- 权限日志查询：可通过 getPermissionLog(limit) 获取最近的决策记录
- 设置持久化：通过 /api/settings 接口读写数据库中的设置键值
- IM 权限模式：IM 场景下可通过命令切换 permission_mode，影响平台接入的权限策略

**章节来源**
- [permission-bridge.ts:279-281](file://src/lib/sdk/permission-bridge.ts#L279-L281)
- [settings/route.ts:19-38](file://src/app/api/settings/route.ts#L19-L38)
- [im/commands.ts:840-866](file://src/lib/im/commands.ts#L840-L866)