agcodex_execpolicy/
policy.rs1use multimap::MultiMap;
2use regex_lite::Error as RegexError;
3use regex_lite::Regex;
4
5use crate::ExecCall;
6use crate::Forbidden;
7use crate::MatchedExec;
8use crate::NegativeExamplePassedCheck;
9use crate::ProgramSpec;
10use crate::error::Error;
11use crate::error::Result;
12use crate::policy_parser::ForbiddenProgramRegex;
13use crate::program::PositiveExampleFailedCheck;
14
15pub struct Policy {
16 programs: MultiMap<String, ProgramSpec>,
17 forbidden_program_regexes: Vec<ForbiddenProgramRegex>,
18 forbidden_substrings_pattern: Option<Regex>,
19}
20
21impl Policy {
22 pub fn new(
23 programs: MultiMap<String, ProgramSpec>,
24 forbidden_program_regexes: Vec<ForbiddenProgramRegex>,
25 forbidden_substrings: Vec<String>,
26 ) -> std::result::Result<Self, RegexError> {
27 let forbidden_substrings_pattern = if forbidden_substrings.is_empty() {
28 None
29 } else {
30 let escaped_substrings = forbidden_substrings
31 .iter()
32 .map(|s| regex_lite::escape(s))
33 .collect::<Vec<_>>()
34 .join("|");
35 Some(Regex::new(&format!("({escaped_substrings})"))?)
36 };
37 Ok(Self {
38 programs,
39 forbidden_program_regexes,
40 forbidden_substrings_pattern,
41 })
42 }
43
44 pub fn check(&self, exec_call: &ExecCall) -> Result<MatchedExec> {
45 let ExecCall { program, args } = &exec_call;
46 for ForbiddenProgramRegex { regex, reason } in &self.forbidden_program_regexes {
47 if regex.is_match(program) {
48 return Ok(MatchedExec::Forbidden {
49 cause: Forbidden::Program {
50 program: program.clone(),
51 exec_call: exec_call.clone(),
52 },
53 reason: reason.clone(),
54 });
55 }
56 }
57
58 for arg in args {
59 if let Some(regex) = &self.forbidden_substrings_pattern
60 && regex.is_match(arg)
61 {
62 return Ok(MatchedExec::Forbidden {
63 cause: Forbidden::Arg {
64 arg: arg.clone(),
65 exec_call: exec_call.clone(),
66 },
67 reason: format!("arg `{arg}` contains forbidden substring"),
68 });
69 }
70 }
71
72 let mut last_err = Err(Error::NoSpecForProgram {
73 program: program.clone(),
74 });
75 if let Some(spec_list) = self.programs.get_vec(program) {
76 for spec in spec_list {
77 match spec.check(exec_call) {
78 Ok(matched_exec) => return Ok(matched_exec),
79 Err(err) => {
80 last_err = Err(err);
81 }
82 }
83 }
84 }
85 last_err
86 }
87
88 pub fn check_each_good_list_individually(&self) -> Vec<PositiveExampleFailedCheck> {
89 let mut violations = Vec::new();
90 for (_program, spec) in self.programs.flat_iter() {
91 violations.extend(spec.verify_should_match_list());
92 }
93 violations
94 }
95
96 pub fn check_each_bad_list_individually(&self) -> Vec<NegativeExamplePassedCheck> {
97 let mut violations = Vec::new();
98 for (_program, spec) in self.programs.flat_iter() {
99 violations.extend(spec.verify_should_not_match_list());
100 }
101 violations
102 }
103}