pub use agent_mesh_protocol::caveats::{Caveats, CountBound, Scope};
pub trait ScopeExt<T: Ord + Clone> {
fn permits(&self, item: &T) -> bool;
}
impl<T: Ord + Clone> ScopeExt<T> for Scope<T> {
fn permits(&self, item: &T) -> bool {
match self {
Self::All => true,
Self::Only(set) => set.contains(item),
}
}
}
pub trait CountBoundExt {
fn permits_one_more(&self, used_so_far: u64) -> bool;
}
impl CountBoundExt for CountBound {
fn permits_one_more(&self, used_so_far: u64) -> bool {
match self {
Self::Unlimited => true,
Self::AtMost(n) => used_so_far < *n,
}
}
}
pub trait CaveatsExt {
fn permits_fs_read(&self, path: &str) -> bool;
fn permits_fs_write(&self, path: &str) -> bool;
fn permits_exec(&self, cmd: &str) -> bool;
fn permits_net(&self, host: &str) -> bool;
}
impl CaveatsExt for Caveats {
fn permits_fs_read(&self, path: &str) -> bool {
self.fs_read.permits(&path.to_string())
}
fn permits_fs_write(&self, path: &str) -> bool {
self.fs_write.permits(&path.to_string())
}
fn permits_exec(&self, cmd: &str) -> bool {
self.exec.permits(&cmd.to_string())
}
fn permits_net(&self, host: &str) -> bool {
self.net.permits(&host.to_string())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn scope_all_permits_everything() {
let s: Scope<String> = Scope::All;
assert!(s.permits(&"anything".to_string()));
assert!(s.permits(&"".to_string()));
}
#[test]
fn scope_only_permits_exact_members() {
let s = Scope::<String>::only(["a".to_string(), "b".to_string()]);
assert!(s.permits(&"a".to_string()));
assert!(s.permits(&"b".to_string()));
assert!(!s.permits(&"c".to_string()));
assert!(!s.permits(&"".to_string()));
}
#[test]
fn scope_none_permits_nothing() {
let s: Scope<String> = Scope::none();
assert!(!s.permits(&"a".to_string()));
}
#[test]
fn count_bound_permits_one_more() {
assert!(CountBound::Unlimited.permits_one_more(0));
assert!(CountBound::Unlimited.permits_one_more(99_999));
assert!(CountBound::AtMost(3).permits_one_more(0));
assert!(CountBound::AtMost(3).permits_one_more(2));
assert!(!CountBound::AtMost(3).permits_one_more(3));
assert!(!CountBound::AtMost(3).permits_one_more(99));
assert!(!CountBound::AtMost(0).permits_one_more(0));
}
#[test]
fn caveats_top_permits_everything() {
let c = Caveats::top();
assert!(c.permits_fs_read("/anywhere"));
assert!(c.permits_fs_write("/anywhere"));
assert!(c.permits_exec("rm"));
assert!(c.permits_net("evil.example.com"));
assert!(c.max_calls.permits_one_more(1_000_000));
}
#[test]
fn caveats_attenuated_denies_outside_scope() {
let c = Caveats {
fs_write: Scope::only(["allowed.rs".to_string()]),
net: Scope::only(["allowed.example.com".to_string()]),
max_calls: CountBound::AtMost(2),
..Caveats::top()
};
assert!(c.permits_fs_write("allowed.rs"));
assert!(!c.permits_fs_write("forbidden.rs"));
assert!(c.permits_net("allowed.example.com"));
assert!(!c.permits_net("evil.example.com"));
assert!(c.max_calls.permits_one_more(1));
assert!(!c.max_calls.permits_one_more(2));
}
}