1use crate::error::{NetwatchError, Result};
7use std::collections::HashMap;
8use std::time::{Duration, Instant};
9
10#[derive(Debug, Clone, PartialEq)]
12pub enum SecurityEvent {
13 InvalidInput {
15 input_type: String,
16 attempted_value: String,
17 source: String,
18 },
19 SuspiciousFileAccess { path: String, access_type: String },
21 RateLimitExceeded { source: String, attempt_count: u32 },
23 ConfigTampering {
25 config_field: String,
26 old_value: String,
27 new_value: String,
28 },
29 ResourceExhaustion {
31 resource_type: String,
32 usage_amount: u64,
33 limit: u64,
34 },
35}
36
37pub struct SecurityMonitor {
39 events: Vec<(Instant, SecurityEvent)>,
40 event_counts: HashMap<String, u32>,
41 rate_limits: HashMap<String, (Instant, u32)>,
42 max_events: usize,
43 event_cursor: usize, last_cleanup: Instant,
45 high_performance_mode: bool,
46 events_this_second: u32,
47 current_second: u64,
48}
49
50impl SecurityMonitor {
51 pub fn new() -> Self {
53 let now = Instant::now();
54 Self {
55 events: Vec::with_capacity(1000),
56 event_counts: HashMap::new(),
57 rate_limits: HashMap::new(),
58 max_events: 1000, event_cursor: 0,
60 last_cleanup: now,
61 high_performance_mode: false,
62 events_this_second: 0,
63 current_second: now.elapsed().as_secs(),
64 }
65 }
66
67 pub fn set_high_performance_mode(&mut self, enabled: bool) {
69 self.high_performance_mode = enabled;
70 if enabled {
71 self.max_events = 500;
73 if self.events.capacity() > self.max_events {
75 self.events.truncate(self.max_events);
76 self.events.shrink_to_fit();
77 }
78 }
79 }
80
81 pub fn record_event(&mut self, event: SecurityEvent) {
83 let now = Instant::now();
84 let current_second = now.elapsed().as_secs();
85
86 if current_second != self.current_second {
88 self.current_second = current_second;
89 self.events_this_second = 0;
90 }
91
92 if self.high_performance_mode {
94 self.events_this_second += 1;
95
96 if self.events_this_second > 100 && !self.is_critical_event(&event) {
98 return;
99 }
100
101 if self.events_this_second > 500 {
103 return;
104 }
105 }
106
107 if self.events.len() < self.max_events {
109 self.events.push((now, event.clone()));
110 } else {
111 self.events[self.event_cursor] = (now, event.clone());
113 self.event_cursor = (self.event_cursor + 1) % self.max_events;
114 }
115
116 if !self.high_performance_mode || self.is_critical_event(&event) {
118 let event_key = self.event_key(&event);
119 *self.event_counts.entry(event_key).or_insert(0) += 1;
120 }
121
122 if self.is_critical_event(&event) {
124 eprintln!("SECURITY ALERT: {event:?}");
125 }
126
127 if now.duration_since(self.last_cleanup) > Duration::from_secs(300) {
129 self.cleanup_old_data(now);
130 }
131 }
132
133 fn cleanup_old_data(&mut self, now: Instant) {
135 self.last_cleanup = now;
136
137 if self.high_performance_mode && self.event_counts.len() > 100 {
139 let keys_to_remove: Vec<String> = self
140 .event_counts
141 .iter()
142 .filter(|(_, &count)| count < 5) .map(|(key, _)| key.clone())
144 .collect();
145
146 for key in keys_to_remove {
147 self.event_counts.remove(&key);
148 }
149 }
150
151 let cutoff = now - Duration::from_secs(120); self.rate_limits.retain(|_, (time, _)| *time > cutoff);
154 }
155
156 pub fn check_rate_limit(&mut self, source: &str, max_per_minute: u32) -> Result<()> {
158 let now = Instant::now();
159 let key = format!("rate_limit_{source}");
160
161 match self.rate_limits.get_mut(&key) {
162 Some((last_reset, count)) => {
163 if now.duration_since(*last_reset) > Duration::from_secs(60) {
164 *last_reset = now;
166 *count = 1;
167 } else {
168 *count += 1;
169 if *count > max_per_minute {
170 return Err(NetwatchError::Security(format!(
171 "Rate limit exceeded for source: {source}"
172 )));
173 }
174 }
175 }
176 None => {
177 self.rate_limits.insert(key, (now, 1));
178 }
179 }
180
181 Ok(())
182 }
183
184 pub fn get_statistics(&self) -> SecurityStatistics {
186 if self.high_performance_mode {
188 return SecurityStatistics {
189 total_events: self.events.len(),
190 events_last_hour: self.events.len().min(100), events_last_day: self.events.len(),
192 critical_events: 0, event_types: HashMap::new(), };
195 }
196
197 let now = Instant::now();
199 let last_hour = now - Duration::from_secs(3600);
200 let last_day = now - Duration::from_secs(86400);
201
202 let mut events_last_hour = 0;
203 let mut events_last_day = 0;
204 let mut critical_events = 0;
205
206 for (time, event) in &self.events {
208 if *time > last_day {
209 events_last_day += 1;
210 if *time > last_hour {
211 events_last_hour += 1;
212 }
213 }
214 if self.is_critical_event(event) {
215 critical_events += 1;
216 }
217 }
218
219 SecurityStatistics {
220 total_events: self.events.len(),
221 events_last_hour,
222 events_last_day,
223 critical_events,
224 event_types: self.event_counts.clone(),
225 }
226 }
227
228 pub fn check_anomalies(&self) -> Vec<SecurityAnomaly> {
230 if self.high_performance_mode {
232 return Vec::new();
233 }
234
235 let mut anomalies = Vec::new();
236 let now = Instant::now();
237 let last_minute = now - Duration::from_secs(60);
238
239 let mut recent_events = 0;
240 let mut invalid_input_sources = HashMap::new();
241
242 for (time, event) in &self.events {
244 if *time > last_minute {
245 recent_events += 1;
246
247 if let SecurityEvent::InvalidInput { source, .. } = event {
249 *invalid_input_sources.entry(source.clone()).or_insert(0) += 1;
250 }
251 }
252 }
253
254 if recent_events > 10 {
256 anomalies.push(SecurityAnomaly::EventBurst {
257 event_count: recent_events,
258 time_window: Duration::from_secs(60),
259 });
260 }
261
262 for (source, count) in invalid_input_sources {
263 if count > 3 {
264 anomalies.push(SecurityAnomaly::RepeatedInvalidInput {
265 source,
266 attempt_count: count,
267 });
268 }
269 }
270
271 anomalies
272 }
273
274 fn event_key(&self, event: &SecurityEvent) -> String {
275 match event {
276 SecurityEvent::InvalidInput { input_type, .. } => format!("invalid_input_{input_type}"),
277 SecurityEvent::SuspiciousFileAccess { .. } => "suspicious_file_access".to_string(),
278 SecurityEvent::RateLimitExceeded { .. } => "rate_limit_exceeded".to_string(),
279 SecurityEvent::ConfigTampering { .. } => "config_tampering".to_string(),
280 SecurityEvent::ResourceExhaustion { .. } => "resource_exhaustion".to_string(),
281 }
282 }
283
284 fn is_critical_event(&self, event: &SecurityEvent) -> bool {
285 matches!(
286 event,
287 SecurityEvent::ConfigTampering { .. }
288 | SecurityEvent::SuspiciousFileAccess { .. }
289 | SecurityEvent::ResourceExhaustion { .. }
290 )
291 }
292}
293
294impl Default for SecurityMonitor {
295 fn default() -> Self {
296 Self::new()
297 }
298}
299
300#[derive(Debug, Clone)]
302pub struct SecurityStatistics {
303 pub total_events: usize,
304 pub events_last_hour: usize,
305 pub events_last_day: usize,
306 pub critical_events: usize,
307 pub event_types: HashMap<String, u32>,
308}
309
310#[derive(Debug, Clone)]
312pub enum SecurityAnomaly {
313 EventBurst {
315 event_count: usize,
316 time_window: Duration,
317 },
318 RepeatedInvalidInput { source: String, attempt_count: u32 },
320 UnusualAccessPattern {
322 pattern_description: String,
323 confidence: f32,
324 },
325}
326
327static mut SECURITY_MONITOR: Option<SecurityMonitor> = None;
329static mut MONITOR_INITIALIZED: bool = false;
330
331pub fn init_security_monitor() {
333 unsafe {
334 if !MONITOR_INITIALIZED {
335 SECURITY_MONITOR = Some(SecurityMonitor::new());
336 MONITOR_INITIALIZED = true;
337 }
338 }
339}
340
341pub fn enable_high_performance_security(enabled: bool) {
343 unsafe {
344 if let Some(ref mut monitor) = SECURITY_MONITOR {
345 monitor.set_high_performance_mode(enabled);
346 }
347 }
348}
349
350pub fn record_security_event(event: SecurityEvent) {
352 unsafe {
353 if let Some(ref mut monitor) = SECURITY_MONITOR {
354 monitor.record_event(event);
355 }
356 }
357}
358
359pub fn check_security_rate_limit(source: &str, max_per_minute: u32) -> Result<()> {
361 unsafe {
362 if let Some(ref mut monitor) = SECURITY_MONITOR {
363 monitor.check_rate_limit(source, max_per_minute)
364 } else {
365 Ok(())
366 }
367 }
368}
369
370#[allow(static_mut_refs)]
372pub fn get_security_statistics() -> Option<SecurityStatistics> {
373 unsafe {
374 SECURITY_MONITOR
375 .as_ref()
376 .map(|monitor| monitor.get_statistics())
377 }
378}
379
380#[allow(static_mut_refs)]
382pub fn check_security_anomalies() -> Vec<SecurityAnomaly> {
383 unsafe {
384 SECURITY_MONITOR
385 .as_ref()
386 .map(|monitor| monitor.check_anomalies())
387 .unwrap_or_default()
388 }
389}
390
391#[cfg(test)]
392mod tests {
393 use super::*;
394
395 #[test]
396 fn test_security_event_recording() {
397 let mut monitor = SecurityMonitor::new();
398
399 let event = SecurityEvent::InvalidInput {
400 input_type: "interface_name".to_string(),
401 attempted_value: "../etc/passwd".to_string(),
402 source: "cli".to_string(),
403 };
404
405 monitor.record_event(event);
406
407 let stats = monitor.get_statistics();
408 assert_eq!(stats.total_events, 1);
409 assert_eq!(
410 stats.event_types.get("invalid_input_interface_name"),
411 Some(&1)
412 );
413 }
414
415 #[test]
416 fn test_rate_limiting() {
417 let mut monitor = SecurityMonitor::new();
418
419 for _i in 0..3 {
421 assert!(monitor.check_rate_limit("test_source", 5).is_ok());
422 }
423
424 assert!(monitor.check_rate_limit("test_source", 5).is_ok());
426 assert!(monitor.check_rate_limit("test_source", 5).is_ok());
427
428 assert!(monitor.check_rate_limit("test_source", 5).is_err());
430 }
431
432 #[test]
433 fn test_anomaly_detection() {
434 let mut monitor = SecurityMonitor::new();
435
436 for i in 0..15 {
438 let event = SecurityEvent::InvalidInput {
439 input_type: "test".to_string(),
440 attempted_value: format!("attack_{i}"),
441 source: "attacker".to_string(),
442 };
443 monitor.record_event(event);
444 }
445
446 let anomalies = monitor.check_anomalies();
447 assert!(!anomalies.is_empty());
448
449 assert!(anomalies
451 .iter()
452 .any(|a| matches!(a, SecurityAnomaly::EventBurst { .. })));
453
454 assert!(anomalies
456 .iter()
457 .any(|a| matches!(a, SecurityAnomaly::RepeatedInvalidInput { .. })));
458 }
459
460 #[test]
461 fn test_critical_event_detection() {
462 let mut monitor = SecurityMonitor::new();
463
464 let critical_event = SecurityEvent::ConfigTampering {
465 config_field: "log_file".to_string(),
466 old_value: "/tmp/netwatch.log".to_string(),
467 new_value: "/etc/passwd".to_string(),
468 };
469
470 monitor.record_event(critical_event);
471
472 let stats = monitor.get_statistics();
473 assert_eq!(stats.critical_events, 1);
474 }
475}