Codex Handbook
execpolicy-legacy/src/policy.rs 103 lines
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    }}