Files
llmx/codex-rs/execpolicy/src/policy.rs
Michael Bolin 50c48e88f5 chore: upgrade to Rust 1.89 (#2465)
Codex created this PR from the following prompt:

> upgrade this entire repo to Rust 1.89. Note that this requires
updating codex-rs/rust-toolchain.toml as well as the workflows in
.github/. Make sure that things are "clippy clean" as this change will
likely uncover new Clippy errors. `just fmt` and `cargo clippy --tests`
are sufficient to check for correctness

Note this modifies a lot of lines because it folds nested `if`
statements using `&&`.

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/2465).
* #2467
* __->__ #2465
2025-08-19 13:22:02 -07:00

104 lines
3.3 KiB
Rust

use multimap::MultiMap;
use regex_lite::Error as RegexError;
use regex_lite::Regex;
use crate::ExecCall;
use crate::Forbidden;
use crate::MatchedExec;
use crate::NegativeExamplePassedCheck;
use crate::ProgramSpec;
use crate::error::Error;
use crate::error::Result;
use crate::policy_parser::ForbiddenProgramRegex;
use crate::program::PositiveExampleFailedCheck;
pub struct Policy {
programs: MultiMap<String, ProgramSpec>,
forbidden_program_regexes: Vec<ForbiddenProgramRegex>,
forbidden_substrings_pattern: Option<Regex>,
}
impl Policy {
pub fn new(
programs: MultiMap<String, ProgramSpec>,
forbidden_program_regexes: Vec<ForbiddenProgramRegex>,
forbidden_substrings: Vec<String>,
) -> std::result::Result<Self, RegexError> {
let forbidden_substrings_pattern = if forbidden_substrings.is_empty() {
None
} else {
let escaped_substrings = forbidden_substrings
.iter()
.map(|s| regex_lite::escape(s))
.collect::<Vec<_>>()
.join("|");
Some(Regex::new(&format!("({escaped_substrings})"))?)
};
Ok(Self {
programs,
forbidden_program_regexes,
forbidden_substrings_pattern,
})
}
pub fn check(&self, exec_call: &ExecCall) -> Result<MatchedExec> {
let ExecCall { program, args } = &exec_call;
for ForbiddenProgramRegex { regex, reason } in &self.forbidden_program_regexes {
if regex.is_match(program) {
return Ok(MatchedExec::Forbidden {
cause: Forbidden::Program {
program: program.clone(),
exec_call: exec_call.clone(),
},
reason: reason.clone(),
});
}
}
for arg in args {
if let Some(regex) = &self.forbidden_substrings_pattern
&& regex.is_match(arg)
{
return Ok(MatchedExec::Forbidden {
cause: Forbidden::Arg {
arg: arg.clone(),
exec_call: exec_call.clone(),
},
reason: format!("arg `{arg}` contains forbidden substring"),
});
}
}
let mut last_err = Err(Error::NoSpecForProgram {
program: program.clone(),
});
if let Some(spec_list) = self.programs.get_vec(program) {
for spec in spec_list {
match spec.check(exec_call) {
Ok(matched_exec) => return Ok(matched_exec),
Err(err) => {
last_err = Err(err);
}
}
}
}
last_err
}
pub fn check_each_good_list_individually(&self) -> Vec<PositiveExampleFailedCheck> {
let mut violations = Vec::new();
for (_program, spec) in self.programs.flat_iter() {
violations.extend(spec.verify_should_match_list());
}
violations
}
pub fn check_each_bad_list_individually(&self) -> Vec<NegativeExamplePassedCheck> {
let mut violations = Vec::new();
for (_program, spec) in self.programs.flat_iter() {
violations.extend(spec.verify_should_not_match_list());
}
violations
}
}