目录

Claude Code 权限与安全系统源码解读

Claude Code 权限与安全系统源码解读

分析版本:Claude Code Main (Transcription Classifier Feature) 源码路径:/mnt/e/code/cc/claude-code-main oho 项目路径:/mnt/d/fe/opencode_cli/oho


目录

  1. 架构概览
  2. Permission Mode 实现
  3. 命令白名单/黑名单
  4. Dangerous 工具识别
  5. 用户确认流程
  6. 安全边界设计
  7. oho CLI 权限实现
  8. 核心文件索引

1. 架构概览

Claude Code 的权限安全系统采用分层过滤架构,核心设计思想:

┌─────────────────────────────────────────────────────────────┐
│                     Tool Use Request                        │
└─────────────────────────────────────────────────────────────┘
                               │
                               ▼
┌─────────────────────────────────────────────────────────────┐
│  Step 1: Rule-Based Permission Check (checkRuleBasedPermissions) │
│  ┌─────────────────────────────────────────────────────┐    │
│  │ 1a. 工具级拒绝规则 (alwaysDenyRules)                │    │
│  │ 1b. 工具级询问规则 (alwaysAskRules)                  │    │
│  │ 1c. 工具特定权限检查 (tool.checkPermissions)        │    │
│  │ 1d. 工具实现拒绝                                    │    │
│  │ 1e. 用户交互要求检查                                │    │
│  │ 1f. 内容级询问规则                                  │    │
│  │ 1g. 安全检查 (bypass-immune)                        │    │
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘
                               │
                               ▼
┌─────────────────────────────────────────────────────────────┐
│  Step 2: Mode-Based Permission Check                        │
│  ┌─────────────────────────────────────────────────────┐    │
│  │ 2a. bypassPermissions / plan + bypassAvailable     │    │
│  │     → 直接 allow                                   │    │
│  │ 2b. alwaysAllowRules 匹配                         │    │
│  │     → 直接 allow                                   │    │
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘
                               │
                               ▼
┌─────────────────────────────────────────────────────────────┐
│  Step 3: Fallback - Ask User                                │
│  ┌─────────────────────────────────────────────────────┐    │
│  │ 3. passthrough → ask                              │    │
│  │ Auto Mode: 发送至 AI Classifier 判断               │    │
│  │ Default Mode: 弹出用户确认对话框                    │    │
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘

核心设计原则

  • 纵深防御:每层检查独立,失败不影响其他层
  • 显式优先:用户显式规则 > 模式决定 > 默认行为
  • 安全免疫:关键安全检查在 bypassPermissions 模式下仍触发确认
  • AI 辅助决策:Auto 模式使用分类器判断,降低打扰

2. Permission Mode 实现

2.1 模式定义

源码位置src/types/permissions.ts (L16-39)

// 用户可寻址模式
const EXTERNAL_PERMISSION_MODES = [
  'acceptEdits',    // 自动接受文件编辑
  'bypassPermissions', // 跳过所有确认
  'default',        // 默认询问模式
  'dontAsk',       // 自动拒绝所有
  'plan',          // 计划模式
] as const

// 内部模式 (ant 用户额外增加 'auto')
type InternalPermissionMode = ExternalPermissionMode | 'auto' | 'bubble'
type PermissionMode = InternalPermissionMode

模式配置src/utils/permissions/PermissionMode.ts (L42-91)

模式标题颜色说明
defaultDefaulttext正常询问
planPlan ModeplanMode计划模式 (显示暂停图标)
acceptEditsAccept editsautoAccept自动接受文件编辑
bypassPermissionsBypass Permissionserror危险 - 跳过所有确认
dontAskDon’t Askerror自动拒绝所有
autoAuto modewarningAI 分类器判断 (ANT-only)

2.2 权限上下文 (ToolPermissionContext)

核心数据结构src/Tool.ts (L123-138)

