oxigdal_security/scanning/
secrets.rs1use crate::scanning::{Finding, ScanResult, ScanType, Severity};
4use regex::Regex;
5
6pub struct SecretScanner {
8 patterns: Vec<(String, Regex)>,
9}
10
11impl SecretScanner {
12 pub fn new() -> Self {
14 let mut scanner = Self {
15 patterns: Vec::new(),
16 };
17 scanner.add_default_patterns();
18 scanner
19 }
20
21 fn add_default_patterns(&mut self) {
22 self.add_pattern("AWS Access Key", r"AKIA[0-9A-Z]{16}");
24
25 self.add_pattern(
27 "Generic API Key",
28 r#"api[_-]?key["']?\s*[:=]\s*["']?([a-zA-Z0-9_-]{32,})"#,
29 );
30
31 self.add_pattern("Private Key", r"-----BEGIN (RSA |EC )?PRIVATE KEY-----");
33 }
34
35 fn add_pattern(&mut self, name: &str, pattern: &str) {
36 if let Ok(regex) = Regex::new(pattern) {
37 self.patterns.push((name.to_string(), regex));
38 }
39 }
40
41 pub fn scan(&self, text: &str) -> ScanResult {
43 let mut findings = Vec::new();
44
45 for (name, regex) in &self.patterns {
46 for mat in regex.find_iter(text) {
47 findings.push(Finding {
48 id: uuid::Uuid::new_v4().to_string(),
49 severity: Severity::Critical,
50 description: format!("Possible {} detected", name),
51 location: Some(format!("Position: {}", mat.start())),
52 });
53 }
54 }
55
56 ScanResult {
57 scan_type: ScanType::Secrets,
58 findings,
59 scanned_at: chrono::Utc::now(),
60 }
61 }
62}
63
64impl Default for SecretScanner {
65 fn default() -> Self {
66 Self::new()
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73
74 #[test]
75 fn test_secret_scanner() {
76 let scanner = SecretScanner::new();
77 let text = "AWS Key: AKIAIOSFODNN7EXAMPLE";
78 let result = scanner.scan(text);
79
80 assert!(!result.findings.is_empty());
81 assert_eq!(result.scan_type, ScanType::Secrets);
82 }
83}