aether/sandbox/
config.rs

1//! 沙箱配置
2//!
3//! 提供统一的沙箱配置入口,简化权限和安全管理。
4
5use crate::builtins::IOPermissions;
6use crate::runtime::ExecutionLimits;
7use std::collections::HashSet;
8use std::path::PathBuf;
9
10use super::path_validator::PathRestriction;
11
12/// 沙箱策略类型
13#[derive(Debug, Clone, PartialEq)]
14pub enum SandboxPolicy {
15    /// 完全禁用(默认)
16    Disabled,
17    /// 仅允许只读访问
18    ReadOnly,
19    /// 完全访问
20    FullAccess,
21}
22
23/// 统一的沙箱配置
24#[derive(Debug, Clone)]
25pub struct SandboxConfig {
26    /// IO 权限(保留向后兼容)
27    pub io_permissions: IOPermissions,
28
29    /// 文件系统沙箱策略
30    pub filesystem_policy: SandboxPolicy,
31
32    /// 文件系统路径限制
33    pub filesystem_restriction: Option<PathRestriction>,
34
35    /// 模块系统沙箱策略
36    pub module_policy: SandboxPolicy,
37
38    /// 模块路径限制
39    pub module_restriction: Option<PathRestriction>,
40
41    /// 是否启用可观测性指标收集
42    pub enable_metrics: bool,
43
44    /// 最大模块缓存数量(0 = 不限制)
45    pub max_module_cache_size: usize,
46
47    /// 模块缓存 TTL(秒,0 = 永不过期)
48    pub module_cache_ttl_secs: u64,
49
50    /// 执行限制配置
51    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    /// 创建 DSL 安全默认配置(禁用所有 IO,严格执行限制)
72    pub fn dsl_safe() -> Self {
73        Self {
74            execution_limits: ExecutionLimits::strict(),
75            ..Default::default()
76        }
77    }
78
79    /// 创建 CLI 完全访问配置
80    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    /// 创建受限沙箱配置(仅允许 root_dir 内访问)
92    pub fn sandboxed(root_dir: PathBuf) -> Self {
93        // 创建文件扩展名白名单(仅允许 .aether 文件)
94        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        // 检查路径限制
165        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}