Classification de commande (execpolicy)
Le crate execpolicy est le moteur de classification de commande de codex : étant donné un argv, il décide s'il faut allow, prompt ou forbidden. Les règles sont écrites en Starlark dans le répertoire rules/ ; au runtime, PolicyParser les compile en Policy, et Policy::check est exécuté avant chaque commande. Côté core, ExecPolicyManager superpose une stratégie d'approval et un heuristique de repli.
Responsabilités
- Utiliser une énum
Decisionà trois valeurs pour exprimer le verdict de chaque règle :codex-rs/execpolicy/src/decision.rs:9-27 - Faire du prefix matching et du network domain matching avec deux types de règles
PrefixRule/NetworkRule:codex-rs/execpolicy/src/rule.rs:40-115 - Le parser Starlark compile le fichier policy en
Policy:codex-rs/execpolicy/src/parser.rs:38-83 - Côté core,
ExecPolicyManagerfusionne le verdict de la policy avec la stratégie d'approval et l'heuristique sur commandes dangereuses :codex-rs/core/src/exec_policy.rs:312-410 - Pour les commandes sans règle匹配ée, dériver une
Decisionpar défaut selon le profile et la source de la commande :codex-rs/core/src/exec_policy.rs:728-760
Motivations de conception
Pourquoi pas regex ou glob ? Parce que la structure d'une commande est un argv ; le prefix matching est plus précis qu'un regex sur la chaîne, et aussi plus facile à réécrire — quand l'utilisateur clique « toujours autoriser », blocking_append_allow_prefix_rule ajoute directement une ligne prefix_rule(pattern=["npm", "run"], decision="allow") au fichier policy, et le même argv suivra hit la règle au coup suivant. PrefixPattern supporte Single et Alts ([a|b]), couvrant le cas « préfixe + paramètre au choix ».
Pourquoi Starlark plutôt que TOML ? Parce que la policy a besoin de logique conditionnelle (« allow seulement si host est dans telle liste ») et de fonctions (prefix_rule, network_rule), ce que TOML ne sait pas exprimer. Starlark est un sous-ensemble de Python, lisible, tout en pouvant être évalué sûrement côté Rust par le crate starlark. PolicyParser attache l'AST et PolicyBuilder via RefCell, et un build final produit une Policy immutable.
Decision à trois valeurs plutôt que binaire, pour distinguer « interdit explicitement » de « requiert approval ». forbidden bloque direct ; prompt déclenche l'UI d'approval ; allow skip l'approval. Quand plusieurs règles matchent, on prend le max — Allow < Prompt < Forbidden, le verdict le plus strict gagne.
Fichiers clés
codex-rs/execpolicy/src/lib.rs:1-33— entrée du crate, re-exporte toute l'API publique.codex-rs/execpolicy/src/decision.rs:9-27— l'énumDecisionet sonparse.codex-rs/execpolicy/src/policy.rs:28-67— structurePolicyavecrules_by_program/network_rules/host_executables.codex-rs/execpolicy/src/policy.rs:188-251— entréescheck/check_multiple_with_options.codex-rs/execpolicy/src/policy.rs:351-375—Evaluationagrège les résultats de multiples règles,from_matchesprend le verdict le plus strict.codex-rs/execpolicy/src/rule.rs:40-60—PrefixPattern::matches_prefixfait le prefix matching.codex-rs/execpolicy/src/parser.rs:38-83—PolicyParserparse le fichier policy Starlark.codex-rs/execpolicy/src/amend.rs:65-125— ajoute une nouvelle règle au fichier policy à l'exécution.codex-rs/core/src/command_canonicalization.rs:14-38— normalise une commande enrobéebash -lcavant matching.
Decision à trois valeurs plutôt que binaire, pour distinguer « interdit explicitement » de « requiert approval ».
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,
}Le prefix matching est plus précis qu'un regex sur la chaîne, et aussi plus facile à réécrire — quand l'utilisateur clique « toujours autoriser », blocking_append_allow_prefix_rule fabrique directement une ligne Starlark et l'ajoute au fichier 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)
}Côté core, après le verdict de la policy, il superpose encore un heuristique : en cas de non-match, render_decision_for_unmatched_command regarde is_known_safe_command, profile_has_managed_filesystem_restrictions, etc., pour dériver une Decision par défaut.
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 => /* 直接放行 */,
}Flux de données
Limites et échecs
parse_shell_lc_plain_commandsne gère que du shell simple ; heredoc/redirection/pipe passent parused_complex_parsing=trueet ne peuvent pas dériver de règle corrective automatique :codex-rs/core/src/exec_policy.rs:325-353- En cas de conflit entre règles,
from_matchesprendmax(), soitAllow < Prompt < Forbidden, le verdict le plus strict gagne :codex-rs/execpolicy/src/policy.rs:357-374 - Le host d'
network_rulene peut pas contenir de scheme ou de path ;normalize_network_rule_hostrefuse une écriture commehttps://example.com/foo:codex-rs/execpolicy/src/rule.rs:156-189 - L'heuristique de repli
render_decision_for_unmatched_commandregardewindows_sandbox_levelet la restriction FS managed du profile — quand le sandbox Windows est désactivé, même un profile très restrictif prend un chemin conservateur :codex-rs/core/src/exec_policy.rs:751-757 canonicalize_command_for_approvalpréfixe les commandes typebash -lc '...'par__codex_shell_script__, pour éviter que des différences de chemin wrapper ne court-circuitent la règle :codex-rs/core/src/command_canonicalization.rs:21-35
Récapitulatif
execpolicy est le cœur de la classification de commande codex : la policy Starlark est compilée en Policy, PrefixRule et NetworkRule font du matching précis, et en cas de non-match, l'heuristique côté core prend le relais. Les trois valeurs de Decision expriment « allow/approval/forbidden » dans une même sémantique ; en cas de conflit de règles, le verdict le plus strict gagne. Comprendre cette chaîne est clé pour déboguer « pourquoi cette commande requiert-elle approval » ou « l'utilisateur a enregistré une règle, pourquoi ne s'applique-t-elle pas » ; l'exécution effective est décrite dans Exécution exec-server et Bac à sable multiplateforme.