Skip to main content

vtcode_core/metrics/
security_metrics.rs

1use chrono::{DateTime, Utc};
2use hashbrown::HashMap;
3use serde::{Deserialize, Serialize};
4use std::collections::VecDeque;
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct SecurityMetrics {
8    pub pii_detections: u64,
9    pub tokens_created: u64,
10    pub pattern_distribution: HashMap<String, u64>,
11    pub audit_events: VecDeque<AuditEvent>,
12}
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct AuditEvent {
16    pub event_type: String,
17    pub pattern_type: String,
18    pub severity: String,
19    pub timestamp: DateTime<Utc>,
20}
21
22impl SecurityMetrics {
23    pub fn new() -> Self {
24        Self {
25            pii_detections: 0,
26            tokens_created: 0,
27            pattern_distribution: HashMap::new(),
28            audit_events: VecDeque::with_capacity(100),
29        }
30    }
31
32    pub fn record_detection(&mut self, pattern_type: String) {
33        self.pii_detections += 1;
34        *self.pattern_distribution.entry(pattern_type).or_insert(0) += 1;
35    }
36
37    pub fn record_tokenization(&mut self, token_count: usize) {
38        self.tokens_created += token_count as u64;
39    }
40
41    pub fn record_audit_event(&mut self, event_type: String, severity: String) {
42        let event = AuditEvent {
43            event_type,
44            pattern_type: String::new(),
45            severity,
46            timestamp: Utc::now(),
47        };
48
49        if self.audit_events.len() >= 100 {
50            self.audit_events.pop_front();
51        }
52        self.audit_events.push_back(event);
53    }
54
55    pub fn detection_rate(&self) -> f64 {
56        if !self.audit_events.is_empty() {
57            self.pii_detections as f64 / self.audit_events.len() as f64
58        } else {
59            0.0
60        }
61    }
62
63    pub fn get_pattern_count(&self, pattern_type: &str) -> u64 {
64        self.pattern_distribution
65            .get(pattern_type)
66            .copied()
67            .unwrap_or(0)
68    }
69
70    pub fn get_most_detected_pattern(&self) -> Option<&str> {
71        self.pattern_distribution
72            .iter()
73            .max_by_key(|(_, count)| *count)
74            .map(|(name, _)| name.as_str())
75    }
76
77    pub fn get_high_severity_events(&self) -> Vec<AuditEvent> {
78        self.audit_events
79            .iter()
80            .filter(|e| e.severity == "high" || e.severity == "critical")
81            .cloned()
82            .collect()
83    }
84}
85
86impl Default for SecurityMetrics {
87    fn default() -> Self {
88        Self::new()
89    }
90}
91
92#[cfg(test)]
93mod tests {
94    use super::*;
95
96    #[test]
97    fn test_record_detection() {
98        let mut metrics = SecurityMetrics::new();
99        metrics.record_detection("email".to_owned());
100        metrics.record_detection("ssn".to_owned());
101        metrics.record_detection("email".to_owned());
102
103        assert_eq!(metrics.pii_detections, 3);
104        assert_eq!(metrics.get_pattern_count("email"), 2);
105        assert_eq!(metrics.get_pattern_count("ssn"), 1);
106    }
107
108    #[test]
109    fn test_record_tokenization() {
110        let mut metrics = SecurityMetrics::new();
111        metrics.record_tokenization(5);
112        metrics.record_tokenization(3);
113
114        assert_eq!(metrics.tokens_created, 8);
115    }
116
117    #[test]
118    fn test_record_audit_event() {
119        let mut metrics = SecurityMetrics::new();
120        metrics.record_audit_event("pii_detected".to_owned(), "info".to_owned());
121        metrics.record_audit_event("tokenized".to_owned(), "info".to_owned());
122
123        assert_eq!(metrics.audit_events.len(), 2);
124    }
125
126    #[test]
127    fn test_detection_rate() {
128        let mut metrics = SecurityMetrics::new();
129        metrics.record_detection("email".to_owned());
130        metrics.record_detection("email".to_owned());
131        metrics.record_audit_event("test".to_owned(), "info".to_owned());
132
133        let rate = metrics.detection_rate();
134        assert!(rate > 0.0);
135    }
136
137    #[test]
138    fn test_most_detected_pattern() {
139        let mut metrics = SecurityMetrics::new();
140        metrics.record_detection("email".to_owned());
141        metrics.record_detection("email".to_owned());
142        metrics.record_detection("email".to_owned());
143        metrics.record_detection("ssn".to_owned());
144
145        assert_eq!(metrics.get_most_detected_pattern(), Some("email"));
146    }
147
148    #[test]
149    fn test_high_severity_events() {
150        let mut metrics = SecurityMetrics::new();
151        metrics.record_audit_event("event1".to_owned(), "low".to_owned());
152        metrics.record_audit_event("event2".to_owned(), "high".to_owned());
153        metrics.record_audit_event("event3".to_owned(), "critical".to_owned());
154        metrics.record_audit_event("event4".to_owned(), "info".to_owned());
155
156        let high_severity = metrics.get_high_severity_events();
157        assert_eq!(high_severity.len(), 2);
158    }
159
160    #[test]
161    fn test_audit_events_limit() {
162        let mut metrics = SecurityMetrics::new();
163        for i in 0..150 {
164            metrics.record_audit_event(format!("event_{}", i), "info".to_owned());
165        }
166
167        assert_eq!(metrics.audit_events.len(), 100);
168    }
169}