type ToolPermissionContext = {
  mode: PermissionMode                           // 当前权限模式
  additionalWorkingDirectories: Map<string>     // 额外工作目录
  alwaysAllowRules: ToolPermissionRulesBySource // 允许规则
  alwaysDenyRules: ToolPermissionRulesBySource  // 拒绝规则
  alwaysAskRules: ToolPermissionRulesBySource   // 询问规则
  isBypassPermissionsModeAvailable: boolean    // bypass 模式是否可用
  isAutoModeAvailable?: boolean                 // auto 模式是否可用
  strippedDangerousRules?: ToolPermissionRulesBySource // auto 模式剥离的危险规则
  shouldAvoidPermissionPrompts?: boolean         // 避免权限提示 (后台代理)
  awaitAutomatedChecksBeforeDialog?: boolean    // 对话框前等待自动检查
  prePlanMode?: PermissionMode                   // 进入 plan 前的模式
}

2.3 权限规则结构

源码位置src/types/permissions.ts (L67-79)

// 规则值:指定工具和可选内容
type PermissionRuleValue = {
  toolName: string       // 工具名,如 "Bash", "Read", "Edit"
  ruleContent?: string   // 内容,如 "rm -rf" 来自规则 "Bash(rm -rf:*)"
}

// 完整规则
type PermissionRule = {
  source: PermissionRuleSource  // 规则来源
  ruleBehavior: PermissionBehavior  // 'allow' | 'deny' | 'ask'
  ruleValue: PermissionRuleValue
}

规则来源优先级src/utils/permissions/permissions.ts (L109-114)

const PERMISSION_RULE_SOURCES = [
  ...SETTING_SOURCES,  // userSettings, projectSettings, localSettings
  'cliArg',           // 命令行参数
  'command',          // 命令
  'session',          // 会话
] as const

2.4 权限检查管道

核心函数src/utils/permissions/permissions.ts (L1158-1319)

async function hasPermissionsToUseToolInner(
  tool: Tool,
  input: { [key: string]: unknown },
  context: ToolUseContext,
): Promise<PermissionDecision> {
  // ========== Step 1: Rule-Based Checks ==========

  // 1a. 工具级拒绝规则
  const denyRule = getDenyRuleForTool(appState.toolPermissionContext, tool)
  if (denyRule) {
    return { behavior: 'deny', decisionReason: { type: 'rule', rule: denyRule } }
  }

  // 1b. 工具级询问规则 (沙箱自动允许除外)
  const askRule = getAskRuleForTool(appState.toolPermissionContext, tool)
  if (askRule && !canSandboxAutoAllow) {
    return { behavior: 'ask', decisionReason: { type: 'rule', rule: askRule } }
  }

  // 1c. 工具特定权限检查
  let toolPermissionResult = await tool.checkPermissions(parsedInput, context)

  // 1d. 工具实现拒绝
  if (toolPermissionResult?.behavior === 'deny') return toolPermissionResult

  // 1e. 工具要求用户交互
  if (tool.requiresUserInteraction?.() && toolPermissionResult?.behavior === 'ask') {
    return toolPermissionResult
  }

  // 1f. 内容级询问规则 (优先于 bypass)
  if (toolPermissionResult?.behavior === 'ask' &&
      toolPermissionResult.decisionReason?.type === 'rule' &&
      toolPermissionResult.decisionReason.rule.ruleBehavior === 'ask') {
    return toolPermissionResult
  }

  // 1g. 安全检查 (bypass-immune)
  if (toolPermissionResult?.behavior === 'ask' &&
      toolPermissionResult.decisionReason?.type === 'safetyCheck') {
    return toolPermissionResult
  }

  // ========== Step 2: Mode-Based Check ==========

  const shouldBypassPermissions =
    context.mode === 'bypassPermissions' ||
    (context.mode === 'plan' && context.isBypassPermissionsModeAvailable)

  if (shouldBypassPermissions) {
    return { behavior: 'allow', decisionReason: { type: 'mode', mode } }
  }

  // 2b. 始终允许规则
  const alwaysAllowedRule = toolAlwaysAllowedRule(appState.toolPermissionContext, tool)
  if (alwaysAllowedRule) {
    return { behavior: 'allow', decisionReason: { type: 'rule', rule: alwaysAllowedRule } }
  }

  // ========== Step 3: Fallback to Ask ==========
  return { behavior: 'ask', message: createPermissionRequestMessage(tool.name) }
}

2.5 模式切换

模式循环顺序 (Shift+Tab):default → acceptEdits → plan → bypassPermissions → auto → default

切换处理src/utils/permissions/permissionSetup.ts (L597-646)

