Skip to main content

wasm_sandbox/security/
audit.rs

1//! Security audit and logging
2
3use std::collections::VecDeque;
4use std::sync::{Arc, Mutex};
5use std::time::{Duration, SystemTime};
6
7use serde::{Serialize, Deserialize};
8
9/// Severity level for audit events
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
11pub enum AuditSeverity {
12    /// Informational message
13    Info,
14    
15    /// Warning message
16    Warning,
17    
18    /// Error message
19    Error,
20    
21    /// Critical security event
22    Critical,
23}
24
25/// Type of audit event
26#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
27pub enum AuditEventType {
28    /// Module loaded
29    ModuleLoaded { 
30        /// Module ID
31        id: String, 
32        
33        /// Module size
34        size: usize 
35    },
36    
37    /// Instance created
38    InstanceCreated { 
39        /// Instance ID
40        id: String 
41    },
42    
43    /// Instance terminated
44    InstanceTerminated { 
45        /// Instance ID
46        id: String, 
47        
48        /// Exit code
49        exit_code: Option<i32> 
50    },
51    
52    /// Function called
53    FunctionCall { 
54        /// Instance ID
55        instance_id: String, 
56        
57        /// Function name
58        function_name: String 
59    },
60    
61    /// Resource limit reached
62    ResourceLimit { 
63        /// Instance ID
64        instance_id: String, 
65        
66        /// Resource type
67        resource: String, 
68        
69        /// Limit type
70        limit_type: String, 
71        
72        /// Limit value
73        limit: u64,
74        
75        /// Attempted value
76        attempted: u64 
77    },
78    
79    /// Capability violation
80    CapabilityViolation { 
81        /// Instance ID
82        instance_id: String, 
83        
84        /// Capability domain
85        domain: String, 
86        
87        /// Operation
88        operation: String 
89    },
90    
91    /// Host function call
92    HostFunctionCall { 
93        /// Instance ID
94        instance_id: String, 
95        
96        /// Function name
97        function_name: String 
98    },
99    
100    /// Memory access
101    MemoryAccess { 
102        /// Instance ID
103        instance_id: String, 
104        
105        /// Access type
106        access_type: String, 
107        
108        /// Memory address
109        address: u32, 
110        
111        /// Size in bytes
112        size: usize 
113    },
114    
115    /// Custom event
116    Custom { 
117        /// Event type
118        event_type: String, 
119        
120        /// Event data
121        data: String 
122    },
123}
124
125/// Audit event record
126#[derive(Debug, Clone, Serialize, Deserialize)]
127pub struct AuditEvent {
128    /// Timestamp
129    pub timestamp: SystemTime,
130    
131    /// Severity level
132    pub severity: AuditSeverity,
133    
134    /// Event type
135    pub event_type: AuditEventType,
136    
137    /// Message
138    pub message: String,
139}
140
141/// Audit logger for the sandbox
142#[derive(Debug, Clone)]
143pub struct AuditLogger {
144    /// Log events
145    events: Arc<Mutex<VecDeque<AuditEvent>>>,
146    
147    /// Maximum number of events to keep
148    max_events: usize,
149    
150    /// Whether to log to stdout
151    log_to_stdout: bool,
152    
153    /// Whether to log to a file
154    log_to_file: bool,
155    
156    /// File path for logging
157    file_path: Option<String>,
158}
159
160impl AuditLogger {
161    /// Create a new audit logger
162    pub fn new(max_events: usize) -> Self {
163        Self {
164            events: Arc::new(Mutex::new(VecDeque::with_capacity(max_events))),
165            max_events,
166            log_to_stdout: false,
167            log_to_file: false,
168            file_path: None,
169        }
170    }
171    
172    /// Enable logging to stdout
173    pub fn with_stdout(mut self) -> Self {
174        self.log_to_stdout = true;
175        self
176    }
177    
178    /// Enable logging to a file
179    pub fn with_file(mut self, file_path: &str) -> Self {
180        self.log_to_file = true;
181        self.file_path = Some(file_path.to_string());
182        self
183    }
184    
185    /// Log an event
186    pub fn log(&self, severity: AuditSeverity, event_type: AuditEventType, message: &str) {
187        let event = AuditEvent {
188            timestamp: SystemTime::now(),
189            severity,
190            event_type,
191            message: message.to_string(),
192        };
193        
194        // Log to stdout if enabled
195        if self.log_to_stdout {
196            let timestamp = chrono::DateTime::<chrono::Utc>::from(event.timestamp)
197                .format("%Y-%m-%d %H:%M:%S%.3f")
198                .to_string();
199                
200            let level = match event.severity {
201                AuditSeverity::Info => "INFO",
202                AuditSeverity::Warning => "WARN",
203                AuditSeverity::Error => "ERROR",
204                AuditSeverity::Critical => "CRITICAL",
205            };
206            
207            println!("[{}] {} - {} - {:?}", timestamp, level, event.message, event.event_type);
208        }
209        
210        // Log to file if enabled
211        if self.log_to_file {
212            if let Some(file_path) = &self.file_path {
213                // Open the file in append mode
214                let json = serde_json::to_string(&event).unwrap_or_else(|_| "{}".to_string());
215                
216                // Append to file
217                std::fs::OpenOptions::new()
218                    .create(true)
219                    .append(true)
220                    .open(file_path)
221                    .map(|mut file| {
222                        use std::io::Write;
223                        let _ = writeln!(file, "{}", json);
224                    })
225                    .ok();
226            }
227        }
228        
229        // Store in memory
230        let mut events = self.events.lock().unwrap();
231        events.push_back(event);
232        
233        // Remove oldest events if over capacity
234        while events.len() > self.max_events {
235            events.pop_front();
236        }
237    }
238    
239    /// Log an info event
240    pub fn info(&self, event_type: AuditEventType, message: &str) {
241        self.log(AuditSeverity::Info, event_type, message);
242    }
243    
244    /// Log a warning event
245    pub fn warning(&self, event_type: AuditEventType, message: &str) {
246        self.log(AuditSeverity::Warning, event_type, message);
247    }
248    
249    /// Log an error event
250    pub fn error(&self, event_type: AuditEventType, message: &str) {
251        self.log(AuditSeverity::Error, event_type, message);
252    }
253    
254    /// Log a critical event
255    pub fn critical(&self, event_type: AuditEventType, message: &str) {
256        self.log(AuditSeverity::Critical, event_type, message);
257    }
258    
259    /// Get all events
260    pub fn get_events(&self) -> Vec<AuditEvent> {
261        self.events.lock().unwrap().iter().cloned().collect()
262    }
263    
264    /// Get events by severity
265    pub fn get_events_by_severity(&self, severity: AuditSeverity) -> Vec<AuditEvent> {
266        self.events.lock().unwrap()
267            .iter()
268            .filter(|e| e.severity == severity)
269            .cloned()
270            .collect()
271    }
272    
273    /// Get events in a time range
274    pub fn get_events_in_range(&self, start: SystemTime, end: SystemTime) -> Vec<AuditEvent> {
275        self.events.lock().unwrap()
276            .iter()
277            .filter(|e| e.timestamp >= start && e.timestamp <= end)
278            .cloned()
279            .collect()
280    }
281    
282    /// Clear all events
283    pub fn clear(&self) {
284        self.events.lock().unwrap().clear();
285    }
286}
287
288/// Security audit configuration
289#[derive(Debug, Clone)]
290pub struct AuditConfig {
291    /// Whether to enable auditing
292    pub enabled: bool,
293    
294    /// Log to stdout
295    pub log_to_stdout: bool,
296    
297    /// Log to file
298    pub log_to_file: bool,
299    
300    /// File path for logging
301    pub file_path: Option<String>,
302    
303    /// Maximum number of events to keep in memory
304    pub max_events: usize,
305    
306    /// Minimum severity to log
307    pub min_severity: AuditSeverity,
308    
309    /// Whether to log module loads
310    pub log_module_loads: bool,
311    
312    /// Whether to log instance creation
313    pub log_instance_creation: bool,
314    
315    /// Whether to log function calls
316    pub log_function_calls: bool,
317    
318    /// Whether to log resource limit events
319    pub log_resource_limits: bool,
320    
321    /// Whether to log capability violations
322    pub log_capability_violations: bool,
323}
324
325impl Default for AuditConfig {
326    fn default() -> Self {
327        Self {
328            enabled: true,
329            log_to_stdout: false,
330            log_to_file: false,
331            file_path: None,
332            max_events: 1000,
333            min_severity: AuditSeverity::Info,
334            log_module_loads: true,
335            log_instance_creation: true,
336            log_function_calls: true,
337            log_resource_limits: true,
338            log_capability_violations: true,
339        }
340    }
341}
342
343/// Security threat level
344#[derive(Debug, Clone, Copy, PartialEq, Eq)]
345pub enum ThreatLevel {
346    /// No threat detected
347    None,
348    
349    /// Low threat level
350    Low,
351    
352    /// Medium threat level
353    Medium,
354    
355    /// High threat level
356    High,
357    
358    /// Critical threat level
359    Critical,
360}
361
362/// Security scanner for audit logs
363pub struct SecurityScanner {
364    /// Audit logger
365    logger: AuditLogger,
366    
367    /// Scan configuration
368    config: ScanConfig,
369}
370
371/// Security scan configuration
372#[derive(Debug, Clone)]
373pub struct ScanConfig {
374    /// Threshold for capability violations to trigger a warning
375    pub capability_violation_threshold: usize,
376    
377    /// Threshold for resource limit violations to trigger a warning
378    pub resource_limit_threshold: usize,
379    
380    /// Scan interval
381    pub scan_interval: Duration,
382    
383    /// Whether to detect memory access patterns
384    pub detect_memory_access_patterns: bool,
385    
386    /// Whether to detect network access patterns
387    pub detect_network_access_patterns: bool,
388    
389    /// Whether to detect filesystem access patterns
390    pub detect_filesystem_access_patterns: bool,
391}
392
393impl Default for ScanConfig {
394    fn default() -> Self {
395        Self {
396            capability_violation_threshold: 3,
397            resource_limit_threshold: 5,
398            scan_interval: Duration::from_secs(60),
399            detect_memory_access_patterns: true,
400            detect_network_access_patterns: true,
401            detect_filesystem_access_patterns: true,
402        }
403    }
404}
405
406impl SecurityScanner {
407    /// Create a new security scanner
408    pub fn new(logger: AuditLogger, config: ScanConfig) -> Self {
409        Self {
410            logger,
411            config,
412        }
413    }
414    
415    /// Scan audit logs for security threats
416    pub fn scan(&self) -> Vec<SecurityThreat> {
417        let mut threats = Vec::new();
418        let events = self.logger.get_events();
419        
420        // Count capability violations
421        let capability_violations = events.iter()
422            .filter(|e| matches!(e.event_type, AuditEventType::CapabilityViolation { .. }))
423            .count();
424            
425        if capability_violations >= self.config.capability_violation_threshold {
426            threats.push(SecurityThreat {
427                level: ThreatLevel::Medium,
428                description: format!(
429                    "High number of capability violations detected: {} (threshold: {})",
430                    capability_violations,
431                    self.config.capability_violation_threshold
432                ),
433                events: events.iter()
434                    .filter(|e| matches!(e.event_type, AuditEventType::CapabilityViolation { .. }))
435                    .cloned()
436                    .collect(),
437            });
438        }
439        
440        // Count resource limit violations
441        let resource_violations = events.iter()
442            .filter(|e| matches!(e.event_type, AuditEventType::ResourceLimit { .. }))
443            .count();
444            
445        if resource_violations >= self.config.resource_limit_threshold {
446            threats.push(SecurityThreat {
447                level: ThreatLevel::Medium,
448                description: format!(
449                    "High number of resource limit violations detected: {} (threshold: {})",
450                    resource_violations,
451                    self.config.resource_limit_threshold
452                ),
453                events: events.iter()
454                    .filter(|e| matches!(e.event_type, AuditEventType::ResourceLimit { .. }))
455                    .cloned()
456                    .collect(),
457            });
458        }
459        
460        // Check for memory access patterns
461        if self.config.detect_memory_access_patterns {
462            let memory_accesses = events.iter()
463                .filter(|e| matches!(e.event_type, AuditEventType::MemoryAccess { .. }))
464                .collect::<Vec<_>>();
465                
466            // Detect potential buffer overflow attempts
467            // (This is a simplified heuristic and should be more sophisticated in a real system)
468            let mut suspicious_addresses = std::collections::HashSet::new();
469            for event in &memory_accesses {
470                if let AuditEventType::MemoryAccess { 
471                    address, size, access_type, .. 
472                } = &event.event_type {
473                    if access_type == "write" && *size > 1024 && *address > 0xFFFF0000 {
474                        suspicious_addresses.insert(*address);
475                    }
476                }
477            }
478            
479            if suspicious_addresses.len() > 2 {
480                threats.push(SecurityThreat {
481                    level: ThreatLevel::High,
482                    description: format!(
483                        "Potential buffer overflow attempt detected: {} suspicious memory writes",
484                        suspicious_addresses.len()
485                    ),
486                    events: memory_accesses.into_iter().cloned().collect(),
487                });
488            }
489        }
490        
491        threats
492    }
493    
494    /// Start a background scanning thread
495    pub fn start_scanner(&self) -> std::thread::JoinHandle<()> {
496        let logger = self.logger.clone();
497        let config = self.config.clone();
498        
499        std::thread::spawn(move || {
500            let scanner = SecurityScanner::new(logger.clone(), config);
501            
502            loop {
503                // Sleep for the scan interval
504                std::thread::sleep(scanner.config.scan_interval);
505                
506                // Scan for threats
507                let threats = scanner.scan();
508                
509                // Log threats
510                for threat in threats {
511                    logger.log(
512                        match threat.level {
513                            ThreatLevel::None | ThreatLevel::Low => AuditSeverity::Info,
514                            ThreatLevel::Medium => AuditSeverity::Warning,
515                            ThreatLevel::High | ThreatLevel::Critical => AuditSeverity::Critical,
516                        },
517                        AuditEventType::Custom {
518                            event_type: "security_threat".to_string(),
519                            data: threat.description.clone(),
520                        },
521                        &format!("Security threat detected: {}", threat.description),
522                    );
523                }
524            }
525        })
526    }
527}
528
529/// Security threat detection result
530#[derive(Debug, Clone)]
531pub struct SecurityThreat {
532    /// Threat level
533    pub level: ThreatLevel,
534    
535    /// Description of the threat
536    pub description: String,
537    
538    /// Related audit events
539    pub events: Vec<AuditEvent>,
540}