9.2 权限评估引擎

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


上一节我们了解了权限模型的数据结构——Rule 和 Ruleset。本节将深入权限评估引擎的核心逻辑:当 Agent 尝试执行一个需要权限的操作时,系统如何从 Ruleset 中找到匹配的规则并做出决策。

9.2.1 PermissionNext.evaluate() 的实现

evaluate() 是权限系统的核心函数,负责为一个具体的权限请求找到最匹配的规则:

export function evaluate(
  permission: string,    // 权限类型(如 "bash")
  pattern: string,       // 匹配模式(如 "ls -la")
  ...rulesets: Ruleset[]  // 一个或多个规则集
): Rule {
  const merged = merge(...rulesets)  // 合并所有规则集
  const match = merged.findLast(
    (rule) =>
      Wildcard.match(permission, rule.permission) &&
      Wildcard.match(pattern, rule.pattern),
  )
  return match ?? { action: "ask", permission, pattern: "*" }
}

这个函数的逻辑可以分解为三步:

第一步:合并规则集

第二步:从后往前查找匹配

findLast() 是关键——它从数组的末尾开始查找第一个同时满足以下条件的规则:

  1. 规则的 permission 字段通配符匹配请求的权限类型

  2. 规则的 pattern 字段通配符匹配请求的模式

衍生解释:Array.prototype.findLast()

findLast() 是 ES2023 标准引入的数组方法,它与 find() 的功能相同但搜索方向相反——从数组末尾开始向前搜索。在 OpenCode 的权限系统中,这意味着后加入的规则优先级更高,自然实现了"后来者优先"的覆盖语义。

第三步:默认回退

如果没有任何规则匹配,默认返回 ask —— 这意味着在没有明确配置的情况下,所有敏感操作都会询问用户。这是"安全优先"设计哲学的体现。

评估示例

假设有以下合并后的 Ruleset:

评估结果:

请求
匹配规则
结果

evaluate("bash", "ls -la")

bash / ls *

allow

evaluate("bash", "rm -rf /")

bash / rm *

deny

evaluate("bash", "curl https://...")

* / *

allow(匹配第一条通配规则)

evaluate("read", ".env")

read / *.env

deny

evaluate("doom_loop", "bash")

doom_loop / *

ask

evaluate("unknown", "anything")

* / *

allow

9.2.2 Wildcard 模式匹配(util/wildcard.ts)

权限评估的核心依赖于 Wildcard(通配符)模式匹配。OpenCode 在 util/wildcard.ts 中实现了一个简洁但功能完备的通配符匹配器:

这个实现的工作原理是将通配符模式转换为正则表达式:

  1. 转义特殊字符:将 .+^ 等正则元字符转义,防止被当作正则语法。

  2. 通配符转换

    • *.*(匹配零个或多个任意字符)

    • ?.(匹配恰好一个任意字符)

  3. 尾部空格 + 通配符优化:如果模式以 " *" 结尾(如 "ls *"),将其转换为 "( .*)?" 使其可选——这意味着 "ls *" 既能匹配 "ls" 也能匹配 "ls -la"

匹配示例:

模式
输入
结果

*

任意字符串

✅ 匹配

bash

"bash"

✅ 匹配

bash

"read"

❌ 不匹配

*.env

".env"

✅ 匹配

*.env

"production.env"

✅ 匹配

ls *

"ls"

✅ 匹配(尾部通配可选)

ls *

"ls -la"

✅ 匹配

rm *

"rm -rf /"

✅ 匹配

src/*

"src/index.ts"

✅ 匹配

结构化通配符匹配

Wildcard 还提供了两个高级匹配函数:

all() 按模式长度排序后依次匹配,最长的匹配模式胜出。这实现了"最具体的规则优先"的语义——例如,"git commit""git *" 更具体。

allStructured() 用于命令权限的精细匹配——它将命令分为"动词"(如 git)和"参数"(如 checkout main),分别进行通配符匹配。

9.2.3 ~$HOME 路径展开

在权限规则的 pattern 字段中,用户可以使用 ~$HOME 来引用 home 目录。expand() 函数在规则解析时将这些符号展开为实际路径:

展开示例(假设 home 目录为 /home/user):

输入模式
展开结果

~/Documents/*

/home/user/Documents/*

~

/home/user

$HOME/.ssh/*

/home/user/.ssh/*

$HOME

/home/user

/absolute/path/*

/absolute/path/*(不变)

这个展开发生在 fromConfig() 中,也就是 Ruleset 构建阶段,而不是评估阶段。这意味着所有模式在首次加载时就被标准化了,避免了每次评估都进行路径展开的性能开销。


本节小结

OpenCode 的权限评估引擎通过 evaluate() 函数实现,其核心是 findLast() 从后往前搜索的策略——后加入的规则自动获得更高优先级,没有匹配时默认为 ask。通配符匹配由 Wildcard.match() 实现,通过将 *? 转换为正则表达式来进行模式匹配,并对尾部的 " *" 做了特殊的可选化处理。路径模式支持 ~$HOME 的展开,在 Ruleset 构建阶段一次性完成标准化。

Last updated