export function transitionPermissionMode(
  fromMode: string,
  toMode: string,
  context: ToolPermissionContext,
): ToolPermissionContext {
  // plan 模式特殊处理
  handlePlanModeTransition(fromMode, toMode)
  handleAutoModeTransition(fromMode, toMode)

  // 进入 auto 模式:剥离危险权限
  if (toMode === 'auto' && fromMode !== 'auto') {
    context = stripDangerousPermissionsForAutoMode(context)
  }

  // 离开 auto 模式:恢复危险权限
  if (fromMode === 'auto' && toMode !== 'auto') {
    context = restoreDangerousPermissions(context)
  }

  return context
}

3. 命令白名单/黑名单

3.1 Bash 工具规则匹配

源码位置src/tools/BashTool/bashPermissions.ts

Bash 工具的权限检查采用多阶段匹配:

async function bashToolCheckPermission(
  input: BashInput,
  context: ToolUseContext,
): Promise<PermissionResult> {
  // 1. 精确匹配检查
  const exactMatch = bashToolCheckExactMatchPermission(input, context)

  // 2. 前缀/通配符匹配
  const prefixMatch = matchingRulesForInput(input, context)

  // 3. 路径约束检查
  const pathCheck = checkPathConstraints(input, context)

  // 4. Sed 约束检查
  const sedCheck = checkSedConstraints(input, context)

  // 5. 模式特定权限
  const modeCheck = checkPermissionMode(context)

  // 6. 只读命令检查
  const readOnlyCheck = checkReadOnlyConstraints(input, context)
}

3.2 规则匹配与安全剥离

关键安全机制src/utils/permissions/permissionSetup.ts (L94-147)

// 检查 Bash 权限规则是否危险 (auto 模式)
export function isDangerousBashPermission(
  toolName: string,
  ruleContent: string | undefined,
): boolean {
  // 工具级允许 (无内容或 Bash(*)) - 允许所有命令
  if (ruleContent === undefined || ruleContent === '') return true

  // 通配符 * 匹配一切
  if (ruleContent === '*') return true

  // 检查危险模式
  for (const pattern of DANGEROUS_BASH_PATTERNS) {
    if (content === pattern) return true
    if (content === `${pattern}:*`) return true  // 前缀语法
    if (content === `${pattern}*`) return true   // 通配符
    if (content === `${pattern} *`) return true   // 带空格
  }
  return false
}

3.3 只读命令白名单

源码位置src/utils/shell/readOnlyCommandValidation.ts

// Git 只读命令
export const GIT_READ_ONLY_COMMANDS = {
  'git diff': { safeFlags: { '--stat': 'none', '-p': 'none', ... } },
  'git log': { safeFlags: { '--oneline': 'none', '--graph': 'none', ... } },
  'git show': { safeFlags: {...} },
  'git status': { safeFlags: {...} },
  'git grep': { safeFlags: {...} },
  // ...
}

// GitHub CLI 只读命令
export const GH_READ_ONLY_COMMANDS = {
  'gh pr view': { safeFlags: {...}, additionalCommandIsDangerousCallback: ghIsDangerousCallback },
  'gh pr list': { safeFlags: {...} },
  'gh issue view': { safeFlags: {...} },
  // ...
}

3.4 危险模式列表

源码位置src/utils/permissions/dangerousPatterns.ts (L44-79)

// 跨平台代码执行入口
const CROSS_PLATFORM_CODE_EXEC = [
  'python', 'python3', 'node', 'deno', 'ruby', 'perl', 'php', 'lua',
  'npx', 'bunx', 'npm run', 'yarn run', 'pnpm run', 'bun run',
  'bash', 'sh', 'ssh',
]

// Bash 危险模式 (ANT 用户额外增加)
const DANGEROUS_BASH_PATTERNS = [
  ...CROSS_PLATFORM_CODE_EXEC,
  'zsh', 'fish', 'eval', 'exec', 'env', 'xargs', 'sudo',
  // ANT-only: 内部工具 + 常见过度允许的前缀
  ...(process.env.USER_TYPE === 'ant' ? [
    'fa run', 'coo', 'gh', 'gh api', 'curl', 'wget',
    'git', 'kubectl', 'aws', 'gcloud', 'gsutil',
  ] : []),
]

4. Dangerous 工具识别

4.1 工具级危险识别

