Step 17: 权限与安全系统
1. 权限系统架构总览
Claude Code 的权限与安全系统是一个多层防御体系,从宏观到微观分为以下层次:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
┌─────────────────────────────────────────────────────────┐
│ 权限模式状态机 (Permission Mode) │
│ default → plan → acceptEdits → auto → bypassPermissions │
├─────────────────────────────────────────────────────────┤
│ 规则引擎 (Rule Engine) │
│ alwaysAllow / alwaysDeny / alwaysAsk 规则匹配 │
├───────────────────────┬─────────────────────────────────┤
│ Yolo/Auto 分类器 │ Bash 安全检查 │
│ (LLM 侧查询) │ (AST 解析 + 模式匹配) │
├───────────────────────┴─────────────────────────────────┤
│ 沙箱层 (Sandbox) │
│ bwrap/bubblewrap 文件系统 + 网络隔离 │
├─────────────────────────────────────────────────────────┤
│ SSRF 防护 (HTTP Hooks) │
│ DNS 解析拦截, 私有 IP 阻断 │
├─────────────────────────────────────────────────────────┤
│ Kill Switch / 紧急制动 │
│ Statsig 特性门控, 服务端远程禁用 │
└─────────────────────────────────────────────────────────┘
核心文件分布:
| 层次 | 文件 | 职责 |
|---|---|---|
| 权限核心 | permissions.ts |
权限检查主流程 (hasPermissionsToUseTool) |
| 权限模式 | PermissionMode.ts |
模式枚举与配置 |
| 权限规则 | PermissionRule.ts |
规则类型定义 |
| 规则解析 | permissionRuleParser.ts |
规则字符串序列化/反序列化 |
| 规则加载 | permissionSetup.ts |
初始化上下文, 危险规则剥离, kill switch |
| 规则更新 | PermissionUpdate.ts |
用户交互后持久化规则变更 |
| Yolo 分类器 | yoloClassifier.ts |
Auto 模式下 AI 分类决策 |
| Bash 分类器 | bashClassifier.ts |
Bash 命令本地分类 (deny/ask/allow) |
| 危险模式 | dangerousPatterns.ts |
危险命令前缀清单 |
| 文件系统 | filesystem.ts |
文件路径安全检查 |
| Kill Switch | bypassPermissionsKillswitch.ts |
远程禁用机制 |
| 拒绝追踪 | denialTracking.ts |
连续拒绝回退机制 |
| Bash 安全 | bashSecurity.ts |
命令注入/危险模式检测 |
| Bash 权限 | bashPermissions.ts |
Bash/PowerShell 权限匹配 |
| 沙箱适配 | sandbox-adapter.ts |
沙箱配置转换与初始化 |
| SSRF 防护 | ssrfGuard.ts |
HTTP Hooks 的 SSRF 防护 |
2. 权限模式状态机
2.1 模式定义
权限模式定义在 src/types/permissions.ts 中:
1
2
3
4
5
// 外部可用模式
type ExternalPermissionMode = 'acceptEdits' | 'bypassPermissions' | 'default' | 'dontAsk' | 'plan'
// 内部模式 (包含 ant-only 模式)
type PermissionMode = ExternalPermissionMode | 'auto' | 'bubble'
2.2 模式配置
每个模式有对应的配置 (PermissionMode.ts):
| 模式 | 标题 | 符号 | 颜色 | 说明 |
|---|---|---|---|---|
default |
Default | (空) | text | 默认模式, 每次操作都需确认 |
plan |
Plan Mode | ⏸ | planMode | 只读规划, 不执行修改操作 |
acceptEdits |
Accept edits | ⏵⏵ | autoAccept | 自动接受文件编辑, 其他操作仍需确认 |
bypassPermissions |
Bypass Permissions | ⏵⏵ | error | 绕过所有权限检查 |
dontAsk |
Don’t Ask | ⏵⏵ | error | 静默拒绝所有权限请求 |
auto |
Auto mode | ⏵⏵ | warning | AI 分类器自动决策 (ant-only) |
2.3 模式流转
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
┌─────────┐
启动 ───→ │ default │ ←── 模式重置
└────┬────┘
│ 用户 Tab/Shift-Tab 切换
┌──────────┼──────────┐
▼ ▼ ▼
┌─────────┐ ┌────────┐ ┌──────────────┐
│ plan │ │acceptEd│ │bypassPerms* │
│(只读) │ │(自动编辑)│ │(全权绕过) │
└────┬────┘ └───┬────┘ └──────┬───────┘
│ │ │
│ plan中Tab│ │
└──────┐ │ │
▼ │ │
┌────────┐│ │
│ auto ││ (ant-only) │
│(AI决策) ││ │
└────────┘│ │
│ │
┌─────────┘ │
│ dontAsk │
│ (静默拒绝) │
└───────────────────────┘
* bypassPermissions 需要 Statsig 门控通过 + 设置允许
关键流转逻辑:
- default → plan: 用户按 Shift+Tab 进入计划模式
- plan → auto: 在 plan 模式中按 Tab 可以激活 auto 模式 (需要 TRANSCRIPT_CLASSIFIER feature flag)
- default → acceptEdits: 用户按 Tab 切换到自动编辑模式
- default → bypassPermissions: 需要
isBypassPermissionsModeAvailable为 true,且 Statsig 门控未禁用 - 任何模式 → dontAsk: 异步 Agent 模式下,当无法弹出提示时自动进入
2.4 Kill Switch 机制对模式的影响
bypassPermissionsKillswitch.ts 实现了两个关键的远程禁用机制:
-
Bypass Permissions Kill Switch: 如果 Statsig 特性门控
tengu_disable_bypass_permissions_mode启用,或者settings.permissions.disableBypassPermissionsMode === 'disable',则强制切回default模式并设置isBypassPermissionsModeAvailable: false。极端情况下调用gracefulShutdown(1, 'bypass_permissions_disabled')关闭进程。 -
Auto Mode Gate:
verifyAutoModeGateAccess()检查以下条件:- GrowthBook 配置的
enabled状态 (enabled/disabled/opt-in) - 用户是否显式 opt-in (
skipAutoPermissionPrompt设置 或 CLI--enable-auto-mode标志) - 模型是否支持 auto 模式
- Circuit breaker 是否触发(连续拒绝过多时自动禁用)
- GrowthBook 配置的
3. 规则评估流程
3.1 规则数据结构
权限规则存储在 ToolPermissionContext 中:
1
2
3
4
5
6
7
8
9
10
11
12
type ToolPermissionContext = {
mode: PermissionMode
additionalWorkingDirectories: ReadonlyMap<string, AdditionalWorkingDirectory>
alwaysAllowRules: ToolPermissionRulesBySource // 按 source 分组的允许规则
alwaysDenyRules: ToolPermissionRulesBySource // 按 source 分组的拒绝规则
alwaysAskRules: ToolPermissionRulesBySource // 按 source 分组的询问规则
isBypassPermissionsModeAvailable: boolean // bypass 模式是否可用
strippedDangerousRules?: ToolPermissionRulesBySource // 被剥离的危险规则
shouldAvoidPermissionPrompts?: boolean // 是否避免弹出提示
awaitAutomatedChecksBeforeDialog?: boolean
prePlanMode?: PermissionMode
}
规则来源 (PermissionRuleSource) 优先级从高到低:
policySettings- 企业策略设置 (最高优先级)flagSettings- 命令行标志userSettings- 全局用户设置 (~/.claude/settings.json)projectSettings- 项目设置 (.claude/settings.json)localSettings- 本地项目设置 (.claude/settings.local.json)cliArg- CLI 参数command- /命令 会话内规则session- 会话级规则
3.2 规则解析格式
permissionRuleParser.ts 处理规则的序列化格式:
1
2
格式: "ToolName" 或 "ToolName(content)"
内容可包含转义括号: \( 和 \)
解析逻辑:
Bash→{ toolName: 'Bash' }(工具级规则, 匹配所有 Bash 操作)Bash(npm install)→{ toolName: 'Bash', ruleContent: 'npm install' }(精确匹配)Bash(python:*)→{ toolName: 'Bash', ruleContent: 'python:*' }(前缀通配符)Bash(python -c "print\(1\)")→{ toolName: 'Bash', ruleContent: 'python -c "print(1)"' }(转义处理)
3.3 评估流程
hasPermissionsToUseTool (在 permissions.ts 中) 是权限检查的入口,完整流程如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
输入: tool, input, context, assistantMessage, toolUseID
│
▼
┌─── bypassPermissions 模式? ───→ 直接 allow
│
▼
┌─── 检查 alwaysDeny 规则 ───→ 命中则 deny
│
▼
┌─── 检查 alwaysAllow 规则 ───→ 命中则 allow
│
▼
┌─── 检查 alwaysAsk 规则 ───→ 命中则 ask
│
▼
┌─── 调用 tool.checkPermissions() ───→ 工具特定安全检查
│ │
│ ├─ 文件系统安全检查 (filesystem.ts)
│ ├─ Bash 安全检查 (bashSecurity.ts)
│ ├─ PowerShell 安全检查
│ ├─ 工作目录检查
│ └─ Hook 检查
│
▼
┌─── dontAsk 模式? ───→ 静默 deny
│
▼
┌─── auto/plan+autoActive 模式? ───→ Yolo 分类器
│ │
│ ├─ acceptEdits 快速路径: 先尝试 acceptEdits 模式检查
│ ├─ 安全工具白名单: 直接 allow
│ └─ AI 分类器: classifyYoloAction()
│
▼
┌─── 其他模式 ───→ ask (弹出用户提示)
│
▼
结果: { behavior: 'allow' | 'deny' | 'ask' }
3.4 规则匹配细节
对于 Bash 工具,规则匹配有特殊逻辑(bashPermissions.ts):
- 精确匹配:
Bash(npm install)只匹配npm install - 前缀匹配:
Bash(npm:*)匹配所有以npm开头的命令 - 通配符匹配:
Bash(npm run *)匹配npm run build等 - 安全包装器剥离:
timeout 30 npm run build先剥离timeout后匹配npm run - 环境变量剥离:
NODE_ENV=prod npm run build剥离NODE_ENV=prod后匹配
4. Yolo/Auto 分类器
4.1 概述
Auto 模式(又称 Yolo 模式)是 ant-only 的功能,通过 AI 分类器自动决定是允许还是阻断操作,取代用户的手动确认。核心实现在 yoloClassifier.ts。
4.2 分类器架构
分类器采用两阶段 XML 分类(2-Stage XML Classifier):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
┌──────────────────────────────────────┐
│ 构建 Action 描述 │
│ formatActionForClassifier() │
│ 将工具调用序列化为紧凑格式 │
└──────────────┬───────────────────────┘
│
▼
┌──────────────────────────────────────┐
│ 构建 System Prompt │
│ buildYoloSystemPrompt() │
│ 含权限模板 + 用户 allow/deny 规则 │
└──────────────┬───────────────────────┘
│
▼
┌──────────────────────────────────────┐
│ 构建 Transcript (上下文) │
│ buildTranscriptEntries() │
│ 用户消息 + 助手工具调用历史 │
│ + CLAUDE.md 用户配置 │
└──────────────┬───────────────────────┘
│
▼
┌──────────────────────────────────────┐
│ Stage 1: 快速分类 │
│ XML 格式: <block>yes/no</block> │
│ 较小模型, 低延迟 │
│ 后缀: "Err on the side of │
│ blocking. <block> immediately"│
└──────────────┬───────────────────────┘
│
┌─────┴─────┐
│ 明确? │
├─ 是 ──→ 返回结果
└─ 否 ──→ Stage 2
│
▼
┌──────────────────────────────────────┐
│ Stage 2: 深度推理 │
│ 带 extended thinking 的深度分析 │
│ 后缀: "Review the classification │
│ process and follow it │
│ carefully..." │
└──────────────┬───────────────────────┘
│
▼
返回 shouldBlock + reason
4.3 分类器 Prompt 组成
系统 Prompt 由以下部分组装:
- BASE_PROMPT (
auto_mode_system_prompt.txt): 基础安全分类指令 - 权限模板:
- Anthropic 内部:
permissions_anthropic.txt(更严格的安全规则) - 外部用户:
permissions_external.txt(标准安全规则)
- Anthropic 内部:
- 用户自定义规则 (settings.autoMode):
allow: 用户定义的允许描述列表soft_deny: 用户定义的拒绝描述列表environment: 用户定义的环境描述列表
4.4 Bash 本地分类器
在进入 LLM 分类器之前,Bash 命令首先经过本地分类器 (bashClassifier.ts):
1
2
3
4
5
6
7
type ClassifierResult = {
matches: boolean // 是否匹配
matchedDescription?: string // 匹配的规则描述
confidence: 'high' | 'medium' | 'low' // 置信度
reason: string // 分类原因
}
type ClassifierBehavior = 'deny' | 'ask' | 'allow'
本地分类器有三类描述列表:
- deny: 始终拒绝的命令描述
- ask: 需要提示用户的命令描述
- allow: 允许自动执行的命令描述
当 feature('BASH_CLASSIFIER') 启用时,本地分类器的结果会融入 Yolo 分类器的 Prompt 中。
4.5 拒绝追踪与回退
denialTracking.ts 实现了连续拒绝计数器:
1
2
3
4
5
6
7
8
9
type DenialTrackingState = {
consecutiveDenials: number // 连续拒绝次数
totalDenials: number // 总拒绝次数
}
const DENIAL_LIMITS = {
maxConsecutive: 3, // 连续 3 次拒绝后回退到提示模式
maxTotal: 20, // 总共 20 次拒绝后回退到提示模式
}
当 auto 模式下分类器连续拒绝达到阈值时,系统会回退到手动提示模式,防止 AI 在误判时无限循环。
4.6 acceptEdits 快速路径
在调用昂贵的 AI 分类器之前,系统会先检查 acceptEdits 模式是否允许该操作:
1
2
3
4
5
6
7
8
9
10
11
12
// 如果 acceptEdits 模式会允许此操作,跳过分类器
const acceptEditsResult = await tool.checkPermissions(parsedInput, {
...context,
getAppState: () => ({
...state,
toolPermissionContext: { ...state.toolPermissionContext, mode: 'acceptEdits' },
}),
})
if (acceptEditsResult.behavior === 'allow') {
// 快速路径:直接允许,不调用分类器
return { behavior: 'allow', decisionReason: { type: 'mode', mode: 'auto' } }
}
这避免了不必要的分类器 API 调用,特别是对文件编辑操作。
4.7 安全工具白名单
classifierDecision.ts 中有一个安全工具白名单 (isAutoModeAllowlistedTool),以下工具在 auto 模式下直接放行,不需要分类器评估:
- 读取类工具 (Read, Glob, Grep 等)
- 搜索类工具
- 其他非破坏性工具
5. 沙箱架构
5.1 沙箱层级
1
2
3
4
5
6
7
8
9
10
11
12
13
14
┌─────────────────────────────────────────────┐
│ SandboxManager (适配层) │
│ 将 Claude Code 配置转换为 sandbox-runtime │
│ 配置格式, 处理路径解析, 订阅设置变更 │
├─────────────────────────────────────────────┤
│ BaseSandboxManager (sandbox-runtime) │
│ 底层 bwrap/bubblewrap 进程沙箱管理 │
│ 文件系统挂载, 网络代理, 资源限制 │
├─────────────────────────────────────────────┤
│ OS 级沙箱 (bubblewrap/bwrap) │
│ Linux: bwrap + socat 代理 │
│ macOS: sandbox-exec │
│ Windows: 不支持 (WSL2 可用) │
└─────────────────────────────────────────────┘
5.2 沙箱配置
convertToSandboxRuntimeConfig() 将 Claude Code 设置转换为沙箱运行时配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
type SandboxRuntimeConfig = {
network: {
allowedDomains: string[] // 允许的网络域名
deniedDomains: string[] // 拒绝的网络域名
allowUnixSockets?: string[] // 允许的 Unix socket
allowLocalBinding?: boolean // 允许本地绑定
httpProxyPort?: number // HTTP 代理端口
socksProxyPort?: number // SOCKS 代理端口
}
filesystem: {
allowWrite: string[] // 允许写入的路径
denyWrite: string[] // 拒绝写入的路径
allowRead: string[] // 允许读取的路径
denyRead: string[] // 拒绝读取的路径
}
// ...其他配置
}
5.3 关键安全配置
写入保护 (始终拒绝写入):
- 所有 settings.json 文件 (防止沙箱逃逸)
.claude/skills/目录 (技能文件具有等同于命令的权限).git/下的裸仓库文件 (防止 git 钩子注入攻击)
写入允许 (始终允许写入):
- 当前工作目录 (
.) - Claude 临时目录 (
/tmp/claude-{uid}/) - Git worktree 的主仓库
.git目录 - 用户通过
--add-dir添加的目录
路径解析规则:
//path→ 绝对路径 (从文件系统根目录)/path→ 相对于设置文件目录~/path→ 用户主目录./path→ 相对于当前工作目录
5.4 沙箱排除命令
shouldUseSandbox.ts 决定命令是否需要在沙箱内运行:
1
2
3
4
5
6
7
8
9
10
11
12
13
function shouldUseSandbox(input: Partial<SandboxInput>): boolean {
// 1. 沙箱未启用 → 不使用
if (!SandboxManager.isSandboxingEnabled()) return false
// 2. 显式禁用沙箱 + 策略允许非沙箱命令 → 不使用
if (input.dangerouslyDisableSandbox &&
SandboxManager.areUnsandboxedCommandsAllowed()) return false
// 3. 命令包含排除列表中的命令 → 不使用
if (containsExcludedCommand(input.command)) return false
return true // 其他情况使用沙箱
}
排除命令匹配支持:
- 精确匹配
- 前缀匹配 (
bazel:*) - 通配符匹配
- 环境变量和包装器剥离 (
NODE_ENV=prod bazel run→ 匹配bazel:*)
5.5 裸仓库攻击防御
sandbox-adapter.ts 包含了一个关键的安全防御——防止通过创建假 git 仓库来逃逸沙箱:
1
2
3
4
5
6
// 攻击向量: 在沙箱内创建 HEAD + objects/ + refs/ 使 git 认为当前目录是裸仓库
// 然后通过 core.fsmonitor 钩子在沙箱外的 git 操作中执行任意代码
//
// 防御:
// 1. 如果文件存在 → denyWrite (只读绑定)
// 2. 如果文件不存在 → scrubBareGitRepoFiles() 在沙箱命令后清理
6. SSRF 防护
6.1 概述
ssrfGuard.ts 为 HTTP Hooks 提供服务器端请求伪造 (SSRF) 防护,阻止私有/链路本地 IP 地址的请求。
6.2 阻断规则
| IP 范围 | 描述 | 动作 |
|---|---|---|
0.0.0.0/8 |
“this” 网络 | 阻断 |
10.0.0.0/8 |
私有网络 (A 类) | 阻断 |
100.64.0.0/10 |
CGNAT / 共享地址空间 | 阻断 |
169.254.0.0/16 |
链路本地 (云元数据) | 阻断 |
172.16.0.0/12 |
私有网络 (B 类) | 阻断 |
192.168.0.0/16 |
私有网络 (C 类) | 阻断 |
127.0.0.0/8 |
本地回环 | 允许 |
::1 |
IPv6 本地回环 | 允许 |
:: |
IPv6 未指定 | 阻断 |
fc00::/7 |
IPv6 唯一本地 | 阻断 |
fe80::/10 |
IPv6 链路本地 | 阻断 |
::ffff:a.b.c.d |
IPv4 映射的 IPv6 | 委托给 v4 检查 |
6.3 实现
ssrfGuardedLookup() 是一个与 dns.lookup 兼容的函数,用作 axios 的自定义 DNS 解析器:
1
2
3
4
5
// 关键实现细节:
// 1. IP 字面量直接验证,不经过 DNS
// 2. 域名先通过 dns.lookup 解析,然后验证所有结果 IP
// 3. 任何结果 IP 在阻断列表中 → 整个请求被拒绝
// 4. IPv4 映射的 IPv6 地址 (如 ::ffff:169.254.169.254) 会被提取并检查
特别注意:当使用全局代理或沙箱网络代理时,SSRF 防护实际上被绕过,因为代理执行自己的 DNS 解析。沙箱代理有自己的域名白名单。
7. Hook 安全模型
7.1 Hook 类型与权限交互
Claude Code 支持以下 Hook 类型,它们可以影响权限决策:
- PreToolUse: 工具调用前执行,可以修改输入或拒绝操作
- PostToolUse: 工具调用后执行
- Notification: 通知事件
- Stop: 停止事件
7.2 Hook 对权限的影响
在 hasPermissionsToUseTool 流程中,Hook 的 PermissionRequest 钩子可以在无交互环境(如异步 Agent)中做权限决策:
1
2
3
4
// 异步 Agent 中的 Hook 优先级:
// 1. Hook 返回 allow → 允许执行
// 2. Hook 返回 deny → 拒绝执行
// 3. Hook 返回无决策 → 自动拒绝 (无交互环境默认拒绝)
Hook 决策的 decisionReason 类型为 { type: 'hook', hookName: string, reason?: string },与规则决策、模式决策等并列。
8. 危险模式清单
8.1 Bash 危险命令前缀 (dangerousPatterns.ts)
跨平台代码执行入口点:
1
2
3
4
5
6
7
8
9
10
export const CROSS_PLATFORM_CODE_EXEC = [
// 解释器
'python', 'python3', 'python2', 'node', 'deno', 'tsx', 'ruby', 'perl', 'php', 'lua',
// 包管理器运行器
'npx', 'bunx', 'npm run', 'yarn run', 'pnpm run', 'bun run',
// Shell
'bash', 'sh',
// 远程命令
'ssh',
]
仅 Unix 的危险命令 (ant 额外添加):
1
2
3
// ant-only 额外列表:
'fa run', 'coo', 'gh', 'gh api', 'curl', 'wget', 'git',
'kubectl', 'aws', 'gcloud', 'gsutil'
完整 Bash 危险模式:
1
2
3
4
5
6
7
8
9
export const DANGEROUS_BASH_PATTERNS = [
...CROSS_PLATFORM_CODE_EXEC, // 上面所有跨平台命令
'zsh', 'fish', // 额外 shell
'eval', 'exec', // 代码执行
'env', // 环境变量操纵
'xargs', // 批量命令执行
'sudo', // 权限提升
// ant-only 额外命令...
]
8.2 PowerShell 危险命令 (permissionSetup.ts)
1
2
3
4
5
6
7
8
9
10
11
// PowerShell 特有的危险命令:
'pwsh', 'powershell', 'cmd', 'wsl', // Shell 嵌套
'iex', 'invoke-expression', // 代码执行
'icm', 'invoke-command', // 远程执行
'start-process', 'saps', 'start', // 进程启动
'start-job', 'sajb', 'start-threadjob', // 后台任务
'register-objectevent', 'register-engineevent', // 事件注册
'register-wmievent', 'register-scheduledjob', // 计划任务
'new-pssession', 'nsn', 'enter-pssession', 'etsn', // 远程会话
'add-type', // .NET C# 注入
'new-object', // COM 对象
8.3 Bash 安全检查模式 (bashSecurity.ts)
命令替换和注入模式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const COMMAND_SUBSTITUTION_PATTERNS = [
{ pattern: /<\(/, message: 'process substitution <()' },
{ pattern: />\(/, message: 'process substitution >()' },
{ pattern: /=\(/, message: 'Zsh process substitution =()' },
{ pattern: /(?:^|[\s;&|])=[a-zA-Z_]/, message: 'Zsh equals expansion (=cmd)' },
{ pattern: /\$\(/, message: '$() command substitution' },
{ pattern: /\$\{/, message: '${} parameter substitution' },
{ pattern: /\$\[/, message: '$[] legacy arithmetic expansion' },
{ pattern: /~\[/, message: 'Zsh-style parameter expansion' },
{ pattern: /\(e:/, message: 'Zsh-style glob qualifiers' },
{ pattern: /\(\+/, message: 'Zsh glob qualifier with command execution' },
{ pattern: /\}\s*always\s*\{/, message: 'Zsh always block (try/always construct)' },
{ pattern: /<#/, message: 'PowerShell comment syntax' },
]
Zsh 危险内置命令:
1
2
3
4
5
6
7
8
9
10
11
12
const ZSH_DANGEROUS_COMMANDS = new Set([
'zmodload', // 模块加载网关
'emulate', // eval 等价物
'sysopen', // 文件描述符控制
'sysread', 'syswrite', 'sysseek', // FD 读写
'zpty', // 伪终端执行
'ztcp', // TCP 连接
'zsocket', // Unix/TCP socket
'mapfile', // 关联数组文件 I/O
'zf_rm', 'zf_mv', 'zf_ln', 'zf_chmod', 'zf_chown', // 内置文件操作
'zf_mkdir', 'zf_rmdir', 'zf_chgrp',
])
8.4 文件系统危险路径 (filesystem.ts)
危险文件(自动编辑时需要额外确认):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
export const DANGEROUS_FILES = [
'.gitconfig', '.gitmodules', // Git 配置
'.bashrc', '.bash_profile', // Shell 配置
'.zshrc', '.zprofile', '.profile', // Shell 配置
'.ripgreprc', // 搜索工具配置
'.mcp.json', '.claude.json', // Claude 配置
]
export const DANGEROUS_DIRECTORIES = [
'.git', // Git 仓库
'.vscode', // VS Code 配置
'.idea', // JetBrains 配置
'.claude', // Claude 配置
]
Windows 特殊路径模式检测:
- NTFS 交替数据流 (
file.txt::$DATA) - 8.3 短文件名 (
GIT~1) - 长路径前缀 (
\\?\C:\...,\\.\C:\...) - 尾部点和空格 (
.git.→ 规范化为.git) - DOS 设备名 (
.git.CON,.bashrc.AUX) - UNC 路径 (
\\server\share) - 路径遍历 (
...)
9. Kill Switch 机制
9.1 Bypass Permissions Kill Switch
bypassPermissionsKillswitch.ts 实现了两个独立的 kill switch:
远程禁用 (Statsig 门控)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
async function checkAndDisableBypassPermissionsIfNeeded(
toolPermissionContext: ToolPermissionContext,
setAppState: (f: (prev: AppState) => AppState) => void,
): Promise<void> {
// 只运行一次 (启动时检查)
if (bypassPermissionsCheckRan) return
bypassPermissionsCheckRan = true
// 检查 bypass 模式是否可用
if (!toolPermissionContext.isBypassPermissionsModeAvailable) return
// 查询远程 Statsig 门控
const shouldDisable = await shouldDisableBypassPermissions()
if (!shouldDisable) return
// 强制切换到 default 模式并标记 bypass 不可用
setAppState(prev => ({
...prev,
toolPermissionContext: createDisabledBypassPermissionsContext(prev.toolPermissionContext),
}))
}
紧急关闭
当 Statsig 门控触发但进程已经在 bypass 模式时:
1
2
3
4
5
6
7
8
9
10
async function checkAndDisableBypassPermissions(
currentContext: ToolPermissionContext,
): Promise<void> {
if (!currentContext.isBypassPermissionsModeAvailable) return
const shouldDisable = await shouldDisableBypassPermissions()
if (!shouldDisable) return
// 紧急关闭进程!
void gracefulShutdown(1, 'bypass_permissions_disabled')
}
9.2 Auto Mode Gate
Auto 模式有一个类似的多层门控机制:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
async function checkAndDisableAutoModeIfNeeded(
toolPermissionContext: ToolPermissionContext,
setAppState: (f: (prev: AppState) => AppState) => void,
fastMode?: boolean,
): Promise<void> {
// 1. 检查 GrowthBook 配置
const { updateContext, notification } = await verifyAutoModeGateAccess(
toolPermissionContext,
fastMode,
)
// 2. 更新权限上下文 (可能降级 auto → default)
setAppState(prev => {
const nextCtx = updateContext(prev.toolPermissionContext)
// 可能附带通知消息
if (notification) {
return { ...prev, toolPermissionContext: nextCtx, notifications: { ... } }
}
return nextCtx === prev.toolPermissionContext ? prev : { ...prev, toolPermissionContext: nextCtx }
})
}
verifyAutoModeGateAccess() 检查:
- GrowthBook 配置:
enabled: 'enabled'→ 自动模式可用enabled: 'disabled'→ 自动模式被远程禁用 (circuit breaker)enabled: 'opt-in'→ 需要用户显式 opt-in
- 设置检查:
settings.permissions.disableAutoMode === 'disable'→ 禁用
-
模型支持: 模型是否支持自动模式
- Fast Mode 断路器: 某些模型配置下自动模式在 fast mode 中不可用
9.3 安全规则剥离
进入 auto/bypass 模式时,系统会自动剥离危险权限规则 (permissionSetup.ts):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 在 auto 模式下剥离的危险规则:
// - Bash(*) 或 Bash(无内容) → 允许所有 Bash 命令
// - Bash(python:*) 等解释器前缀规则
// - PowerShell(*) 或 PowerShell(iex:*) 等
// - Agent(*) → 允许所有子代理 (绕过分类器评估)
function isDangerousClassifierPermission(
toolName: string,
ruleContent: string | undefined,
): boolean {
return (
isDangerousBashPermission(toolName, ruleContent) ||
isDangerousPowerShellPermission(toolName, ruleContent) ||
isDangerousTaskPermission(toolName, ruleContent)
)
}
被剥离的规则存储在 strippedDangerousRules 中,退出 auto 模式时可以恢复。
9.4 Kill Switch 重置
登录后可以重置 kill switch 状态,允许重新检查:
1
2
3
4
5
6
7
export function resetBypassPermissionsCheck(): void {
bypassPermissionsCheckRan = false
}
export function resetAutoModeGateCheck(): void {
autoModeCheckRan = false
}
10. 关键安全设计原则总结
纵深防御 (Defense in Depth)
权限系统不是单一检查点,而是多层防御:
- 规则层: alwaysDeny > alwaysAllow > alwaysAsk
- 工具特定检查层: 每个工具的
checkPermissions()方法 - 分类器层: AI 模型评估操作安全性
- 沙箱层: OS 级别的文件系统和网络隔离
- Hook 层: 用户自定义的前置/后置钩子
Fail-Safe 默认
系统始终采用”最安全”的默认值:
- 未知模式 → 回退到
default(需要确认) - 分类器不可用 → 回退到提示用户
- 连续拒绝超过阈值 → 回退到提示模式
- Kill switch 触发 → 强制关闭或降级模式
最小权限原则
- Auto 模式自动剥离危险规则 (不信任过宽的 allow 规则)
- 沙箱默认拒绝写入关键配置文件
- SSRF 防护默认阻断所有私有 IP
- bypassPermissions 模式有远程禁用开关
时间敏感安全
- Kill switch 只运行一次 (启动时),登录后重置
- 拒绝追踪有最大连续数和总数限制
- 沙箱配置在设置变更时动态更新 (通过
settingsChangeDetector)