9.5 各工具的权限策略

模型: claude-opus-4-6 (anthropic/claude-opus-4-6) 生成日期: 2026-02-17


前面四节我们从模型、引擎、配置和交互流程四个维度解析了权限系统的架构。本节将回到具体的工具层面,看看不同工具是如何利用权限系统来保护用户安全的。

9.5.1 Bash 工具:命令级权限

Bash 是 OpenCode 中权限需求最复杂的工具——它可以执行任意 Shell 命令,风险最高。

// tool/bash.ts
permission: "bash",  // 权限类型名称

Bash 工具在请求权限时,会将 BashArity 处理后的命令前缀作为 pattern。例如:

  • 执行 npm install lodash → 权限模式为 "npm install"

  • 执行 rm -rf /tmp/test → 权限模式为 "rm"

此外,如果 Bash 命令涉及项目目录之外的路径,还会触发 external_directory 权限:

// tool/bash.ts
permission: "external_directory",
// pattern 为命令中涉及的外部目录路径

build Agent 的默认权限对 Bash 的配置是 "*": "allow"(允许所有),但对 external_directory 则做了精细的区分:

  • ~/.sshdeny(永远禁止——防止 Agent 接触 SSH 密钥)

  • ~/.gnupgdeny(永远禁止——防止 Agent 接触 GPG 密钥)

  • 其他外部目录 → ask(询问用户)

这种分层策略既保证了日常开发中的流畅体验(项目内命令直接执行),又对高危操作提供了严格的保护。

循环检测(Doom Loop)

Bash 工具还参与了一个特殊的权限检查——循环检测。当系统检测到 Agent 连续多次执行相同的工具调用(相同的工具、相同的参数),会触发 doom_loop 权限请求:

doom_loop 权限的默认配置是 ask——系统会暂停并询问用户:"Agent 似乎陷入了循环,是否继续?"。这防止了 Agent 在遇到错误时无限重试同一个失败操作,消耗不必要的 LLM 调用和时间。

9.5.2 Edit/Write 工具:文件路径级权限

文件编辑类工具(Edit、Write、Patch/apply_patch、MultiEdit)共享同一个权限名称 edit

使用相同的权限名称意味着用户可以通过一条规则同时控制所有文件编辑行为。例如:

这条配置禁止 Agent 修改 lock 文件和 node_modules 目录(这些文件通常由包管理器自动管理),但允许编辑其他文件。

权限请求时的 pattern 是目标文件的路径,使得用户可以按文件路径精确控制 Agent 的编辑范围。

9.5.3 Read 工具:.env 文件的特殊保护

Read 工具的权限名称是 read

build Agent 对 Read 工具的默认权限做了专门的 .env 文件保护:

为什么要特别保护 .env 文件?因为它是存储 API 密钥、数据库密码等敏感信息的标准位置。如果 Agent 读取了 .env 文件的内容,这些信息可能会出现在 LLM 的上下文中,理论上存在泄露风险(尽管 LLM Provider 通常不会存储用户的对话内容,但这仍然是一个最佳实践层面的安全防护)。

通配符模式 "*.env*" 不仅匹配 .env 文件,还匹配 .env.local.env.productiondatabase.env 等各种变体,覆盖了常见的环境变量文件命名约定。

9.5.4 Task 工具:Sub-agent 调度权限

Task 工具用于调度子 Agent(Sub-agent),它需要 task 权限:

Task 权限的特殊之处在于,权限评估会考虑目标 Agent 的名称。例如,规则可以配置为:

这允许 Agent 调度 explore 类型的子 Agent(低风险的只读搜索),但禁止调度 build 类型的子 Agent(可以执行文件修改等高风险操作)。

session/prompt.ts 中,Task 权限的评估使用了子 Agent 的名称作为 pattern:

9.5.5 外部目录访问控制(external_directory)

external_directory 是一个跨工具的权限类型——它不属于某个特定的工具,而是由任何涉及项目目录之外文件路径的操作触发。

OpenCode 的默认行为是将 Agent 的操作限制在当前项目目录内。当 Agent 尝试访问项目目录之外的路径时(例如读取 /etc/hosts 或修改 ~/other-project/config.json),external_directory 权限被触发。

默认配置中,除了 ~/.ssh~/.gnupg 被硬编码为 deny 之外,其他外部目录访问都是 ask

这种设计在安全性和实用性之间取得了平衡——Agent 偶尔确实需要访问项目外的文件(如读取全局配置文件、安装全局工具等),但这些操作不应该在用户不知情的情况下发生。

工具权限的禁用检测

权限系统还有一个"预检"功能——在向 LLM 发送工具列表时,提前过滤掉被全局 deny 的工具:

这个函数在 llm.ts 中被调用:

如果一个工具的通配规则 "*"deny,说明它被完全禁用了——不仅不应该执行,甚至不应该出现在 LLM 的工具列表中。这避免了 LLM 尝试调用一个必定会被拒绝的工具,浪费宝贵的推理时间。


第 9 章总结

OpenCode 的权限控制系统是其安全架构的核心支柱。它基于简洁但强大的三值模型(allow/deny/ask)、通配符模式匹配、以及"后来者优先"的规则合并策略,构建了一个灵活而健壮的权限框架。

关键设计亮点:

  1. 安全默认:无规则匹配时默认 ask,确保用户始终有知情权。

  2. 层级覆盖:Agent 默认 → 用户配置 → Session 运行时,通过数组拼接自然实现优先级。

  3. 批量审批always 回复会自动审批同类等待请求,减少用户的操作负担。

  4. 粒度控制:BashArity 字典确保权限请求以"人类可理解"的粒度展示。

  5. 敏感保护.env 文件、SSH 密钥、GPG 密钥等敏感资源有专门的保护策略。

  6. 预检过滤:被全局 deny 的工具直接从 LLM 的工具列表中移除,避免无效调用。

  7. 循环检测doom_loop 权限防止 Agent 陷入无意义的重复操作。

从工程哲学角度看,OpenCode 的权限系统体现了"渐进信任"的理念——初始状态下谨慎保守(ask),用户可以通过 always 逐步扩大 Agent 的自主范围,而敏感操作始终有硬编码的保护底线(deny)。

Last updated