1use std::collections::HashSet;
8
9#[derive(Debug, Clone)]
11pub struct SecurityPolicy {
12 pub allowed_connectors: HashSet<String>,
13 pub denied_paths: Vec<String>,
14 pub allow_network: bool,
15 pub allow_file_read: bool,
16 pub allow_file_write: bool,
17 pub sandbox_mode: bool,
18}
19
20impl SecurityPolicy {
21 pub fn permissive() -> Self {
22 SecurityPolicy {
23 allowed_connectors: HashSet::new(),
24 denied_paths: Vec::new(),
25 allow_network: true,
26 allow_file_read: true,
27 allow_file_write: true,
28 sandbox_mode: false,
29 }
30 }
31
32 pub fn sandbox() -> Self {
33 SecurityPolicy {
34 allowed_connectors: HashSet::new(),
35 denied_paths: Vec::new(),
36 allow_network: false,
37 allow_file_read: true,
38 allow_file_write: false,
39 sandbox_mode: true,
40 }
41 }
42
43 pub fn check(&self, permission: &str) -> bool {
45 if !self.sandbox_mode {
46 return true;
47 }
48 match permission {
49 "network" => self.allow_network,
50 "file_read" => self.allow_file_read,
51 "file_write" => self.allow_file_write,
52 "python" => false,
53 "env_write" => false,
54 p if p.starts_with("connector:") => {
55 let conn_type = &p["connector:".len()..];
56 self.allowed_connectors.is_empty() || self.allowed_connectors.contains(conn_type)
57 }
58 _ => true,
59 }
60 }
61
62 pub fn check_path(&self, path: &str) -> bool {
64 if !self.sandbox_mode {
65 return true;
66 }
67 !self
68 .denied_paths
69 .iter()
70 .any(|denied| path.starts_with(denied))
71 }
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77
78 #[test]
79 fn test_permissive_allows_all() {
80 let policy = SecurityPolicy::permissive();
81 assert!(policy.check("network"));
82 assert!(policy.check("file_read"));
83 assert!(policy.check("file_write"));
84 assert!(policy.check("connector:postgres"));
85 }
86
87 #[test]
88 fn test_sandbox_restricts() {
89 let policy = SecurityPolicy::sandbox();
90 assert!(!policy.check("network"));
91 assert!(policy.check("file_read"));
92 assert!(!policy.check("file_write"));
93 }
94
95 #[test]
96 fn test_connector_whitelist() {
97 let mut policy = SecurityPolicy::sandbox();
98 policy.allowed_connectors.insert("postgres".to_string());
99 assert!(policy.check("connector:postgres"));
100 assert!(!policy.check("connector:mysql"));
101 }
102
103 #[test]
104 fn test_denied_paths() {
105 let mut policy = SecurityPolicy::sandbox();
106 policy.denied_paths.push("/etc/".to_string());
107 assert!(!policy.check_path("/etc/passwd"));
108 assert!(policy.check_path("/home/user/file.txt"));
109 }
110}