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, forbidden_program_regexes: Vec, forbidden_substrings_pattern: Option, } impl Policy { pub fn new( programs: MultiMap, forbidden_program_regexes: Vec, forbidden_substrings: Vec, ) -> std::result::Result { let forbidden_substrings_pattern = if forbidden_substrings.is_empty() { None } else { let escaped_substrings = forbidden_substrings .iter() .map(|s| regex_lite::escape(s)) .collect::>() .join("|"); Some(Regex::new(&format!("({escaped_substrings})"))?) }; Ok(Self { programs, forbidden_program_regexes, forbidden_substrings_pattern, }) } pub fn check(&self, exec_call: &ExecCall) -> Result { 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 { if 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 { 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 { let mut violations = Vec::new(); for (_program, spec) in self.programs.flat_iter() { violations.extend(spec.verify_should_not_match_list()); } violations } }