源码位置src/utils/permissions/permissionSetup.ts (L157-233)

// PowerShell 危险权限检查
export function isDangerousPowerShellPermission(
  toolName: string,
  ruleContent: string | undefined,
): boolean {
  if (toolName !== 'PowerShell') return false

  // 工具级允许 = 危险
  if (!ruleContent) return true

  const dangerousPatterns = [
    ...CROSS_PLATFORM_CODE_EXEC,
    'pwsh', 'powershell', 'cmd', 'wsl',
    // 代码执行
    'iex', 'invoke-expression', 'icm', 'invoke-command',
    // 进程生成
    'start-process', 'saps', 'start', 'start-job',
    // 事件/会话代码执行
    'register-objectevent', 'register-engineevent',
    // .NET 逃逸
    'add-type', 'new-object',
  ]
  // ... 匹配逻辑
}

// Agent (子代理) 权限危险检查
export function isDangerousTaskPermission(
  toolName: string,
  ruleContent: string | undefined,
): boolean {
  return toolName === 'Agent'  // 任何 Agent 允许规则都危险
}

4.2 Bash 安全检查

源码位置src/tools/BashTool/bashSecurity.ts

该文件包含 23 种安全检查,用于识别潜在危险命令:

const BASH_SECURITY_CHECK_IDS = {
  INCOMPLETE_COMMANDS: 1,      // 不完整命令
  JQ_SYSTEM_FUNCTION: 2,       // jq system() 函数
  JQ_FILE_ARGUMENTS: 3,        // jq 危险标志
  OBFUSCATED_FLAGS: 4,         // 混淆标志
  SHELL_METACHARACTERS: 5,     // Shell 元字符
  DANGEROUS_VARIABLES: 6,      // 危险变量
  NEWLINES: 7,                 // 换行符分隔多命令
  COMMAND_SUBSTITUTION: 8,      // 命令替换
  INPUT_REDIRECTION: 9,        // 输入重定向
  OUTPUT_REDIRECTION: 10,      // 输出重定向
  IFS_INJECTION: 11,           // IFS 注入
  GIT_COMMIT_SUBSTITUTION: 12, // Git 提交替换
  PROC_ENVIRON_ACCESS: 13,     // /proc 环境访问
  MALFORMED_TOKEN_INJECTION: 14, // 畸形标记注入
  BACKSLASH_ESCAPED_WHITESPACE: 15, // 反斜杠逃逸
  BRACE_EXPANSION: 16,         // 大括号扩展
  CONTROL_CHARACTERS: 17,      // 控制字符
  UNICODE_WHITESPACE: 18,      // Unicode 空白
  MID_WORD_HASH: 19,           // 词中哈希
  ZSH_DANGEROUS_COMMANDS: 20,  // Zsh 危险命令
  BACKSLASH_ESCAPED_OPERATORS: 21, // 反斜杠转义操作符
  COMMENT_QUOTE_DESYNC: 22,    // 注释引号去同步
  QUOTED_NEWLINE: 23,          // 引号内换行
}

4.3 Zsh 危险命令集合

源码位置src/tools/BashTool/bashSecurity.ts (L45-74)

const ZSH_DANGEROUS_COMMANDS = new Set([
  // zmodload 是许多危险模块的入口
  'zmodload',
  // emulate with -c 是 eval 等价物
  'emulate',
  // Zsh 模块内置命令
  'sysopen', 'sysread', 'syswrite', 'sysseek',
  'zpty',   // 伪终端命令执行
  'ztcp',   // TCP 连接 (数据泄露)
  'zsocket', // Unix/TCP 套接字
  'mapfile', // 关联数组
  'zf_rm', 'zf_mv', 'zf_ln', 'zf_chmod', 'zf_chown',
  'zf_mkdir', 'zf_rmdir', 'zf_chgrp',
])

4.4 危险模式检测回调

Git reflog 写操作检测src/utils/shell/readOnlyCommandValidation.ts (L278-304)

'git reflog': {
  safeFlags: {...},
  additionalCommandIsDangerousCallback: (_rawCommand, args) => {
    const DANGEROUS_SUBCOMMANDS = new Set(['expire', 'delete', 'exists'])
    for (const token of args) {
      if (!token || token.startsWith('-')) continue
      if (DANGEROUS_SUBCOMMANDS.has(token)) return true
      return false  // 第一个安全位置后即安全
    }
    return false
  },
}

