use std::collections::VecDeque;
use std::sync::{Arc, Mutex};
use std::time::{Duration, SystemTime};
use serde::{Serialize, Deserialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum AuditSeverity {
Info,
Warning,
Error,
Critical,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum AuditEventType {
ModuleLoaded {
id: String,
size: usize
},
InstanceCreated {
id: String
},
InstanceTerminated {
id: String,
exit_code: Option<i32>
},
FunctionCall {
instance_id: String,
function_name: String
},
ResourceLimit {
instance_id: String,
resource: String,
limit_type: String,
limit: u64,
attempted: u64
},
CapabilityViolation {
instance_id: String,
domain: String,
operation: String
},
HostFunctionCall {
instance_id: String,
function_name: String
},
MemoryAccess {
instance_id: String,
access_type: String,
address: u32,
size: usize
},
Custom {
event_type: String,
data: String
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuditEvent {
pub timestamp: SystemTime,
pub severity: AuditSeverity,
pub event_type: AuditEventType,
pub message: String,
}
#[derive(Debug, Clone)]
pub struct AuditLogger {
events: Arc<Mutex<VecDeque<AuditEvent>>>,
max_events: usize,
log_to_stdout: bool,
log_to_file: bool,
file_path: Option<String>,
}
impl AuditLogger {
pub fn new(max_events: usize) -> Self {
Self {
events: Arc::new(Mutex::new(VecDeque::with_capacity(max_events))),
max_events,
log_to_stdout: false,
log_to_file: false,
file_path: None,
}
}
pub fn with_stdout(mut self) -> Self {
self.log_to_stdout = true;
self
}
pub fn with_file(mut self, file_path: &str) -> Self {
self.log_to_file = true;
self.file_path = Some(file_path.to_string());
self
}
pub fn log(&self, severity: AuditSeverity, event_type: AuditEventType, message: &str) {
let event = AuditEvent {
timestamp: SystemTime::now(),
severity,
event_type,
message: message.to_string(),
};
if self.log_to_stdout {
let timestamp = chrono::DateTime::<chrono::Utc>::from(event.timestamp)
.format("%Y-%m-%d %H:%M:%S%.3f")
.to_string();
let level = match event.severity {
AuditSeverity::Info => "INFO",
AuditSeverity::Warning => "WARN",
AuditSeverity::Error => "ERROR",
AuditSeverity::Critical => "CRITICAL",
};
println!("[{}] {} - {} - {:?}", timestamp, level, event.message, event.event_type);
}
if self.log_to_file {
if let Some(file_path) = &self.file_path {
let json = serde_json::to_string(&event).unwrap_or_else(|_| "{}".to_string());
std::fs::OpenOptions::new()
.create(true)
.append(true)
.open(file_path)
.map(|mut file| {
use std::io::Write;
let _ = writeln!(file, "{}", json);
})
.ok();
}
}
let mut events = self.events.lock().unwrap();
events.push_back(event);
while events.len() > self.max_events {
events.pop_front();
}
}
pub fn info(&self, event_type: AuditEventType, message: &str) {
self.log(AuditSeverity::Info, event_type, message);
}
pub fn warning(&self, event_type: AuditEventType, message: &str) {
self.log(AuditSeverity::Warning, event_type, message);
}
pub fn error(&self, event_type: AuditEventType, message: &str) {
self.log(AuditSeverity::Error, event_type, message);
}
pub fn critical(&self, event_type: AuditEventType, message: &str) {
self.log(AuditSeverity::Critical, event_type, message);
}
pub fn get_events(&self) -> Vec<AuditEvent> {
self.events.lock().unwrap().iter().cloned().collect()
}
pub fn get_events_by_severity(&self, severity: AuditSeverity) -> Vec<AuditEvent> {
self.events.lock().unwrap()
.iter()
.filter(|e| e.severity == severity)
.cloned()
.collect()
}
pub fn get_events_in_range(&self, start: SystemTime, end: SystemTime) -> Vec<AuditEvent> {
self.events.lock().unwrap()
.iter()
.filter(|e| e.timestamp >= start && e.timestamp <= end)
.cloned()
.collect()
}
pub fn clear(&self) {
self.events.lock().unwrap().clear();
}
}
#[derive(Debug, Clone)]
pub struct AuditConfig {
pub enabled: bool,
pub log_to_stdout: bool,
pub log_to_file: bool,
pub file_path: Option<String>,
pub max_events: usize,
pub min_severity: AuditSeverity,
pub log_module_loads: bool,
pub log_instance_creation: bool,
pub log_function_calls: bool,
pub log_resource_limits: bool,
pub log_capability_violations: bool,
}
impl Default for AuditConfig {
fn default() -> Self {
Self {
enabled: true,
log_to_stdout: false,
log_to_file: false,
file_path: None,
max_events: 1000,
min_severity: AuditSeverity::Info,
log_module_loads: true,
log_instance_creation: true,
log_function_calls: true,
log_resource_limits: true,
log_capability_violations: true,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ThreatLevel {
None,
Low,
Medium,
High,
Critical,
}
pub struct SecurityScanner {
logger: AuditLogger,
config: ScanConfig,
}
#[derive(Debug, Clone)]
pub struct ScanConfig {
pub capability_violation_threshold: usize,
pub resource_limit_threshold: usize,
pub scan_interval: Duration,
pub detect_memory_access_patterns: bool,
pub detect_network_access_patterns: bool,
pub detect_filesystem_access_patterns: bool,
}
impl Default for ScanConfig {
fn default() -> Self {
Self {
capability_violation_threshold: 3,
resource_limit_threshold: 5,
scan_interval: Duration::from_secs(60),
detect_memory_access_patterns: true,
detect_network_access_patterns: true,
detect_filesystem_access_patterns: true,
}
}
}
impl SecurityScanner {
pub fn new(logger: AuditLogger, config: ScanConfig) -> Self {
Self {
logger,
config,
}
}
pub fn scan(&self) -> Vec<SecurityThreat> {
let mut threats = Vec::new();
let events = self.logger.get_events();
let capability_violations = events.iter()
.filter(|e| matches!(e.event_type, AuditEventType::CapabilityViolation { .. }))
.count();
if capability_violations >= self.config.capability_violation_threshold {
threats.push(SecurityThreat {
level: ThreatLevel::Medium,
description: format!(
"High number of capability violations detected: {} (threshold: {})",
capability_violations,
self.config.capability_violation_threshold
),
events: events.iter()
.filter(|e| matches!(e.event_type, AuditEventType::CapabilityViolation { .. }))
.cloned()
.collect(),
});
}
let resource_violations = events.iter()
.filter(|e| matches!(e.event_type, AuditEventType::ResourceLimit { .. }))
.count();
if resource_violations >= self.config.resource_limit_threshold {
threats.push(SecurityThreat {
level: ThreatLevel::Medium,
description: format!(
"High number of resource limit violations detected: {} (threshold: {})",
resource_violations,
self.config.resource_limit_threshold
),
events: events.iter()
.filter(|e| matches!(e.event_type, AuditEventType::ResourceLimit { .. }))
.cloned()
.collect(),
});
}
if self.config.detect_memory_access_patterns {
let memory_accesses = events.iter()
.filter(|e| matches!(e.event_type, AuditEventType::MemoryAccess { .. }))
.collect::<Vec<_>>();
let mut suspicious_addresses = std::collections::HashSet::new();
for event in &memory_accesses {
if let AuditEventType::MemoryAccess {
address, size, access_type, ..
} = &event.event_type {
if access_type == "write" && *size > 1024 && *address > 0xFFFF0000 {
suspicious_addresses.insert(*address);
}
}
}
if suspicious_addresses.len() > 2 {
threats.push(SecurityThreat {
level: ThreatLevel::High,
description: format!(
"Potential buffer overflow attempt detected: {} suspicious memory writes",
suspicious_addresses.len()
),
events: memory_accesses.into_iter().cloned().collect(),
});
}
}
threats
}
pub fn start_scanner(&self) -> std::thread::JoinHandle<()> {
let logger = self.logger.clone();
let config = self.config.clone();
std::thread::spawn(move || {
let scanner = SecurityScanner::new(logger.clone(), config);
loop {
std::thread::sleep(scanner.config.scan_interval);
let threats = scanner.scan();
for threat in threats {
logger.log(
match threat.level {
ThreatLevel::None | ThreatLevel::Low => AuditSeverity::Info,
ThreatLevel::Medium => AuditSeverity::Warning,
ThreatLevel::High | ThreatLevel::Critical => AuditSeverity::Critical,
},
AuditEventType::Custom {
event_type: "security_threat".to_string(),
data: threat.description.clone(),
},
&format!("Security threat detected: {}", threat.description),
);
}
}
})
}
}
#[derive(Debug, Clone)]
pub struct SecurityThreat {
pub level: ThreatLevel,
pub description: String,
pub events: Vec<AuditEvent>,
}