コマンド分類 (execpolicy)
execpolicy crate は codex のコマンド分類エンジンだ:argv を与えられると、allow、prompt、forbidden のいずれかを判定する。ルールは Starlark で rules/ ディレクトリに書かれ、実行時に PolicyParser が Policy にパースし、Policy::check が各コマンドの前に一回走る。core の ExecPolicyManager はその上に承認ポリシーとヒューリスティックフォールバックを重ねる。
責務
Decisionの三値列挙型で各ルールの判決を表す:codex-rs/execpolicy/src/decision.rs:9-27PrefixRule/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 をサポートし、「前缀 + 二択引数」のシーンをちょうど覆う。
なぜ TOML ではなく Starlark なのか?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 入口。全公開 API を re-export。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 と クロスプラットフォームサンドボックス にある。