5. 用户确认流程

5.1 权限对话框处理

源码位置src/components/permissions/FilePermissionDialog/useFilePermissionDialog.ts

export function useFilePermissionDialog<T extends ToolInput>({
  filePath,
  completionType,
  languageName,
  toolUseConfirm,  // 确认请求
  onDone,          // 完成回调
  onReject,        // 拒绝回调
  parseInput,
  operationType = 'write',
}) {
  const toolPermissionContext = useAppState(s => s.toolPermissionContext)
  const [acceptFeedback, setAcceptFeedback] = useState('')
  const [rejectFeedback, setRejectFeedback] = useState('')
  const [focusedOption, setFocusedOption] = useState('yes')

  // 根据上下文生成选项
  const options = useMemo(() =>
    getFilePermissionOptions({
      filePath,
      toolPermissionContext,
      operationType,
      // ...
    }), [filePath, toolPermissionContext])

  // 处理选项选择
  const onChange = useCallback((option, input, feedback?) => {
    const handler = PERMISSION_HANDLERS[option.type]
    handler(params, {
      feedback,
      scope: option.type === 'accept-session' ? option.scope : undefined,
    })
  }, [])
}

5.2 权限选项类型

可能的权限决策

选项类型行为说明
accept-once当次允许仅本次允许
accept-session会话允许本次会话允许
accept-always永久允许写入设置
deny-once当次拒绝仅本次拒绝
deny-session会话拒绝本次会话拒绝
deny-always永久拒绝写入设置

5.3 权限确认消息创建

源码位置src/utils/permissions/permissions.ts (L137-211)

export function createPermissionRequestMessage(
  toolName: string,
  decisionReason?: PermissionDecisionReason,
): string {
  if (decisionReason) {
    switch (decisionReason.type) {
      case 'classifier':
        return `Classifier '${decisionReason.classifier}' requires approval: ${decisionReason.reason}`
      case 'hook':
        return `Hook '${decisionReason.hookName}' blocked: ${decisionReason.reason}`
      case 'rule':
        return `Permission rule '${ruleString}' requires approval`
      case 'sandboxOverride':
        return 'Run outside of the sandbox'
      case 'safetyCheck':
        return decisionReason.reason
      case 'mode':
        return `Current mode (${modeTitle}) requires approval`
    }
  }
  return `Claude requested permissions to use ${toolName}`
}

5.4 Auto Mode 分类器流程

源码位置src/utils/permissions/permissions.ts (L688-926)

// Auto 模式使用 AI 分类器判断
if (appState.toolPermissionContext.mode === 'auto') {
  // 1. AcceptEdits 快速通道
  const acceptEditsResult = await tool.checkPermissions(input, {
    ...context,
    mode: 'acceptEdits',
  })
  if (acceptEditsResult.behavior === 'allow') {
    return { behavior: 'allow', decisionReason: { type: 'mode', mode: 'auto' } }
  }

  // 2. 安全工具白名单跳过分类器
  if (isAutoModeAllowlistedTool(tool.name)) {
    return { behavior: 'allow' }
  }

  // 3. 运行分类器
  const classifierResult = await classifyYoloAction(
    context.messages,
    action,
    context.options.tools,
    appState.toolPermissionContext,
    context.abortController.signal,
  )

  // 4. 分类器拒绝
  if (classifierResult.shouldBlock) {
    // 更新拒绝追踪
    const newDenialState = recordDenial(denialState)

    // 超过限制则回退到询问
    if (shouldFallbackToPrompting(denialState)) {
      return { behavior: 'ask', ... }
    }
    return { behavior: 'deny', message: buildYoloRejectionMessage(...) }
  }

  // 5. 分类器允许
  return { behavior: 'allow', decisionReason: { type: 'classifier', ... } }
}

5.5 拒绝追踪

源码位置src/utils/permissions/denialTracking.ts

const DENIAL_LIMITS = {
  maxConsecutive: 3,  // 连续 3 次拒绝后回退到询问
  maxTotal: 10,       // 总计 10 次拒绝后回退到询问
}

export function shouldFallbackToPrompting(state: DenialTrackingState): boolean {
  return state.consecutiveDenials >= DENIAL_LIMITS.maxConsecutive ||
         state.totalDenials >= DENIAL_LIMITS.maxTotal
}

