Skip to main content

ucm_engine/
config.rs

1//! Performance and execution configuration for the UCM engine.
2//!
3//! This module provides configuration options for controlling engine
4//! performance, resource limits, and execution behavior.
5
6use serde::{Deserialize, Serialize};
7
8/// Performance configuration for the engine
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct PerformanceConfig {
11    /// Maximum memory usage in MB (advisory)
12    pub max_memory_mb: usize,
13    /// Maximum operations per batch
14    pub max_operations_per_batch: usize,
15    /// Cache size in MB
16    pub cache_size_mb: usize,
17    /// Enable streaming for large operations
18    pub enable_streaming: bool,
19    /// Maximum concurrent operations (for async)
20    pub max_concurrent_ops: usize,
21    /// Operation timeout in milliseconds
22    pub operation_timeout_ms: u64,
23}
24
25impl Default for PerformanceConfig {
26    fn default() -> Self {
27        Self {
28            max_memory_mb: 512,
29            max_operations_per_batch: 1000,
30            cache_size_mb: 64,
31            enable_streaming: true,
32            max_concurrent_ops: 4,
33            operation_timeout_ms: 30000,
34        }
35    }
36}
37
38impl PerformanceConfig {
39    /// Create a minimal configuration for resource-constrained environments
40    pub fn minimal() -> Self {
41        Self {
42            max_memory_mb: 64,
43            max_operations_per_batch: 100,
44            cache_size_mb: 8,
45            enable_streaming: false,
46            max_concurrent_ops: 1,
47            operation_timeout_ms: 10000,
48        }
49    }
50
51    /// Create a high-performance configuration
52    pub fn high_performance() -> Self {
53        Self {
54            max_memory_mb: 2048,
55            max_operations_per_batch: 10000,
56            cache_size_mb: 256,
57            enable_streaming: true,
58            max_concurrent_ops: 16,
59            operation_timeout_ms: 60000,
60        }
61    }
62}
63
64/// Security configuration
65#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct SecurityConfig {
67    /// Allowed URL schemes for media sources
68    pub allowed_schemes: Vec<String>,
69    /// Maximum URL length
70    pub max_url_length: usize,
71    /// Block external resource loading
72    pub block_external_resources: bool,
73    /// Sanitize content on input
74    pub sanitize_content: bool,
75    /// Maximum content size per block in bytes
76    pub max_block_size: usize,
77}
78
79impl Default for SecurityConfig {
80    fn default() -> Self {
81        Self {
82            allowed_schemes: vec!["http".to_string(), "https".to_string(), "data".to_string()],
83            max_url_length: 2048,
84            block_external_resources: false,
85            sanitize_content: true,
86            max_block_size: 1024 * 1024, // 1MB
87        }
88    }
89}
90
91impl SecurityConfig {
92    /// Create a strict security configuration
93    pub fn strict() -> Self {
94        Self {
95            allowed_schemes: vec!["https".to_string()],
96            max_url_length: 1024,
97            block_external_resources: true,
98            sanitize_content: true,
99            max_block_size: 256 * 1024, // 256KB
100        }
101    }
102
103    /// Validate a URL against security settings
104    pub fn validate_url(&self, url: &str) -> Result<(), String> {
105        if url.len() > self.max_url_length {
106            return Err(format!(
107                "URL exceeds maximum length of {} characters",
108                self.max_url_length
109            ));
110        }
111
112        // Check scheme
113        let scheme = url.split(':').next().unwrap_or("");
114        if !self.allowed_schemes.contains(&scheme.to_lowercase()) {
115            return Err(format!(
116                "URL scheme '{}' is not allowed. Allowed schemes: {:?}",
117                scheme, self.allowed_schemes
118            ));
119        }
120
121        // Check for path traversal attempts
122        if url.contains("..") {
123            return Err("Path traversal attempts are not allowed".to_string());
124        }
125
126        Ok(())
127    }
128}
129
130/// Combined engine configuration
131#[derive(Debug, Clone, Default, Serialize, Deserialize)]
132pub struct EngineConfig {
133    pub performance: PerformanceConfig,
134    pub security: SecurityConfig,
135}
136
137impl EngineConfig {
138    pub fn new() -> Self {
139        Self::default()
140    }
141
142    pub fn with_performance(mut self, config: PerformanceConfig) -> Self {
143        self.performance = config;
144        self
145    }
146
147    pub fn with_security(mut self, config: SecurityConfig) -> Self {
148        self.security = config;
149        self
150    }
151}
152
153#[cfg(test)]
154mod tests {
155    use super::*;
156
157    #[test]
158    fn test_default_config() {
159        let config = EngineConfig::default();
160        assert_eq!(config.performance.max_memory_mb, 512);
161        assert!(config
162            .security
163            .allowed_schemes
164            .contains(&"https".to_string()));
165    }
166
167    #[test]
168    fn test_minimal_config() {
169        let config = PerformanceConfig::minimal();
170        assert!(config.max_memory_mb < 100);
171        assert!(!config.enable_streaming);
172    }
173
174    #[test]
175    fn test_high_performance_config() {
176        let config = PerformanceConfig::high_performance();
177        assert!(config.max_memory_mb > 1000);
178        assert!(config.max_concurrent_ops > 8);
179    }
180
181    #[test]
182    fn test_strict_security() {
183        let config = SecurityConfig::strict();
184        assert!(config.block_external_resources);
185        assert_eq!(config.allowed_schemes.len(), 1);
186    }
187
188    #[test]
189    fn test_url_validation() {
190        let config = SecurityConfig::default();
191
192        // Valid URLs
193        assert!(config.validate_url("https://example.com/image.jpg").is_ok());
194        assert!(config.validate_url("http://example.com/file").is_ok());
195        assert!(config.validate_url("data:image/png;base64,abc123").is_ok());
196
197        // Invalid scheme
198        assert!(config.validate_url("ftp://example.com/file").is_err());
199
200        // Path traversal
201        assert!(config
202            .validate_url("https://example.com/../etc/passwd")
203            .is_err());
204    }
205
206    #[test]
207    fn test_url_length_limit() {
208        let config = SecurityConfig {
209            max_url_length: 50,
210            ..Default::default()
211        };
212
213        let short_url = "https://example.com/a";
214        let long_url = "https://example.com/".to_string() + &"a".repeat(100);
215
216        assert!(config.validate_url(short_url).is_ok());
217        assert!(config.validate_url(&long_url).is_err());
218    }
219}