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::EnvRead | Kind::EnvSet | Kind::Mcp => return None,
58 };
59 Some(access.effect(atom.subject()?))
60 }
61
62 #[must_use]
65 pub fn check_os(&self, atom: &AtomicAction) -> Option<Effect> {
66 let effect = self.check(atom)?;
67 let allowed = atom.kind() == Kind::FsRead
68 && !self.read.denies(atom.subject()?)
69 && any_match(&self.read_allowances, atom.subject()?);
70 Some(if allowed { Effect::Allow } else { effect })
71 }
72}
73
74impl CompiledAccess {
75 fn compile(access: &Access, case_insensitive: bool) -> Result<Self, PolicyError> {
76 let compile = |rules: &[super::Rule]| {
77 rules
78 .iter()
79 .map(|rule| {
80 rule.patterns
81 .iter()
82 .map(|p| GlobPattern::compile(p, case_insensitive))
83 .collect::<Result<Vec<_>, _>>()
84 })
85 .collect::<Result<Vec<_>, _>>()
86 };
87 Ok(Self {
88 default: access.default,
89 deny: compile(&access.deny)?,
90 allow: compile(&access.allow)?,
91 })
92 }
93
94 fn denies(&self, subject: &str) -> bool {
95 self.deny.iter().any(|r| any_match(r, subject))
96 }
97
98 fn effect(&self, subject: &str) -> Effect {
99 let hit = |rules: &[Vec<GlobPattern>]| rules.iter().any(|r| any_match(r, subject));
100 if self.denies(subject) {
101 Effect::Deny
102 } else if hit(&self.allow) {
103 Effect::Allow
104 } else {
105 self.default
106 }
107 }
108}