6. 安全边界设计

6.1 安全检查的 Bypass 免疫

关键设计原则:某些安全检查在 bypassPermissions 模式下仍然触发确认对话框

源码位置src/utils/permissions/permissions.ts (L1252-1260)

// 1g. 安全检查 (.git/, .claude/, .vscode/, shell configs)
// bypass-immune — 即使在 bypassPermissions 模式也必须询问
if (
  toolPermissionResult?.behavior === 'ask' &&
  toolPermissionResult.decisionReason?.type === 'safetyCheck'
) {
  return toolPermissionResult
}

安全检查路径src/utils/permissions/pathValidation.ts

// 检查路径安全性
export function checkPathSafetyForAutoEdit(path: string): PermissionResult {
  // .git/ 目录 - 始终询问
  if (path.includes('.git/')) {
    return { behavior: 'ask', decisionReason: { type: 'safetyCheck', ... } }
  }

  // .claude/ 配置目录 - 始终询问
  if (path.includes('.claude/')) {
    return { behavior: 'ask', decisionReason: { type: 'safetyCheck', ... } }
  }

  // Shell 配置文件 - 始终询问
  if (isShellConfigFile(path)) {
    return { behavior: 'ask', decisionReason: { type: 'safetyCheck', ... } }
  }
}

6.2 内容级规则优先于模式

设计原则:用户显式配置的内容级询问规则优先于 bypassPermissions 模式

源码位置src/utils/permissions/permissions.ts (L1238-1250)

// 1f. 内容级询问规则优先于 bypassPermissions
// 当用户显式配置如 Bash(npm publish:*) 的询问规则时
// 即使在 bypassPermissions 模式也必须尊重
if (
  toolPermissionResult?.behavior === 'ask' &&
  toolPermissionResult.decisionReason?.type === 'rule' &&
  toolPermissionResult.decisionReason.rule.ruleBehavior === 'ask'
) {
  return toolPermissionResult
}

6.3 环境变量剥离

防御机制:防止通过环境变量绕过拒绝规则

源码位置src/utils/bash/commands.ts

// 拒绝规则剥离所有前导环境变量
export function stripAllLeadingEnvVars(command: string): string {
  // 剥离 FOO=bar BAZ=qux from 'FOO=bar rm -rf /'
  // 防止通过环境变量绕过 deny 规则
}

// 允许规则保留环境变量以便合法使用
// 例如: FOO=bar ls 是合法的 ls 命令

6.4 复合命令检测

防御机制:防止前缀规则被复合命令绕过

// 示例: 规则 Bash(cd:*) 不应匹配 "cd /path && rm -rf"
// 因为复合命令的第二部分是危险的
export function matchesCompoundCommand(command: string, prefix: string): boolean {
  // 检测 &&, ||, ;, | 等操作符
  // 如果规则前缀后面跟着操作符,则不匹配
}

6.5 沙箱集成

源码位置src/tools/BashTool/shouldUseSandbox.ts

export function shouldUseSandbox(input: BashInput): boolean {
  // 检查是否应该使用沙箱执行

  // 某些命令类型默认使用沙箱
  if (isReadOnlyCommand(input.command)) return true

  // 危险命令可能使用沙箱限制
  if (isPotentiallyDangerous(input.command)) {
    return SandboxManager.isSandboxingEnabled()
  }

  return false
}

6.6 Statsig 特性门控

动态禁用机制:通过 Statsig 远程禁用 bypassPermissions 模式

源码位置src/utils/permissions/permissionSetup.ts (L689-811)

export function initialPermissionModeFromCLI({
  permissionModeCli,
  dangerouslySkipPermissions,
}): { mode: PermissionMode; notification?: string } {
  // 检查 GrowthBook Statsig gate
  const growthBookDisableBypassPermissionsMode =
    checkStatsigFeatureGate_CACHED_MAY_BE_STALE(
      'tengu_disable_bypass_permissions_mode',
    )

  // 检查设置
  const settingsDisableBypassPermissionsMode =
    settings.permissions?.disableBypassPermissionsMode === 'disable'

  // Statsig 优先于设置
  const disableBypassPermissionsMode =
    growthBookDisableBypassPermissionsMode || settingsDisableBypassPermissionsMode

  // 如果禁用,跳过 bypassPermissions 模式
  if (mode === 'bypassPermissions' && disableBypassPermissionsMode) {
    return { mode: 'default', notification: '...' }
  }
}

