use crate::builtins::IOPermissions;
use crate::runtime::ExecutionLimits;
use std::collections::HashSet;
use std::path::PathBuf;
use super::path_validator::PathRestriction;
#[derive(Debug, Clone, PartialEq)]
pub enum SandboxPolicy {
Disabled,
ReadOnly,
FullAccess,
}
#[derive(Debug, Clone)]
pub struct SandboxConfig {
pub io_permissions: IOPermissions,
pub filesystem_policy: SandboxPolicy,
pub filesystem_restriction: Option<PathRestriction>,
pub module_policy: SandboxPolicy,
pub module_restriction: Option<PathRestriction>,
pub enable_metrics: bool,
pub max_module_cache_size: usize,
pub module_cache_ttl_secs: u64,
pub execution_limits: ExecutionLimits,
}
impl Default for SandboxConfig {
fn default() -> Self {
Self {
io_permissions: IOPermissions::default(),
filesystem_policy: SandboxPolicy::Disabled,
filesystem_restriction: None,
module_policy: SandboxPolicy::Disabled,
module_restriction: None,
enable_metrics: false,
max_module_cache_size: 100,
module_cache_ttl_secs: 0,
execution_limits: ExecutionLimits::default(),
}
}
}
impl SandboxConfig {
pub fn dsl_safe() -> Self {
Self {
execution_limits: ExecutionLimits::strict(),
..Default::default()
}
}
pub fn cli_full_access() -> Self {
Self {
io_permissions: IOPermissions::allow_all(),
filesystem_policy: SandboxPolicy::FullAccess,
module_policy: SandboxPolicy::FullAccess,
enable_metrics: true,
execution_limits: ExecutionLimits::lenient(),
..Default::default()
}
}
pub fn sandboxed(root_dir: PathBuf) -> Self {
let mut allowed_extensions = HashSet::new();
allowed_extensions.insert("aether".to_string());
Self {
io_permissions: IOPermissions {
filesystem_enabled: true,
network_enabled: false,
},
filesystem_policy: SandboxPolicy::ReadOnly,
filesystem_restriction: Some(PathRestriction {
root_dir: root_dir.clone(),
allow_absolute: false,
allow_parent_traversal: false,
allowed_extensions: None,
}),
module_policy: SandboxPolicy::ReadOnly,
module_restriction: Some(PathRestriction {
root_dir,
allow_absolute: false,
allow_parent_traversal: false,
allowed_extensions: Some(allowed_extensions),
}),
enable_metrics: true,
execution_limits: ExecutionLimits::default(),
..Default::default()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sandbox_config_default() {
let config = SandboxConfig::default();
assert_eq!(config.filesystem_policy, SandboxPolicy::Disabled);
assert_eq!(config.module_policy, SandboxPolicy::Disabled);
assert!(!config.enable_metrics);
}
#[test]
fn test_sandbox_config_dsl_safe() {
let config = SandboxConfig::dsl_safe();
assert_eq!(config.filesystem_policy, SandboxPolicy::Disabled);
assert_eq!(config.module_policy, SandboxPolicy::Disabled);
assert!(!config.io_permissions.filesystem_enabled);
assert!(!config.io_permissions.network_enabled);
}
#[test]
fn test_sandbox_config_cli_full_access() {
let config = SandboxConfig::cli_full_access();
assert_eq!(config.filesystem_policy, SandboxPolicy::FullAccess);
assert_eq!(config.module_policy, SandboxPolicy::FullAccess);
assert!(config.io_permissions.filesystem_enabled);
assert!(config.io_permissions.network_enabled);
assert!(config.enable_metrics);
}
#[test]
fn test_sandbox_config_sandboxed() {
let root = PathBuf::from("/safe/dir");
let config = SandboxConfig::sandboxed(root.clone());
assert_eq!(config.filesystem_policy, SandboxPolicy::ReadOnly);
assert_eq!(config.module_policy, SandboxPolicy::ReadOnly);
assert!(config.io_permissions.filesystem_enabled);
assert!(!config.io_permissions.network_enabled);
assert!(config.filesystem_restriction.is_some());
assert!(config.module_restriction.is_some());
let fs_restriction = config.filesystem_restriction.as_ref().unwrap();
assert!(!fs_restriction.allow_absolute);
assert!(!fs_restriction.allow_parent_traversal);
let module_restriction = config.module_restriction.as_ref().unwrap();
assert!(module_restriction.allowed_extensions.is_some());
let extensions = module_restriction.allowed_extensions.as_ref().unwrap();
assert!(extensions.contains("aether"));
}
}