首页
/ openinterpreter 的 execpolicy 详解:用 Starlark 编写 prefix_rule 命令策略、host_executable 匹配语义与 codex execpolicy check CLI

openinterpreter 的 execpolicy 详解:用 Starlark 编写 prefix_rule 命令策略、host_executable 匹配语义与 codex execpolicy check CLI

2026-09-06 16:30:49作者:秋阔奎Evelyn

execpolicy 是 openinterpreter(Codex 系 CLI)仓库中的一个策略引擎与命令行工具,用于在 Agent 执行 shell 命令之前,基于可版本化的策略文件决定该命令应当放行(allow)、请求用户审批(prompt)还是直接禁止(forbidden)。本篇基于仓库内 codex-rs/execpolicy/README.md 完整覆盖其策略语法、匹配语义与 CLI 用法,并结合 src/parser.rssrc/policy.rssrc/rule.rs 等源码,补充解析与匹配阶段的实现细节,帮助读者既会写策略文件,也理解判定结果的产生机制。

一、execpolicy 是什么:prefix-rule 策略引擎概览

根据 README 的 Overview,execpolicy 围绕两个 Starlark 内置函数构建:

  • prefix_rule(pattern=[...], decision?, justification?, match?, not_match?):按命令 token 前缀匹配规则;
  • host_executable(name=..., paths=[...]):主机可执行文件元数据,用于约束哪些绝对路径可以走 basename 回退匹配。

需要明确的边界:当前版本覆盖的是 execpolicy 语言的 prefix-rule 子集加主机可执行文件元数据,README 明确说明更丰富的语言(a richer language)会后续跟进。策略文件本身是 Starlark 语法,由 parser.rs 中的 PolicyParser 基于 starlark crate 解析——parse 方法使用 Dialect::Extended(并开启 f-string)构建 AST,再用 GlobalsBuilder 注入 policy_builtins 后求值,求值过程中通过 Evaluator.extra 持有 PolicyBuilder 累积规则。

几个核心匹配约定:

  • token 按顺序匹配pattern 中的任意元素可以是列表,表示“多选一”的备选 token;
  • decision 默认为 allow,合法取值为 allowpromptforbidden
  • justification 是可选的人类可读理由,可为任意 decision 提供,并在不同上下文(审批提示、拒绝消息)中被展示。README 特别建议:当 decision = "forbidden" 时,若合适应在 justification 中给出推荐替代方案,例如 "Use `jj` instead of `git`."
  • match / not_match 提供加载期校验的调用示例(可视为单元测试):示例可以是 token 数组,也可以是字符串(字符串会用 shlex 分词);
  • CLI 始终打印评估结果的 JSON 序列化;
  • 旧版规则匹配器位于 codex-execpolicy-legacy 独立 crate 中,与新的 prefix-rule 引擎并存。

从源码结构看,Decisionsrc/decision.rs 中定义为 Allow / Prompt / Forbidden 三值枚举,且派生了 Ord:源码注释指出 Prompt 表示“请求显式用户审批;当以 approval_policy="never" 运行时会被直接拒绝”,Forbidden 表示“不再经过任何考虑直接阻断”。这个严重程度排序正是后文“取最严格判定”的基础。

二、prefix_rule 语法:参数、默认值与示例校验

README 给出的完整策略形状如下:

prefix_rule(
    pattern = ["cmd", ["alt1", "alt2"]], # ordered tokens; list entries denote alternatives
    decision = "prompt",                 # allow | prompt | forbidden; defaults to allow
    justification = "explain why this rule exists",
    match = [["cmd", "alt1"], "cmd alt2"],           # examples that must match this rule
    not_match = [["cmd", "oops"], "cmd alt3"],       # examples that must not match this rule
)

各参数在实现层的约束(均可在 src/parser.rsprefix_rule builtin 中找到对应校验逻辑):

参数 必填 类型 说明与校验
pattern token 列表 有序 token;元素可为字符串或字符串列表(备选)。解析函数 parse_pattern 要求列表非空;parse_pattern_token 要求备选列表非空且元素均为字符串,否则报 InvalidPattern。含多个备选的首 token 会在解析期展开为多条 PrefixRule(见下文源码分析)
decision allow / prompt / forbidden 缺省为 allow;取值经 Decision::parse 校验,非法值报 InvalidDecision
justification 字符串 不能为纯空白,否则报 justification cannot be empty
match 示例列表 每个示例为 token 数组或字符串;字符串示例用 shlex::split 分词,非法 shell 语法会报 InvalidExample。加载期必须命中本规则,否则报 ExampleDidNotMatch
not_match 示例列表 同上格式;加载期不得命中任何规则,否则报 ExampleDidMatch

