wasm_sandbox/security/
mod.rs

1//! Security policies and resource limitations
2
3use std::collections::HashMap;
4use std::path::PathBuf;
5
6pub mod audit;
7pub mod capabilities;
8pub mod resource_limits;
9pub mod audit_impl;
10
11/// Host specification for network access
12#[derive(Debug, Clone, PartialEq, Eq)]
13pub struct HostSpec {
14    /// Hostname or IP address
15    pub host: String,
16    
17    /// Port range (inclusive)
18    pub ports: Option<PortRange>,
19    
20    /// Whether to allow secure connections (HTTPS)
21    pub secure: bool,
22}
23
24/// Port range specification
25#[derive(Debug, Clone, PartialEq, Eq)]
26pub struct PortRange {
27    /// Start port (inclusive)
28    pub start: u16,
29    
30    /// End port (inclusive)
31    pub end: u16,
32}
33
34impl PortRange {
35    /// Create a new port range
36    pub fn new(start: u16, end: u16) -> Self {
37        assert!(start <= end, "Start port must be less than or equal to end port");
38        Self { start, end }
39    }
40    
41    /// Create a single port range
42    pub fn single(port: u16) -> Self {
43        Self {
44            start: port,
45            end: port,
46        }
47    }
48    
49    /// Check if a port is in the range
50    pub fn contains(&self, port: u16) -> bool {
51        port >= self.start && port <= self.end
52    }
53}
54
55/// Network access capabilities
56#[derive(Debug, Clone, PartialEq)]
57pub enum NetworkCapability {
58    /// No network access allowed
59    None,
60    
61    /// Loopback only (localhost)
62    Loopback,
63    
64    /// Specific hosts only
65    AllowedHosts(Vec<HostSpec>),
66    
67    /// Specific ports only
68    AllowedPorts(Vec<PortRange>),
69    
70    /// Full network access
71    Full,
72}
73
74impl Default for NetworkCapability {
75    fn default() -> Self {
76        Self::None
77    }
78}
79
80/// Filesystem access capabilities
81#[derive(Debug, Clone, PartialEq)]
82pub struct FilesystemCapability {
83    /// Directories that can be read from
84    pub readable_dirs: Vec<PathBuf>,
85    
86    /// Directories that can be written to
87    pub writable_dirs: Vec<PathBuf>,
88    
89    /// Maximum file size for writing
90    pub max_file_size: Option<u64>,
91    
92    /// Allow file creation
93    pub allow_create: bool,
94    
95    /// Allow file deletion
96    pub allow_delete: bool,
97}
98
99impl Default for FilesystemCapability {
100    fn default() -> Self {
101        Self {
102            readable_dirs: Vec::new(),
103            writable_dirs: Vec::new(),
104            max_file_size: None,
105            allow_create: false,
106            allow_delete: false,
107        }
108    }
109}
110
111/// Environment variable access capabilities
112#[derive(Debug, Clone, PartialEq)]
113pub enum EnvironmentCapability {
114    /// No environment variable access
115    None,
116    
117    /// Allow only specific variables
118    Allowlist(Vec<String>),
119    
120    /// Deny specific variables
121    Denylist(Vec<String>),
122    
123    /// Full environment variable access
124    Full,
125}
126
127impl Default for EnvironmentCapability {
128    fn default() -> Self {
129        Self::None
130    }
131}
132
133/// Process creation capability
134#[derive(Debug, Clone, PartialEq)]
135pub enum ProcessCapability {
136    /// No process creation allowed
137    None,
138    
139    /// Allow only specific commands
140    AllowedCommands(Vec<String>),
141    
142    /// Full process creation capability
143    Full,
144}
145
146impl Default for ProcessCapability {
147    fn default() -> Self {
148        Self::None
149    }
150}
151
152/// Time access capability
153#[derive(Debug, Clone, PartialEq)]
154pub enum TimeCapability {
155    /// Read-only time access
156    ReadOnly,
157    
158    /// Full time access (can set system time)
159    Full,
160}
161
162impl Default for TimeCapability {
163    fn default() -> Self {
164        Self::ReadOnly
165    }
166}
167
168/// Random number generation capability
169#[derive(Debug, Clone, PartialEq)]
170pub enum RandomCapability {
171    /// No random number generation
172    None,
173    
174    /// Pseudo-random number generation only
175    PseudoOnly,
176    
177    /// Full random number generation (includes secure random)
178    Full,
179}
180
181impl Default for RandomCapability {
182    fn default() -> Self {
183        Self::PseudoOnly
184    }
185}
186
187/// Custom capability type
188#[derive(Debug, Clone, PartialEq)]
189pub enum CustomCapability {
190    /// Boolean capability (enabled/disabled)
191    Boolean(bool),
192    
193    /// Numeric capability with limits
194    Numeric {
195        /// Current value
196        value: i64,
197        /// Minimum allowed value
198        min: i64,
199        /// Maximum allowed value
200        max: i64,
201    },
202    
203    /// String capability
204    String(String),
205    
206    /// String list capability
207    StringList(Vec<String>),
208}
209
210/// Security capabilities for the sandbox
211#[derive(Debug, Clone, PartialEq)]
212pub struct Capabilities {
213    /// Network access permissions
214    pub network: NetworkCapability,
215    
216    /// Filesystem access permissions
217    pub filesystem: FilesystemCapability,
218    
219    /// Environment variable access
220    pub environment: EnvironmentCapability,
221    
222    /// Process creation capability
223    pub process: ProcessCapability,
224    
225    /// Time access capability (for limiting time manipulation)
226    pub time: TimeCapability,
227    
228    /// Random number generation capability
229    pub random: RandomCapability,
230    
231    /// Custom capabilities map
232    pub custom: HashMap<String, CustomCapability>,
233}
234
235impl Capabilities {
236    /// Create capabilities with minimal permissions (most restrictive)
237    pub fn minimal() -> Self {
238        Self {
239            network: NetworkCapability::None,
240            filesystem: FilesystemCapability::default(),
241            environment: EnvironmentCapability::None,
242            process: ProcessCapability::None,
243            time: TimeCapability::ReadOnly,
244            random: RandomCapability::PseudoOnly,
245            custom: HashMap::new(),
246        }
247    }
248    
249    /// Create capabilities for development (least restrictive)
250    pub fn development() -> Self {
251        Self {
252            network: NetworkCapability::Loopback,
253            filesystem: FilesystemCapability {
254                readable_dirs: vec![std::env::current_dir().unwrap_or_default()],
255                writable_dirs: vec![std::env::temp_dir()],
256                max_file_size: Some(10 * 1024 * 1024), // 10MB
257                allow_create: true,
258                allow_delete: false,
259            },
260            environment: EnvironmentCapability::Allowlist(vec![
261                "PATH".to_string(),
262                "TEMP".to_string(),
263                "TMP".to_string(),
264            ]),
265            process: ProcessCapability::None,
266            time: TimeCapability::ReadOnly,
267            random: RandomCapability::Full,
268            custom: HashMap::new(),
269        }
270    }
271    
272    /// Add a custom capability
273    pub fn add_custom(&mut self, name: &str, capability: CustomCapability) {
274        self.custom.insert(name.to_string(), capability);
275    }
276    
277    /// Get a custom capability
278    pub fn get_custom(&self, name: &str) -> Option<&CustomCapability> {
279        self.custom.get(name)
280    }
281}
282
283impl Default for Capabilities {
284    fn default() -> Self {
285        Self::minimal()
286    }
287}
288
289/// Memory resource limits
290#[derive(Debug, Clone)]
291pub struct MemoryLimits {
292    /// Maximum memory pages (64KB each)
293    pub max_memory_pages: u32,
294    
295    /// Reserved memory pages
296    pub reserved_memory_pages: u32,
297    
298    /// Growth rate limiting
299    pub max_growth_rate: Option<u32>,
300    
301    /// Maximum memory addresses
302    pub max_tables: u32,
303}
304
305impl Default for MemoryLimits {
306    fn default() -> Self {
307        Self {
308            max_memory_pages: 160, // 10MB (160 * 64KB)
309            reserved_memory_pages: 16, // 1MB (16 * 64KB)
310            max_growth_rate: Some(10),
311            max_tables: 1,
312        }
313    }
314}
315
316/// CPU resource limits
317#[derive(Debug, Clone)]
318pub struct CpuLimits {
319    /// Maximum execution time in milliseconds
320    pub max_execution_time_ms: u64,
321    
322    /// CPU usage percentage (0-100)
323    pub cpu_usage_percentage: Option<u8>,
324    
325    /// Thread limit
326    pub max_threads: Option<u32>,
327}
328
329impl Default for CpuLimits {
330    fn default() -> Self {
331        Self {
332            max_execution_time_ms: 5000, // 5 seconds
333            cpu_usage_percentage: Some(50), // 50%
334            max_threads: Some(1), // Single-threaded by default
335        }
336    }
337}
338
339/// I/O resource limits
340#[derive(Debug, Clone)]
341pub struct IoLimits {
342    /// Maximum number of open files
343    pub max_open_files: u32,
344    
345    /// Maximum read bytes per second
346    pub max_read_bytes_per_second: Option<u64>,
347    
348    /// Maximum write bytes per second
349    pub max_write_bytes_per_second: Option<u64>,
350    
351    /// Maximum total read bytes
352    pub max_total_read_bytes: Option<u64>,
353    
354    /// Maximum total write bytes
355    pub max_total_write_bytes: Option<u64>,
356}
357
358impl Default for IoLimits {
359    fn default() -> Self {
360        Self {
361            max_open_files: 10,
362            max_read_bytes_per_second: Some(1024 * 1024), // 1MB/s
363            max_write_bytes_per_second: Some(1024 * 1024), // 1MB/s
364            max_total_read_bytes: Some(10 * 1024 * 1024), // 10MB
365            max_total_write_bytes: Some(5 * 1024 * 1024), // 5MB
366        }
367    }
368}
369
370/// Time limits
371#[derive(Debug, Clone)]
372pub struct TimeLimits {
373    /// Maximum execution time in milliseconds
374    pub max_total_time_ms: u64,
375    
376    /// Maximum idle time in milliseconds
377    pub max_idle_time_ms: Option<u64>,
378}
379
380impl Default for TimeLimits {
381    fn default() -> Self {
382        Self {
383            max_total_time_ms: 30000, // 30 seconds
384            max_idle_time_ms: Some(5000), // 5 seconds
385        }
386    }
387}
388
389/// Resource limits for the sandbox
390#[derive(Debug, Clone)]
391pub struct ResourceLimits {
392    /// Memory limits in bytes
393    pub memory: MemoryLimits,
394    
395    /// CPU limits
396    pub cpu: CpuLimits,
397    
398    /// I/O limits
399    pub io: IoLimits,
400    
401    /// Time limits
402    pub time: TimeLimits,
403    
404    /// Wasmtime fuel limits (instruction counting)
405    pub fuel: Option<u64>,
406}
407
408impl Default for ResourceLimits {
409    fn default() -> Self {
410        Self {
411            memory: MemoryLimits::default(),
412            cpu: CpuLimits::default(),
413            io: IoLimits::default(),
414            time: TimeLimits::default(),
415            fuel: Some(10_000_000), // 10M instructions by default
416        }
417    }
418}