aa_security/policy/
document.rs1use super::capability::CapabilitySet;
15use super::syscall::SyscallAllowlist;
16
17#[derive(Debug, Clone, PartialEq, Eq, Default)]
22#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
23pub struct NetworkPolicy {
24 pub allowlist: Vec<String>,
26}
27
28#[derive(Debug, Clone, PartialEq, Eq)]
35#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
36pub struct ToolRule {
37 pub name: String,
39 pub allow: bool,
41 pub requires_approval_if: Option<String>,
43}
44
45#[derive(Debug, Clone, PartialEq, Eq, Default)]
47#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
48pub struct PolicyDocument {
49 pub name: Option<String>,
51 pub network: Option<NetworkPolicy>,
53 pub capabilities: Option<CapabilitySet>,
55 pub tools: Vec<ToolRule>,
57 pub syscall_allowlist: Option<SyscallAllowlist>,
62}
63
64impl PolicyDocument {
65 pub fn denied_capabilities(&self) -> Vec<&super::capability::Capability> {
67 self.capabilities
68 .as_ref()
69 .map(|c| c.deny.iter().collect())
70 .unwrap_or_default()
71 }
72
73 pub fn egress_allowlist(&self) -> &[String] {
75 self.network.as_ref().map(|n| n.allowlist.as_slice()).unwrap_or(&[])
76 }
77
78 pub fn allowed_syscalls(&self) -> Vec<super::syscall::Syscall> {
81 self.syscall_allowlist
82 .as_ref()
83 .map(|a| a.iter().collect())
84 .unwrap_or_default()
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use super::super::capability::Capability;
91 use super::*;
92
93 #[test]
94 fn default_document_is_empty() {
95 let doc = PolicyDocument::default();
96 assert!(doc.name.is_none());
97 assert!(doc.network.is_none());
98 assert!(doc.capabilities.is_none());
99 assert!(doc.tools.is_empty());
100 assert!(doc.denied_capabilities().is_empty());
101 assert!(doc.egress_allowlist().is_empty());
102 assert!(doc.syscall_allowlist.is_none());
103 assert!(doc.allowed_syscalls().is_empty());
104 }
105
106 #[test]
107 fn accessors_read_through() {
108 use super::super::syscall::{Syscall, SyscallAllowlist};
109 let mut caps = CapabilitySet::default();
110 caps.deny.insert(Capability::FileWrite);
111 let doc = PolicyDocument {
112 name: Some("strict".to_string()),
113 network: Some(NetworkPolicy {
114 allowlist: vec!["api.openai.com".to_string()],
115 }),
116 capabilities: Some(caps),
117 tools: vec![ToolRule {
118 name: "write_file".to_string(),
119 allow: false,
120 requires_approval_if: None,
121 }],
122 syscall_allowlist: Some(SyscallAllowlist::from_names(["read", "write"]).unwrap()),
123 };
124 assert_eq!(doc.denied_capabilities(), vec![&Capability::FileWrite]);
125 assert_eq!(doc.egress_allowlist(), ["api.openai.com"]);
126 assert_eq!(doc.tools.len(), 1);
127 assert_eq!(doc.allowed_syscalls(), vec![Syscall::Read, Syscall::Write]);
128 }
129}