命令分类 (execpolicy)
execpolicy crate 是 codex 的命令分类引擎:给定一条 argv,它判断该 allow、prompt 还是 forbidden。规则用 Starlark 写在 rules/ 目录,运行时 PolicyParser 解析成 Policy,Policy::check 在每条命令前跑一遍。core 的 ExecPolicyManager 在它之上叠加审批策略与启发式回退。
职责
- 用
Decision三值枚举表达每条规则的判决:codex-rs/execpolicy/src/decision.rs:9-27 - 用
PrefixRule/NetworkRule两类规则做前缀匹配与网络域名匹配:codex-rs/execpolicy/src/rule.rs:40-115 - Starlark 解析器把 policy 文件编译成
Policy:codex-rs/execpolicy/src/parser.rs:38-83 - core 侧
ExecPolicyManager把 policy 结果跟审批策略、危险命令启发式合并:codex-rs/core/src/exec_policy.rs:312-410 - 对未匹配规则的命令,按 profile 与命令来源推导默认 Decision:
codex-rs/core/src/exec_policy.rs:728-760
设计动机
为什么不用正则或 glob?因为命令结构本身是 argv,前缀匹配比字符串正则更精确,也更容易回写——用户点了"以后都允许"后,blocking_append_allow_prefix_rule 直接往 policy 文件追加一行 prefix_rule(pattern=["npm", "run"], decision="allow"),下次同样的 argv 命中规则。PrefixPattern 支持 Single 与 Alts([a|b])两种 token,正好覆盖"前缀 + 二选一参数"的场景。
为什么是 Starlark 而不是 TOML?因为 policy 需要条件逻辑("host 在某列表里才 allow")、需要函数(prefix_rule、network_rule),TOML 表达不了。Starlark 是 Python 子集,策略文件可读性高,又能被 Rust 侧 starlark crate 安全求值。PolicyParser 把 AST 与 PolicyBuilder 用 RefCell 绑在一起,解析完一次性 build 成不可变 Policy。
Decision 三值而非二值,是为了区分"明确禁止"与"需要审批"。forbidden 直接拦,prompt 触发用户审批 UI,allow 跳过审批。多个规则同时命中时取 max——Allow < Prompt < Forbidden,最严格的判决赢。
关键文件
codex-rs/execpolicy/src/lib.rs:1-33— crate 入口,re-export 全部公开 API。codex-rs/execpolicy/src/decision.rs:9-27—Decisionenum 与parse。codex-rs/execpolicy/src/policy.rs:28-67—Policy结构与rules_by_program/network_rules/host_executables。codex-rs/execpolicy/src/policy.rs:188-251—check/check_multiple_with_options入口。codex-rs/execpolicy/src/policy.rs:351-375—Evaluation聚合多规则结果,from_matches取最严判决。codex-rs/execpolicy/src/rule.rs:40-60—PrefixPattern::matches_prefix做前缀匹配。codex-rs/execpolicy/src/parser.rs:38-83—PolicyParser解析 Starlark policy 文件。codex-rs/execpolicy/src/amend.rs:65-125— 运行时把新规则追加进 policy 文件。codex-rs/core/src/command_canonicalization.rs:14-38— 把bash -lc包装的命令规范化再做匹配。
Decision 三值而非二值,是为了区分"明确禁止"与"需要审批"。
pub enum Decision {
/// Command may run without further approval.
Allow,
/// Request explicit user approval; rejected outright when running with `approval_policy="never"`.
Prompt,
/// Command is blocked without further consideration.
Forbidden,
}前缀匹配比字符串正则更精确,也更容易回写——用户点了"以后都允许",blocking_append_allow_prefix_rule 直接拼出一行 Starlark 调用追加到 policy 文件。
pub fn blocking_append_allow_prefix_rule(
policy_path: &Path,
prefix: &[String],
) -> Result<(), AmendError> {
if prefix.is_empty() {
return Err(AmendError::EmptyPrefix);
}
let tokens = prefix
.iter()
.map(serde_json::to_string)
.collect::<Result<Vec<_>, _>>()
.map_err(|source| AmendError::SerializePrefix { source })?;
let pattern = format!("[{}]", tokens.join(", "));
let rule = format!(r#"prefix_rule(pattern={pattern}, decision="allow")"#);
append_rule_line(policy_path, &rule)
}core 在拿到 policy 判决后还会叠加启发式:未命中任何规则时 render_decision_for_unmatched_command 看 is_known_safe_command、profile_has_managed_filesystem_restrictions 等条件推导默认 Decision。
let evaluation = exec_policy.check_multiple_with_options(
commands.iter(),
&exec_policy_fallback,
&match_options,
);
// ...
match evaluation.decision {
Decision::Forbidden => ExecApprovalRequirement::Forbidden { reason: /* ... */ },
Decision::Prompt => { /* 走审批流程 */ }
Decision::Allow => /* 直接放行 */,
}数据流
边界与失败
parse_shell_lc_plain_commands只能处理简单 shell;heredoc/重定向/管道走used_complex_parsing=true,且不允许自动派生规则修正:codex-rs/core/src/exec_policy.rs:325-353- 多规则冲突时
from_matches取max(),即Allow < Prompt < Forbidden,最严判决赢:codex-rs/execpolicy/src/policy.rs:357-374 network_rule的 host 不能含 scheme 或 path,normalize_network_rule_host会拒绝https://example.com/foo这种写法:codex-rs/execpolicy/src/rule.rs:156-189- 启发式回退
render_decision_for_unmatched_command会看windows_sandbox_level与 profile 的 managed FS 限制——Windows sandbox 关掉时即使 profile 限制写得多严,也走保守路径:codex-rs/core/src/exec_policy.rs:751-757 canonicalize_command_for_approval给bash -lc '...'类命令打__codex_shell_script__前缀,避免 wrapper 路径差异导致规则不命中:codex-rs/core/src/command_canonicalization.rs:21-35
小结
execpolicy 是 codex 命令分类的核心:Starlark policy 编译成 Policy,PrefixRule 与 NetworkRule 做精确匹配,未命中的走 core 侧启发式回退。Decision 三值让"允许/审批/禁止"在同一套语义里表达,规则冲突时最严判决赢。理解这条链路对调试"为什么这条命令要审批"和"用户存了规则为什么没生效"很关键,相关执行落地在 命令执行 exec-server 与 跨平台沙箱。