openmoat_core/ir/
check.rs1use super::{Access, Effect, Enforcement};
5use crate::action::AtomicAction;
6use crate::kind::Kind;
7use crate::pattern::{GlobPattern, any_match};
8use crate::policy::PolicyError;
9
10#[derive(Debug)]
12pub struct Checker {
13 read_allowances: Vec<GlobPattern>,
15 read: CompiledAccess,
16 write: CompiledAccess,
17 net: CompiledAccess,
18 fetch: CompiledAccess,
19}
20
21#[derive(Debug)]
22struct CompiledAccess {
23 default: Effect,
24 deny: Vec<Vec<GlobPattern>>,
25 allow: Vec<Vec<GlobPattern>>,
26}
27
28impl Enforcement {
29 pub fn checker(&self) -> Result<Checker, PolicyError> {
31 let paths = self.fs.case_insensitive;
32 Ok(Checker {
33 read_allowances: self
34 .allowances
35 .iter()
36 .filter(|a| a.kind == Kind::FsRead)
37 .flat_map(|a| &a.patterns)
38 .map(|p| GlobPattern::compile(p, paths))
39 .collect::<Result<_, _>>()?,
40 read: CompiledAccess::compile(&self.fs.read, paths)?,
41 write: CompiledAccess::compile(&self.fs.write, paths)?,
42 net: CompiledAccess::compile(&self.egress.net, true)?,
43 fetch: CompiledAccess::compile(&self.egress.fetch, true)?,
44 })
45 }
46}
47
48impl Checker {
49 #[must_use]
51 pub fn check(&self, atom: &AtomicAction) -> Option<Effect> {
52 let access = match atom.kind() {
53 Kind::FsRead => &self.read,
54 Kind::FsWrite => &self.write,
55 Kind::Net => &self.net,
56 Kind::Fetch => &self.fetch,
57 Kind::Shell | Kind::WebSearch | Kind::EnvRead | Kind::EnvSet | Kind::Mcp => {
58 return None;
59 }
60 };
61 Some(access.effect(atom.subject()?))
62 }
63
64 #[must_use]
67 pub fn check_os(&self, atom: &AtomicAction) -> Option<Effect> {
68 let effect = self.check(atom)?;
69 let allowed = atom.kind() == Kind::FsRead
70 && !self.read.denies(atom.subject()?)
71 && any_match(&self.read_allowances, atom.subject()?);
72 Some(if allowed { Effect::Allow } else { effect })
73 }
74}
75
76impl CompiledAccess {
77 fn compile(access: &Access, case_insensitive: bool) -> Result<Self, PolicyError> {
78 let compile = |rules: &[super::Rule]| {
79 rules
80 .iter()
81 .map(|rule| {
82 rule.patterns
83 .iter()
84 .map(|p| GlobPattern::compile(p, case_insensitive))
85 .collect::<Result<Vec<_>, _>>()
86 })
87 .collect::<Result<Vec<_>, _>>()
88 };
89 Ok(Self {
90 default: access.default,
91 deny: compile(&access.deny)?,
92 allow: compile(&access.allow)?,
93 })
94 }
95
96 fn denies(&self, subject: &str) -> bool {
97 self.deny.iter().any(|r| any_match(r, subject))
98 }
99
100 fn effect(&self, subject: &str) -> Effect {
101 let hit = |rules: &[Vec<GlobPattern>]| rules.iter().any(|r| any_match(r, subject));
102 if self.denies(subject) {
103 Effect::Deny
104 } else if hit(&self.allow) {
105 Effect::Allow
106 } else {
107 self.default
108 }
109 }
110}