use anyhow::Result;
use std::time::{SystemTime, UNIX_EPOCH};
use crate::constants::*;
use crate::types::*;
pub struct SecurityValidator;
impl SecurityValidator {
pub fn verify_signature(metadata: &ModuleMetadata) -> Result<bool> {
match &metadata.signature {
Some(sig) => {
let current_time = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs();
if current_time - sig.timestamp > SIGNATURE_EXPIRY_SECONDS {
return Ok(false);
}
if sig.algorithm != DEFAULT_SIGNATURE_ALGORITHM {
return Ok(false);
}
Ok(!sig.signature.is_empty() && !sig.public_key.is_empty())
}
None => Ok(false), }
}
pub fn check_permissions(metadata: &ModuleMetadata, required_permission: &str) -> Result<bool> {
match required_permission {
"filesystem_access" => Ok(metadata.permissions.filesystem_access),
"network_access" => Ok(metadata.permissions.network_access),
"process_spawn" => Ok(metadata.permissions.process_spawn),
"env_access" => Ok(metadata.permissions.env_access),
"system_access" => Ok(metadata.permissions.system_access),
_ => Ok(false),
}
}
pub fn is_approved(metadata: &ModuleMetadata) -> Result<bool> {
Ok(matches!(metadata.review_status, CodeReviewStatus::Approved { .. }))
}
pub fn verify_supply_chain(metadata: &ModuleMetadata) -> Result<bool> {
match &metadata.supply_chain {
Some(chain) => {
if chain.source_url.is_empty() {
return Ok(false);
}
if chain.commit_hash.is_empty() {
return Ok(false);
}
let current_time = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs();
if chain.build_timestamp > current_time {
return Ok(false);
}
Ok(true)
}
None => Ok(false), }
}
pub fn comprehensive_check(metadata: &ModuleMetadata) -> SecurityCheckResult {
let mut issues = Vec::new();
let mut warnings = Vec::new();
match Self::verify_signature(metadata) {
Ok(true) => {
}
Ok(false) => {
issues.push(SecurityIssue {
severity: SecuritySeverity::High,
message: "Module signature verification failed".to_string(),
component: "signature".to_string(),
});
}
Err(e) => {
warnings.push(SecurityWarning {
message: format!("Failed to verify signature: {}", e),
component: "signature".to_string(),
});
}
}
match Self::is_approved(metadata) {
Ok(true) => {
}
Ok(false) => {
issues.push(SecurityIssue {
severity: SecuritySeverity::Medium,
message: "Module not approved by code review".to_string(),
component: "review".to_string(),
});
}
Err(e) => {
warnings.push(SecurityWarning {
message: format!("Failed to check approval status: {}", e),
component: "review".to_string(),
});
}
}
match Self::verify_supply_chain(metadata) {
Ok(true) => {
}
Ok(false) => {
issues.push(SecurityIssue {
severity: SecuritySeverity::Medium,
message: "Supply chain verification failed".to_string(),
component: "supply_chain".to_string(),
});
}
Err(e) => {
warnings.push(SecurityWarning {
message: format!("Failed to verify supply chain: {}", e),
component: "supply_chain".to_string(),
});
}
}
if metadata.permissions.system_access && !metadata.sandbox_config.enabled {
issues.push(SecurityIssue {
severity: SecuritySeverity::High,
message: "System access granted without sandboxing".to_string(),
component: "permissions".to_string(),
});
}
let is_secure = issues.is_empty();
let risk_level = Self::calculate_risk_level(&issues);
SecurityCheckResult {
is_secure,
risk_level,
issues,
warnings,
check_timestamp: SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs(),
}
}
fn calculate_risk_level(issues: &[SecurityIssue]) -> SecurityRiskLevel {
if issues.iter().any(|i| matches!(i.severity, SecuritySeverity::Critical)) {
SecurityRiskLevel::Critical
} else if issues.iter().any(|i| matches!(i.severity, SecuritySeverity::High)) {
SecurityRiskLevel::High
} else if issues.iter().any(|i| matches!(i.severity, SecuritySeverity::Medium)) {
SecurityRiskLevel::Medium
} else if !issues.is_empty() {
SecurityRiskLevel::Low
} else {
SecurityRiskLevel::None
}
}
}
#[derive(Debug, Clone)]
pub struct SecurityCheckResult {
pub is_secure: bool,
pub risk_level: SecurityRiskLevel,
pub issues: Vec<SecurityIssue>,
pub warnings: Vec<SecurityWarning>,
pub check_timestamp: u64,
}
#[derive(Debug, Clone, PartialEq)]
pub enum SecuritySeverity {
Low,
Medium,
High,
Critical,
}
#[derive(Debug, Clone, PartialEq)]
pub enum SecurityRiskLevel {
None,
Low,
Medium,
High,
Critical,
}
#[derive(Debug, Clone)]
pub struct SecurityIssue {
pub severity: SecuritySeverity,
pub message: String,
pub component: String,
}
#[derive(Debug, Clone)]
pub struct SecurityWarning {
pub message: String,
pub component: String,
}
impl SecurityCheckResult {
pub fn summary(&self) -> String {
format!(
"Security check {}: {} issues, {} warnings, risk level: {:?}",
if self.is_secure { "PASSED" } else { "FAILED" },
self.issues.len(),
self.warnings.len(),
self.risk_level
)
}
pub fn has_security_risk(&self) -> bool {
matches!(self.risk_level, SecurityRiskLevel::Medium | SecurityRiskLevel::High | SecurityRiskLevel::Critical)
}
pub fn get_critical_issues(&self) -> Vec<&SecurityIssue> {
self.issues.iter().filter(|i| matches!(i.severity, SecuritySeverity::Critical)).collect()
}
pub fn get_high_severity_issues(&self) -> Vec<&SecurityIssue> {
self.issues.iter().filter(|i| matches!(i.severity, SecuritySeverity::High | SecuritySeverity::Critical)).collect()
}
}