7. oho CLI 权限实现

oho 是 Claude Code 的 CLI 客户端,权限安全决策由服务器处理,CLI 主要提供配置和交互接口。

7.1 AutoApprove 配置

源码位置oho/internal/types/types.go (L104)

// Config 配置类型
type Config struct {
  Providers    map[string]interface{} `json:"providers"`
  DefaultModel string                 `json:"defaultModel"`
  Theme        string                 `json:"theme"`
  Language     string                 `json:"language"`
  AutoApprove  []string               `json:"autoApprove"`  // 自动批准的工具列表
  MaxTokens    int                    `json:"maxTokens"`
  Temperature  float64                `json:"temperature"`
}

配置命令oho/cmd/configcmd/config.go (L222)

setCmd.Flags().StringSliceVar(&autoApprove, "auto-approve", nil,
  "自动批准的工具列表(注意:可能不被服务器支持)")

使用示例

# 设置自动批准的工具列表
oho config set --auto-approve read,ls,cat,grep

7.2 权限响应命令

源码位置oho/cmd/session/session.go (L811-860)

// permissionsCmd 响应权限请求
var permissionsCmd = &cobra.Command{
  Use:   "permissions [id] [permissionID]",
  Short: "响应权限请求",
  Args:  cobra.MaximumNArgs(2),
  RunE: func(cmd *cobra.Command, args []string) error {
    // ...
    req := map[string]interface{}{
      "response": permissionResp,  // "allow" 或 "deny"
      "remember": rememberPerm,    // 是否记住决策
    }
    resp, err := c.Post(ctx,
      fmt.Sprintf("/session/%s/permissions/%s", id, permID), req)
    // ...
  },
}

API 映射oho/API_MAPPING.md (L61)

命令API 端点方法描述
oho session permissions <id> <permId>/session/:id/permissions/:permissionIDPOST响应权限请求

7.3 交互式确认

源码位置oho/internal/util/output.go (L92-102)

// Confirm 确认操作
func Confirm(prompt string) bool {
  if config.Get().JSON {
    return true  // JSON 模式下自动批准
  }

  fmt.Printf("%s [y/N]: ", prompt)
  var response string
  _, _ = fmt.Scanln(&response)
  return strings.ToLower(response) == "y" || strings.ToLower(response) == "yes"
}

7.4 oho 权限安全特点

特性说明
服务器决策权限判断逻辑在服务器端,CLI 仅传递配置和响应
AutoApprove透传工具列表给服务器,由服务器决定是否自动批准
permissions 命令允许用户响应服务器发出的权限请求
Confirm 函数CLI 层面的交互式确认,用于需要本地确认的场景
JSON 模式--json 模式下自动批准所有操作(无交互)

8. 核心文件索引

8.1 Claude Code 主源码

类型定义

文件说明
src/types/permissions.ts核心权限类型定义
src/utils/permissions/PermissionRule.ts权限规则类型
src/utils/permissions/PermissionMode.ts模式枚举与配置
src/utils/permissions/PermissionResult.ts决策结果类型

核心实现

文件说明
src/utils/permissions/permissions.ts主权限检查管道
src/utils/permissions/permissionSetup.ts权限上下文初始化与模式切换
src/utils/permissions/dangerousPatterns.ts危险模式列表
src/utils/permissions/denialTracking.ts拒绝追踪状态
src/utils/permissions/yoloClassifier.tsAuto 模式 AI 分类器

工具实现

文件说明
src/Tool.ts工具接口与 checkPermissions 定义
src/tools/BashTool/bashSecurity.tsBash 安全检查 (23 种)
src/tools/BashTool/bashPermissions.tsBash 权限规则匹配
src/utils/shell/readOnlyCommandValidation.ts只读命令白名单

UI/UX

文件说明
src/components/permissions/FilePermissionDialog/useFilePermissionDialog.ts文件权限对话框
src/components/permissions/hooks.ts权限相关 React hooks
src/hooks/useCanUseTool.tsx权限检查 React hook

集成点

