命令分類 (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 與 跨平台沙箱。