Befehlsklassifikation (execpolicy)
Der execpolicy-Crate ist die Befehlsklassifikations-Engine von codex: Für ein gegebenes argv entscheidet er, ob allow, prompt oder forbidden. Die Regeln sind in Starlark unter rules/ geschrieben; zur Laufzeit parst PolicyParser sie in eine Policy, und Policy::check läuft vor jedem Befehl. Auf core-Seite legt ExecPolicyManager Approval-Policy und heuristische Fälle darüber.
Verantwortlichkeiten
- Mit der dreiwertigen Enum
Decisiondie Entscheidung jeder Regel ausdrücken:codex-rs/execpolicy/src/decision.rs:9-27 - Mit
PrefixRule/NetworkRulePräfix-Matching und Netzwerk-Domain-Matching durchführen:codex-rs/execpolicy/src/rule.rs:40-115 - Starlark-Parser kompiliert die Policy-Datei in eine
Policy:codex-rs/execpolicy/src/parser.rs:38-83 - Auf core-Seite
ExecPolicyManager: Policy-Ergebnis mit Approval-Policy und heuristischem Rückfall für gefährliche Befehle zusammenführen:codex-rs/core/src/exec_policy.rs:312-410 - Für Befehle ohne Regelmatch: Standard-Decision aus Profil und Befehlsquelle ableiten:
codex-rs/core/src/exec_policy.rs:728-760
Entwurfsbeweggründe
Warum nicht Regex oder Glob? Weil die Befehlsstruktur selbst argv ist und Präfix-Matching präziser als String-Regex ist; zudem ist es einfacher zurückzuschreiben — klickt der Nutzer auf „ab immer erlaubt", hängt blocking_append_allow_prefix_rule einfach eine Zeile prefix_rule(pattern=["npm", "run"], decision="allow") an die Policy-Datei an; beim nächsten Mal matcht dasselbe argv. PrefixPattern unterstützt Single und Alts ([a|b]) als Token und deckt damit „Präfix + Entweder-Oder-Argument" ab.
Warum Starlark statt TOML? Weil Policy bedingte Logik braucht („host in bestimmter Liste → allow") und Funktionen (prefix_rule, network_rule), was TOML nicht ausdrücken kann. Starlark ist eine Python-Teilmenge, Policy-Dateien bleiben lesbar, und der starlark-Crate auf Rust-Seite kann sie sicher auswerten. PolicyParser bindet AST und PolicyBuilder über RefCell zusammen und baut nach dem Parsen in einem Schritt die unveränderliche Policy.
Decision ist drei- statt zweiwertig, um „explizit verboten" von „Approval nötig" zu unterscheiden. forbidden blockiert direkt, prompt löst die Approval-UI aus, allow überspringt die Approval. Treffen mehrere Regeln zu, gewinnt max — Allow < Prompt < Forbidden; die strengste Entscheidung gewinnt.
Wichtige Dateien
codex-rs/execpolicy/src/lib.rs:1-33— Crate-Einstieg, re-exportiert die gesamte öffentliche API.codex-rs/execpolicy/src/decision.rs:9-27—Decision-Enum undparse.codex-rs/execpolicy/src/policy.rs:28-67—Policy-Struktur mitrules_by_program/network_rules/host_executables.codex-rs/execpolicy/src/policy.rs:188-251—check/check_multiple_with_options-Einstieg.codex-rs/execpolicy/src/policy.rs:351-375—Evaluationfasst mehrere Regel-Ergebnisse zusammen;from_matcheswählt die strengste Entscheidung.codex-rs/execpolicy/src/rule.rs:40-60—PrefixPattern::matches_prefixfür Präfix-Matching.codex-rs/execpolicy/src/parser.rs:38-83—PolicyParserparst die Starlark-Policy-Datei.codex-rs/execpolicy/src/amend.rs:65-125— Hängt zur Laufzeit neue Regeln in die Policy-Datei an.codex-rs/core/src/command_canonicalization.rs:14-38— Normalisiertbash -lc-verpackte Befehle vor dem Matching.
Decision ist drei- statt zweiwertig, um „explizit verboten" von „Approval nötig" zu unterscheiden.
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,
}Präfix-Matching ist präziser als String-Regex und leichter zurückzuschreiben — klickt der Nutzer auf „ab immer erlaubt", baut blocking_append_allow_prefix_rule einen Starlark-Aufruf zusammen und hängt ihn an die Policy-Datei an.
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)
}Auf core-Seite kommen nach dem Policy-Urteil noch Heuristiken dazu: Bei keinem Regelmatch leitet render_decision_for_unmatched_command aus Bedingungen wie is_known_safe_command und profile_has_managed_filesystem_restrictions eine Standard-Decision ab.
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 => /* 直接放行 */,
}Datenfluss
Grenzen und Fehler
parse_shell_lc_plain_commandsverarbeitet nur einfache Shell; Heredoc/Redirect/Pipe nutzenused_complex_parsing=trueund erlauben keine automatische Regelableitung:codex-rs/core/src/exec_policy.rs:325-353- Bei Konflikten mehrerer Regeln wählt
from_matchesmax(), alsoAllow < Prompt < Forbidden— die strengste Entscheidung gewinnt:codex-rs/execpolicy/src/policy.rs:357-374 - Der Host einer
network_ruledarf kein Scheme oder Pfad enthalten;normalize_network_rule_hostlehnt Schreibweisen wiehttps://example.com/fooab:codex-rs/execpolicy/src/rule.rs:156-189 - Der heuristische Rückfall
render_decision_for_unmatched_commandbetrachtetwindows_sandbox_levelund die managed-FS-Beschränkung des Profils — ist die Windows-Sandbox deaktiviert, geht es auch bei sehr strengem Profil konservativ vor:codex-rs/core/src/exec_policy.rs:751-757 canonicalize_command_for_approvalversiehtbash -lc '...'-Befehle mit dem Präfix__codex_shell_script__, damit Abweichungen im Wrapper-Pfad nicht zu verpassten Regel-Matches führen:codex-rs/core/src/command_canonicalization.rs:21-35
Zusammenfassung
execpolicy ist der Kern der codex-Befehlsklassifikation: Starlark-Policy wird in Policy kompiliert, PrefixRule und NetworkRule matchen präzise, und Treffer ohne Regelmatch laufen über den heuristischen Rückfall auf core-Seite. Das dreiwertige Decision bringt „allow/approval/forbidden" in eine einheitliche Semantik, und bei Regelkonflikten gewinnt die strengste Entscheidung. Diese Kette zu verstehen ist wichtig, um „warum dieser Befehl Approval verlangt" und „warum eine gespeicherte Regel nicht zieht" zu debuggen; die Ausführung landet in Befehlsausführung exec-server und Plattformübergreifende Sandbox.