文件说明
src/services/tools/toolExecution.ts工具执行与权限集成
src/hooks/toolPermission/handlers/interactiveHandler.ts交互式权限处理
src/hooks/toolPermission/handlers/coordinatorHandler.ts协调器处理
src/hooks/toolPermission/handlers/swarmWorkerHandler.ts后台代理处理

8.2 oho CLI

文件说明
oho/internal/types/types.go配置类型定义(含 AutoApprove)
oho/cmd/configcmd/config.go配置命令(含 –auto-approve)
oho/cmd/session/session.go会话命令(含 permissions 子命令)
oho/internal/util/output.go输出工具(含 Confirm 函数)
oho/API_MAPPING.mdAPI 端点映射

8.3 OpenClaw 网关

文档说明
docs/zh-CN/gateway/security/index.md安全架构与威胁模型
docs/zh-CN/gateway/sandboxing.md沙箱隔离机制
docs/zh-CN/tools/exec-approvals.md执行审批系统

附录:权限决策类型

// 允许决策
type PermissionAllowDecision<Input> = {
  behavior: 'allow'
  updatedInput?: Input
  userModified?: boolean
  decisionReason?: PermissionDecisionReason
  toolUseID?: string
  acceptFeedback?: string
}

// 询问决策
type PermissionAskDecision<Input> = {
  behavior: 'ask'
  message: string
  updatedInput?: Input
  decisionReason?: PermissionDecisionReason
  suggestions?: PermissionUpdate[]
  blockedPath?: string
  metadata?: PermissionMetadata
  pendingClassifierCheck?: PendingClassifierCheck  // Auto 模式异步检查
}

// 拒绝决策
type PermissionDenyDecision = {
  behavior: 'deny'
  message: string
  decisionReason: PermissionDecisionReason
  toolUseID?: string
}

// 决策原因
type PermissionDecisionReason =
  | { type: 'rule', rule: PermissionRule }           // 规则匹配
  | { type: 'mode', mode: PermissionMode }           // 模式决定
  | { type: 'subcommandResults', reasons: Map }       // 子命令结果
  | { type: 'permissionPromptTool', ... }             // 权限提示工具
  | { type: 'hook', hookName: string }                 // Hook 拦截
  | { type: 'asyncAgent', reason: string }             // 异步代理
  | { type: 'sandboxOverride', reason: string }        // 沙箱覆盖
  | { type: 'classifier', classifier: string }         // AI 分类器
  | { type: 'workingDir', reason: string }              // 工作目录
  | { type: 'safetyCheck', reason: string, classifierApprovable: boolean }  // 安全检查
  | { type: 'other', reason: string }                  // 其他

参考架构图

┌─────────────────────────────────────────────────────────────────┐
│                     Claude Code 权限系统                          │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌─────────────┐      ┌─────────────┐      ┌─────────────┐       │
│  │   Claude    │      │  oho CLI    │      │  OpenClaw  │       │
│  │   Code      │      │  (客户端)   │      │  Gateway   │       │
│  │  (主源码)   │      │             │      │  (服务端)   │       │
│  └──────┬──────┘      └──────┬──────┘      └──────┬──────┘       │
│         │                    │                    │              │
│         │                    │                    │              │
│         ▼                    ▼                    ▼              │
│  ┌─────────────────────────────────────────────────────────┐     │
│  │                    权限检查管道                           │     │
│  │  ┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐     │     │
│  │  │ Rule    │→ │ Mode    │→ │ Auto    │→ │ Fallback│     │     │
│  │  │ Based   │  │ Based   │  │ Classifier│ │ Ask    │     │     │
│  │  └─────────┘  └─────────┘  └─────────┘  └─────────┘     │     │
│  └─────────────────────────────────────────────────────────┘     │
│         │                                                    │
│         ▼                                                    │
│  ┌─────────────────────────────────────────────────────────┐     │
│  │                    安全边界                              │     │
│  │  • Bypass 免疫 (safetyCheck)                           │     │
│  │  • 内容级规则优先                                        │     │
│  │  • 环境变量剥离                                          │     │
│  │  • 复合命令检测                                          │     │
│  │  • 沙箱集成                                              │     │
│  └─────────────────────────────────────────────────────────┘     │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

文档生成时间:2026-04-03 分析深度:P6 (架构级理解,可完整复现实现)