仓库自带一份语法示例策略 examples/example.codexpolicy(其头部注释说明“仅用于演示语法,不推荐实际使用”),展示了三类典型用法:

prefix_rule(
    pattern = ["git", "reset", "--hard"],
    decision = "forbidden",
    justification = "destructive operation",
    match = [
        ["git", "reset", "--hard"],
    ],
    not_match = [
        ["git", "reset", "--keep"],
        "git reset --merge",
    ],
)

prefix_rule(
    pattern = ["ls"],
    match = [
        ["ls"],
        ["ls", "-l"],
        ["ls", "-a", "."],
    ],
)

prefix_rule(
    pattern = ["cp"],
    decision = "prompt",
    match = [
        ["cp", "foo", "bar"],
        "cp -r src dest",
    ],
)

其中 ls 规则省略 decision,即演示了默认 allow 的行为;git reset --hard 规则则演示了 forbidden + justification + not_match 的完整组合。

加载期示例校验:把 match/not_match 当单元测试

match / not_match 的校验发生在策略加载阶段,这是 README 中 “validated at load time” 的实现。从源码结构看,prefix_rule builtin 不会立即校验示例,而是把 (rules, matches, not_matches, location) 记录为 pending_example_validationPolicyParser::parse 在模块求值完成后调用 validate_pending_examples_from,用这些规则构造临时 Policy,分别执行:

  • validate_not_match_examplessrc/rule.rs):任一负例被任一规则命中即报 ExampleDidMatch
  • validate_match_examplessrc/rule.rs):任一正例未命中任何规则即报 ExampleDidNotMatch

校验错误会附加 Starlark 调用位置(文件、行列区间),方便定位到策略文件中的具体规则。值得注意的是,示例校验内部以 resolve_host_executables: trueMatchOptions 运行,即示例校验不受 basename 回退开关限制。

三、host_executable 与 basename 回退匹配语义

host_executable 用于声明某个 basename 规则只允许对哪些绝对路径生效:

host_executable(
    name = "git",
    paths = [
        "/opt/homebrew/bin/git",
        "/usr/bin/git",
    ],
)

README 列出的四条匹配语义是理解 basename 回退的关键:

  1. execpolicy 总是先尝试对首 token 做精确匹配;
  2. 在 basename 回退(host-executable resolution)未启用时,/usr/bin/git status 只会匹配首 token 恰好是 /usr/bin/git 的规则;
  3. 启用后,若没有精确规则命中,execpolicy 可以把 /usr/bin/git 回退到针对 git 的 basename 规则;
  4. 若存在 host_executable(name="git", ...),basename 回退只允许其列出的绝对路径;
  5. 若某 basename 没有 host_executable() 条目,则允许 basename 回退。

这套语义在 src/policy.rsmatches_for_command_with_options 中清晰可见:先执行 match_exact_rules(按命令首 token 从 rules_by_program 多值映射中取规则并逐条前缀匹配);只有当精确匹配为空、且 MatchOptions::resolve_host_executables 为真时,才走 match_host_executable_rulessrc/policy.rs)。后者要求首 token 是绝对路径,取其 basename 后查规则表,并做 host_executables_by_name 白名单检查——若该 basename 注册过路径列表而当前程序不在列表中,直接返回空。命中后,匹配结果通过 with_resolved_program 附上原始绝对路径,这就是响应中 resolvedProgram 字段的来源。

解析侧同样有严格校验(src/parser.rshost_executable builtin):

  • name 必须是非空裸可执行名(不允许路径分隔);
  • paths 每项必须是字符串且为绝对路径;
  • 每个路径的 basename 必须与 name 一致,否则报 host_executable path ... must have basename ...
  • 重复路径会被去重。

跨平台细节在 src/executable_name.rs:在 Windows 上查找键会统一小写并剥离 .exe.cmd.bat.com 后缀,因此 git.exegit 归一到同一 basename。

四、CLI 实操:codex execpolicy check

README 的 CLI 部分给出了完整用法。从 Codex CLI 运行 codex execpolicy check 子命令,传入一个或多个策略文件(例如 src/default.rules)和待检命令:

codex execpolicy check --rules path/to/policy.rules git status

为绝对程序路径启用 basename 回退,传 --resolve-host-executables

codex execpolicy check \
  --rules path/to/policy.rules \
  --resolve-host-executables \
  /usr/bin/git status

