sandbox_runtime/config/
loader.rs1use std::path::{Path, PathBuf};
4
5use crate::config::schema::SandboxRuntimeConfig;
6use crate::error::{ConfigError, SandboxError};
7
8const DEFAULT_SETTINGS_FILE: &str = ".srt-settings.json";
10
11pub fn default_settings_path() -> Option<PathBuf> {
13 dirs::home_dir().map(|home| home.join(DEFAULT_SETTINGS_FILE))
14}
15
16pub fn load_config(path: &Path) -> Result<SandboxRuntimeConfig, SandboxError> {
18 if !path.exists() {
19 return Err(ConfigError::FileNotFound(path.display().to_string()).into());
20 }
21
22 let content = std::fs::read_to_string(path).map_err(|e| {
23 ConfigError::ParseError(format!("Failed to read config file: {}", e))
24 })?;
25
26 parse_config(&content)
27}
28
29pub fn load_default_config() -> Result<SandboxRuntimeConfig, SandboxError> {
31 match default_settings_path() {
32 Some(path) if path.exists() => load_config(&path),
33 _ => Ok(SandboxRuntimeConfig::default()),
34 }
35}
36
37pub fn parse_config(json: &str) -> Result<SandboxRuntimeConfig, SandboxError> {
39 let config: SandboxRuntimeConfig = serde_json::from_str(json).map_err(|e| {
40 ConfigError::ParseError(format!("Failed to parse config JSON: {}", e))
41 })?;
42
43 config.validate()?;
45
46 Ok(config)
47}
48
49#[cfg(test)]
50mod tests {
51 use super::*;
52
53 #[test]
54 fn test_parse_minimal_config() {
55 let json = r#"{}"#;
56 let config = parse_config(json).unwrap();
57 assert!(config.network.allowed_domains.is_empty());
58 assert!(config.filesystem.allow_write.is_empty());
59 }
60
61 #[test]
62 fn test_parse_full_config() {
63 let json = r#"{
64 "network": {
65 "allowedDomains": ["github.com", "*.npmjs.org"],
66 "deniedDomains": ["evil.com"],
67 "allowLocalBinding": true,
68 "mitmProxy": {
69 "socketPath": "/tmp/mitm.sock",
70 "domains": ["api.example.com"]
71 }
72 },
73 "filesystem": {
74 "denyRead": ["/etc/passwd"],
75 "allowWrite": ["/tmp"],
76 "denyWrite": ["/tmp/secret"],
77 "allowGitConfig": false
78 },
79 "mandatoryDenySearchDepth": 5,
80 "allowPty": true
81 }"#;
82
83 let config = parse_config(json).unwrap();
84 assert_eq!(config.network.allowed_domains.len(), 2);
85 assert_eq!(config.network.denied_domains.len(), 1);
86 assert_eq!(config.network.allow_local_binding, Some(true));
87 assert!(config.network.mitm_proxy.is_some());
88 assert_eq!(config.filesystem.deny_read.len(), 1);
89 assert_eq!(config.filesystem.allow_write.len(), 1);
90 assert_eq!(config.filesystem.deny_write.len(), 1);
91 assert_eq!(config.mandatory_deny_search_depth, Some(5));
92 assert_eq!(config.allow_pty, Some(true));
93 }
94
95 #[test]
96 fn test_invalid_domain_pattern() {
97 let json = r#"{
98 "network": {
99 "allowedDomains": ["*.com"]
100 }
101 }"#;
102
103 let result = parse_config(json);
104 assert!(result.is_err());
105 }
106}