1use crate::builtins::IOPermissions;
6use crate::runtime::ExecutionLimits;
7use std::collections::HashSet;
8use std::path::PathBuf;
9
10use super::path_validator::PathRestriction;
11
12#[derive(Debug, Clone, PartialEq)]
14pub enum SandboxPolicy {
15 Disabled,
17 ReadOnly,
19 FullAccess,
21}
22
23#[derive(Debug, Clone)]
25pub struct SandboxConfig {
26 pub io_permissions: IOPermissions,
28
29 pub filesystem_policy: SandboxPolicy,
31
32 pub filesystem_restriction: Option<PathRestriction>,
34
35 pub module_policy: SandboxPolicy,
37
38 pub module_restriction: Option<PathRestriction>,
40
41 pub enable_metrics: bool,
43
44 pub max_module_cache_size: usize,
46
47 pub module_cache_ttl_secs: u64,
49
50 pub execution_limits: ExecutionLimits,
52}
53
54impl Default for SandboxConfig {
55 fn default() -> Self {
56 Self {
57 io_permissions: IOPermissions::default(),
58 filesystem_policy: SandboxPolicy::Disabled,
59 filesystem_restriction: None,
60 module_policy: SandboxPolicy::Disabled,
61 module_restriction: None,
62 enable_metrics: false,
63 max_module_cache_size: 100,
64 module_cache_ttl_secs: 0,
65 execution_limits: ExecutionLimits::default(),
66 }
67 }
68}
69
70impl SandboxConfig {
71 pub fn dsl_safe() -> Self {
73 Self {
74 execution_limits: ExecutionLimits::strict(),
75 ..Default::default()
76 }
77 }
78
79 pub fn cli_full_access() -> Self {
81 Self {
82 io_permissions: IOPermissions::allow_all(),
83 filesystem_policy: SandboxPolicy::FullAccess,
84 module_policy: SandboxPolicy::FullAccess,
85 enable_metrics: true,
86 execution_limits: ExecutionLimits::lenient(),
87 ..Default::default()
88 }
89 }
90
91 pub fn sandboxed(root_dir: PathBuf) -> Self {
93 let mut allowed_extensions = HashSet::new();
95 allowed_extensions.insert("aether".to_string());
96
97 Self {
98 io_permissions: IOPermissions {
99 filesystem_enabled: true,
100 network_enabled: false,
101 },
102 filesystem_policy: SandboxPolicy::ReadOnly,
103 filesystem_restriction: Some(PathRestriction {
104 root_dir: root_dir.clone(),
105 allow_absolute: false,
106 allow_parent_traversal: false,
107 allowed_extensions: None,
108 }),
109 module_policy: SandboxPolicy::ReadOnly,
110 module_restriction: Some(PathRestriction {
111 root_dir,
112 allow_absolute: false,
113 allow_parent_traversal: false,
114 allowed_extensions: Some(allowed_extensions),
115 }),
116 enable_metrics: true,
117 execution_limits: ExecutionLimits::default(),
118 ..Default::default()
119 }
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126
127 #[test]
128 fn test_sandbox_config_default() {
129 let config = SandboxConfig::default();
130 assert_eq!(config.filesystem_policy, SandboxPolicy::Disabled);
131 assert_eq!(config.module_policy, SandboxPolicy::Disabled);
132 assert!(!config.enable_metrics);
133 }
134
135 #[test]
136 fn test_sandbox_config_dsl_safe() {
137 let config = SandboxConfig::dsl_safe();
138 assert_eq!(config.filesystem_policy, SandboxPolicy::Disabled);
139 assert_eq!(config.module_policy, SandboxPolicy::Disabled);
140 assert!(!config.io_permissions.filesystem_enabled);
141 assert!(!config.io_permissions.network_enabled);
142 }
143
144 #[test]
145 fn test_sandbox_config_cli_full_access() {
146 let config = SandboxConfig::cli_full_access();
147 assert_eq!(config.filesystem_policy, SandboxPolicy::FullAccess);
148 assert_eq!(config.module_policy, SandboxPolicy::FullAccess);
149 assert!(config.io_permissions.filesystem_enabled);
150 assert!(config.io_permissions.network_enabled);
151 assert!(config.enable_metrics);
152 }
153
154 #[test]
155 fn test_sandbox_config_sandboxed() {
156 let root = PathBuf::from("/safe/dir");
157 let config = SandboxConfig::sandboxed(root.clone());
158
159 assert_eq!(config.filesystem_policy, SandboxPolicy::ReadOnly);
160 assert_eq!(config.module_policy, SandboxPolicy::ReadOnly);
161 assert!(config.io_permissions.filesystem_enabled);
162 assert!(!config.io_permissions.network_enabled);
163
164 assert!(config.filesystem_restriction.is_some());
166 assert!(config.module_restriction.is_some());
167
168 let fs_restriction = config.filesystem_restriction.as_ref().unwrap();
169 assert!(!fs_restriction.allow_absolute);
170 assert!(!fs_restriction.allow_parent_traversal);
171
172 let module_restriction = config.module_restriction.as_ref().unwrap();
173 assert!(module_restriction.allowed_extensions.is_some());
174 let extensions = module_restriction.allowed_extensions.as_ref().unwrap();
175 assert!(extensions.contains("aether"));
176 }
177}