其他操作要点:

  • 可传多个 --rules 标志合并规则,按提供顺序加载与评估;--pretty 输出格式化 JSON;
  • 开发时也可以直接运行独立 dev 二进制:
cargo run -p codex-execpolicy -- check --rules path/to/policy.rules git status
  • 示例结果:命中规则输出 {"matchedRules":[{...}],"decision":"allow"};未命中任何规则输出 {"matchedRules":[]}

命令参数的源码级说明

独立二进制的参数定义在 src/execpolicycheck.rsExecPolicyCheckCommand 中,与 README 描述一一对应:

  • --rules / -r:可重复的路径参数,必填;
  • --prettyserde_json::to_string_pretty 输出,否则为紧凑单行 JSON;
  • --resolve-host-executables:对应 MatchOptions { resolve_host_executables },即上文 basename 回退开关;
  • 命令 token:trailing_var_arg + allow_hyphen_values,因此 git reset --hard 这类带连字符的参数会被原样收进 token 列表,而不会被 clap 误认为选项。

“合并规则、按顺序评估”的实现:load_policiessrc/execpolicycheck.rs)顺序读取并解析每个文件,共用同一个 PolicyParser 后统一 build() 成一个 Policy,因此文件间规则天然合并;评估时同一程序名的规则按插入顺序存放于 MultiMap。CLI 路径不传 heuristics 回退(None),所以“无规则命中”就是空的 matchedRules,与独立策略文件的使用场景一致。

此外,仓库还提供了策略文件的程序化追加能力:src/lib.rs 导出 blocking_append_allow_prefix_ruleblocking_append_network_rule,其中前者会自动去重——tests/basic.rsappend_allow_prefix_rule_dedupes_existing_rule 测试验证了对同一前缀追加两次后,文件内容仍只保留一条 prefix_rule(pattern=["python3"], decision="allow")

五、响应结构:matchedRules 与有效 decision

README 给出的响应形状:

{
  "matchedRules": [
    {
      "prefixRuleMatch": {
        "matchedPrefix": ["<token>", "..."],
        "decision": "allow|prompt|forbidden",
        "resolvedProgram": "/absolute/path/to/program",
        "justification": "..."
      }
    }
  ],
  "decision": "allow|prompt|forbidden"
}

对应规则与实现:

  • 没有规则命中时,matchedRules 为空数组且 decision 被省略——对应 src/execpolicycheck.rsdecision 字段 skip_serializing_if = "Option::is_none" 且取值来自 matched_rules.iter().map(RuleMatch::decision).max(),空列表即 None
  • matchedRules 列出所有前缀命中的规则,matchedPrefix 是实际命中的精确前缀(由 src/rule.rsPrefixPattern::matches_prefix 返回,长度等于 pattern 长度);
  • resolvedProgram 仅在绝对可执行路径经 basename 回退命中时出现(Option::is_none 时跳过序列化,见 src/rule.rs);
  • 有效 decision 取所有命中判定中最严格的一个(forbidden > prompt > allow)。源码依据:Decision 派生 Ord 且变体顺序即严重程度顺序,src/policy.rsEvaluation::from_matches 直接对所有命中的 decision 求 .max();CLI 输出同理。

justification 字段同样按需序列化,命中时会随 prefixRuleMatch 一起返回,供上层在审批提示或拒绝消息中展示。

六、匹配与求值流程小结,以及预览状态说明

结合上文,一次 codex execpolicy check 的完整流程是:

  1. 加载:按 --rules 顺序读取策略文件,starlark 解析并执行 prefix_rule / host_executable builtin,累积为 Policy
  2. 加载期校验:对每条规则的 match / not_match 示例做正反例校验,失败即拒绝加载并附位置信息;
  3. 求值:先精确匹配首 token,未命中且开启 --resolve-host-executables 时走 basename 回退(受 host_executable 白名单约束);
  4. 输出:序列化全部命中规则与“最严格”的有效 decision 为 JSON。

需要特别注意 README 结尾的提示:execpolicy 命令仍处于 preview 阶段,API 未来可能有不兼容变更,基于它编写自动化时应以当前仓库 codex-rs/execpolicy 的实际实现为准。从源码结构看,语言扩展已在进行中——parser 已内置 network_rule(host=..., protocol=..., decision=...) builtin(支持 http / https / socks5_tcp / socks5_udp 协议,且 deny 等价于 forbidden,见 src/rule.rstests/basic.rs 中的网络规则测试),后续版本的策略语言可以在此基础上进一步扩展。

参考路径

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
594
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
916
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
516
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388