module_registry/
security.rs1use anyhow::Result;
4use std::time::{SystemTime, UNIX_EPOCH};
5
6use crate::constants::*;
7use crate::types::*;
8
9pub struct SecurityValidator;
11
12impl SecurityValidator {
13 pub fn verify_signature(metadata: &ModuleMetadata) -> Result<bool> {
15 match &metadata.signature {
16 Some(sig) => {
17 let current_time = SystemTime::now()
19 .duration_since(UNIX_EPOCH)
20 .unwrap()
21 .as_secs();
22
23 if current_time - sig.timestamp > SIGNATURE_EXPIRY_SECONDS {
24 return Ok(false);
25 }
26
27 if sig.algorithm != DEFAULT_SIGNATURE_ALGORITHM {
29 return Ok(false);
30 }
31
32 Ok(!sig.signature.is_empty() && !sig.public_key.is_empty())
35 }
36 None => Ok(false), }
38 }
39
40 pub fn check_permissions(metadata: &ModuleMetadata, required_permission: &str) -> Result<bool> {
42 match required_permission {
43 "filesystem_access" => Ok(metadata.permissions.filesystem_access),
44 "network_access" => Ok(metadata.permissions.network_access),
45 "process_spawn" => Ok(metadata.permissions.process_spawn),
46 "env_access" => Ok(metadata.permissions.env_access),
47 "system_access" => Ok(metadata.permissions.system_access),
48 _ => Ok(false),
49 }
50 }
51
52 pub fn is_approved(metadata: &ModuleMetadata) -> Result<bool> {
54 Ok(matches!(metadata.review_status, CodeReviewStatus::Approved { .. }))
55 }
56
57 pub fn verify_supply_chain(metadata: &ModuleMetadata) -> Result<bool> {
59 match &metadata.supply_chain {
60 Some(chain) => {
61 if chain.source_url.is_empty() {
63 return Ok(false);
64 }
65
66 if chain.commit_hash.is_empty() {
68 return Ok(false);
69 }
70
71 let current_time = SystemTime::now()
73 .duration_since(UNIX_EPOCH)
74 .unwrap()
75 .as_secs();
76
77 if chain.build_timestamp > current_time {
78 return Ok(false);
79 }
80
81 Ok(true)
83 }
84 None => Ok(false), }
86 }
87
88 pub fn comprehensive_check(metadata: &ModuleMetadata) -> SecurityCheckResult {
90 let mut issues = Vec::new();
91 let mut warnings = Vec::new();
92
93 match Self::verify_signature(metadata) {
95 Ok(true) => {
96 }
98 Ok(false) => {
99 issues.push(SecurityIssue {
100 severity: SecuritySeverity::High,
101 message: "Module signature verification failed".to_string(),
102 component: "signature".to_string(),
103 });
104 }
105 Err(e) => {
106 warnings.push(SecurityWarning {
107 message: format!("Failed to verify signature: {}", e),
108 component: "signature".to_string(),
109 });
110 }
111 }
112
113 match Self::is_approved(metadata) {
115 Ok(true) => {
116 }
118 Ok(false) => {
119 issues.push(SecurityIssue {
120 severity: SecuritySeverity::Medium,
121 message: "Module not approved by code review".to_string(),
122 component: "review".to_string(),
123 });
124 }
125 Err(e) => {
126 warnings.push(SecurityWarning {
127 message: format!("Failed to check approval status: {}", e),
128 component: "review".to_string(),
129 });
130 }
131 }
132
133 match Self::verify_supply_chain(metadata) {
135 Ok(true) => {
136 }
138 Ok(false) => {
139 issues.push(SecurityIssue {
140 severity: SecuritySeverity::Medium,
141 message: "Supply chain verification failed".to_string(),
142 component: "supply_chain".to_string(),
143 });
144 }
145 Err(e) => {
146 warnings.push(SecurityWarning {
147 message: format!("Failed to verify supply chain: {}", e),
148 component: "supply_chain".to_string(),
149 });
150 }
151 }
152
153 if metadata.permissions.system_access && !metadata.sandbox_config.enabled {
155 issues.push(SecurityIssue {
156 severity: SecuritySeverity::High,
157 message: "System access granted without sandboxing".to_string(),
158 component: "permissions".to_string(),
159 });
160 }
161
162 let is_secure = issues.is_empty();
163 let risk_level = Self::calculate_risk_level(&issues);
164
165 SecurityCheckResult {
166 is_secure,
167 risk_level,
168 issues,
169 warnings,
170 check_timestamp: SystemTime::now()
171 .duration_since(UNIX_EPOCH)
172 .unwrap()
173 .as_secs(),
174 }
175 }
176
177 fn calculate_risk_level(issues: &[SecurityIssue]) -> SecurityRiskLevel {
179 if issues.iter().any(|i| matches!(i.severity, SecuritySeverity::Critical)) {
180 SecurityRiskLevel::Critical
181 } else if issues.iter().any(|i| matches!(i.severity, SecuritySeverity::High)) {
182 SecurityRiskLevel::High
183 } else if issues.iter().any(|i| matches!(i.severity, SecuritySeverity::Medium)) {
184 SecurityRiskLevel::Medium
185 } else if !issues.is_empty() {
186 SecurityRiskLevel::Low
187 } else {
188 SecurityRiskLevel::None
189 }
190 }
191}
192
193#[derive(Debug, Clone)]
195pub struct SecurityCheckResult {
196 pub is_secure: bool,
197 pub risk_level: SecurityRiskLevel,
198 pub issues: Vec<SecurityIssue>,
199 pub warnings: Vec<SecurityWarning>,
200 pub check_timestamp: u64,
201}
202
203#[derive(Debug, Clone, PartialEq)]
205pub enum SecuritySeverity {
206 Low,
207 Medium,
208 High,
209 Critical,
210}
211
212#[derive(Debug, Clone, PartialEq)]
214pub enum SecurityRiskLevel {
215 None,
216 Low,
217 Medium,
218 High,
219 Critical,
220}
221
222#[derive(Debug, Clone)]
224pub struct SecurityIssue {
225 pub severity: SecuritySeverity,
226 pub message: String,
227 pub component: String,
228}
229
230#[derive(Debug, Clone)]
232pub struct SecurityWarning {
233 pub message: String,
234 pub component: String,
235}
236
237impl SecurityCheckResult {
238 pub fn summary(&self) -> String {
240 format!(
241 "Security check {}: {} issues, {} warnings, risk level: {:?}",
242 if self.is_secure { "PASSED" } else { "FAILED" },
243 self.issues.len(),
244 self.warnings.len(),
245 self.risk_level
246 )
247 }
248
249 pub fn has_security_risk(&self) -> bool {
251 matches!(self.risk_level, SecurityRiskLevel::Medium | SecurityRiskLevel::High | SecurityRiskLevel::Critical)
252 }
253
254 pub fn get_critical_issues(&self) -> Vec<&SecurityIssue> {
256 self.issues.iter().filter(|i| matches!(i.severity, SecuritySeverity::Critical)).collect()
257 }
258
259 pub fn get_high_severity_issues(&self) -> Vec<&SecurityIssue> {
261 self.issues.iter().filter(|i| matches!(i.severity, SecuritySeverity::High | SecuritySeverity::Critical)).collect()
262 }
263}