Clasificación de comandos (execpolicy)
El crate execpolicy es el motor de clasificación de comandos de codex: dado un argv, decide si se allow, prompt o forbidden. Las reglas se escriben en Starlark en el directorio rules/; en runtime, PolicyParser lo parsea a Policy, y Policy::check se ejecuta antes de cada comando. El ExecPolicyManager de core añade por encima la política de aprobación y los heurísticos de retroceso.
Responsabilidades
- Usar el enum de tres valores
Decisionpara expresar la sentencia de cada regla:codex-rs/execpolicy/src/decision.rs:9-27 - Aplicar
PrefixRule/NetworkRulepara matching por prefijo y por dominio de red:codex-rs/execpolicy/src/rule.rs:40-115 - Compilar el archivo policy Starlark a
Policycon el parser:codex-rs/execpolicy/src/parser.rs:38-83 - Combinar en core (
ExecPolicyManager) el resultado de la policy con la política de aprobación y los heurísticos de comandos peligrosos:codex-rs/core/src/exec_policy.rs:312-410 - Para comandos sin regla que coincida, derivar el
Decisionpor defecto a partir del profile y del origen del comando:codex-rs/core/src/exec_policy.rs:728-760
Motivación de diseño
¿Por qué no regex o glob? Porque la estructura del comando es en sí un argv, y el matching por prefijo es más preciso que un regex sobre la cadena, y también más fácil de reescribir: cuando el usuario marca «de ahora en adelante permite siempre», blocking_append_allow_prefix_rule añade directamente al archivo de policy una línea prefix_rule(pattern=["npm", "run"], decision="allow"), y la próxima vez el mismo argv hace match. PrefixPattern soporta dos tokens, Single y Alts ([a|b]), cubriendo el escenario de «prefijo + parámetro con dos opciones».
¿Por qué Starlark y no TOML? Porque la policy necesita lógica condicional («allow solo si host está en tal lista») y funciones (prefix_rule, network_rule), y TOML no lo expresa. Starlark es un subconjunto de Python, los archivos de policy son legibles y además el crate starlark de Rust puede evaluarlos de forma segura. PolicyParser ata el AST y el PolicyBuilder con RefCell, y al terminar de parsear hace un build único que produce un Policy inmutable.
Decision tiene tres valores y no dos, para distinguir «prohibido explícitamente» de «requiere aprobación». forbidden bloquea directamente; prompt dispara la UI de aprobación del usuario; allow salta la aprobación. Cuando varias reglas coinciden a la vez, se toma el máximo — Allow < Prompt < Forbidden —, gana la sentencia más estricta.
Archivos clave
codex-rs/execpolicy/src/lib.rs:1-33— entrada del crate, re-exporta toda la API pública.codex-rs/execpolicy/src/decision.rs:9-27— enumDecisionyparse.codex-rs/execpolicy/src/policy.rs:28-67— structPolicyconrules_by_program/network_rules/host_executables.codex-rs/execpolicy/src/policy.rs:188-251— entradascheck/check_multiple_with_options.codex-rs/execpolicy/src/policy.rs:351-375—Evaluationagrega resultados de varias reglas;from_matchestoma la sentencia más estricta.codex-rs/execpolicy/src/rule.rs:40-60—PrefixPattern::matches_prefixhace el matching por prefijo.codex-rs/execpolicy/src/parser.rs:38-83—PolicyParserparsea el archivo Starlark.codex-rs/execpolicy/src/amend.rs:65-125— añade nuevas reglas al archivo policy en runtime.codex-rs/core/src/command_canonicalization.rs:14-38— normaliza los comandos envueltos porbash -lcantes de hacer match.
Decision con tres valores y no dos, para distinguir «prohibido explícitamente» de «requiere aprobación».
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,
}El matching por prefijo es más preciso que el regex sobre la cadena y también más fácil de reescribir: cuando el usuario marca «de ahora en adelante permite siempre», blocking_append_allow_prefix_rule construye una llamada Starlark y la añade al archivo de 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, tras recibir la sentencia de la policy, aún añade heurísticos: si ninguna regla coincidió, render_decision_for_unmatched_command mira condiciones como is_known_safe_command o profile_has_managed_filesystem_restrictions para derivar el Decision por defecto.
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 => { /* flujo de aprobación */ }
Decision::Allow => /* pase directo */,
}Flujo de datos
Bordes y fallos
parse_shell_lc_plain_commandssolo maneja shell simple; heredoc/redirección/pipe van porused_complex_parsing=truey no se permite derivar correcciones automáticas de reglas:codex-rs/core/src/exec_policy.rs:325-353- Cuando varias reglas entran en conflicto,
from_matchestomamax(), es decir,Allow < Prompt < Forbidden; gana la sentencia más estricta:codex-rs/execpolicy/src/policy.rs:357-374 - El host de un
network_ruleno puede llevar scheme ni path;normalize_network_rule_hostrechaza escrituras tipohttps://example.com/foo:codex-rs/execpolicy/src/rule.rs:156-189 - El retroceso heurístico
render_decision_for_unmatched_commandmirawindows_sandbox_levely las restricciones FS managed del profile: si el sandbox de Windows está apagado, aunque el profile sea muy estricto, se va por una ruta conservadora:codex-rs/core/src/exec_policy.rs:751-757 canonicalize_command_for_approvalañade el prefijo__codex_shell_script__a los comandos tipobash -lc '...', para evitar que las diferencias de ruta del wrapper hagan que las reglas no coincidan:codex-rs/core/src/command_canonicalization.rs:21-35
Resumen
execpolicy es el núcleo de la clasificación de comandos en codex: la policy Starlark se compila a Policy, PrefixRule y NetworkRule hacen matching exacto, y lo que no coincide se va al retroceso heurístico del lado core. Decision con tres valores permite expresar «permitir/aprobar/prohibir» en una misma semántica; ante conflictos, gana la sentencia más estricta. Entender esta cadena es clave para depurar «por qué este comando pide aprobación» o «por qué la regla guardada por el usuario no surte efecto». La ejecución asociada cae en Ejecución de comandos exec-server y Sandbox multiplataforma.