1use anyhow::Result;
4use regex::Regex;
5use serde_json::Value;
6use std::collections::{HashMap, HashSet};
7
8#[derive(Debug, Clone)]
10pub struct SecurityVulnerability {
11 pub vulnerability_type: String,
12 pub severity: String,
13 pub description: String,
14 pub location: Option<String>,
15 pub recommendation: String,
16 pub cvss_score: Option<f32>,
17 pub owasp_category: Option<String>,
18 pub confidence: f32,
19 pub file_path: Option<String>,
20 pub line_number: Option<usize>,
21}
22
23#[derive(Debug, Clone)]
25pub struct CvssScore {
26 pub base_score: f32,
27 pub impact_score: f32,
28 pub exploitability_score: f32,
29 pub severity_level: String,
30}
31
32pub struct SecurityAnalyzer {
34 patterns: HashMap<String, Vec<VulnerabilityPattern>>,
35}
36
37#[derive(Debug, Clone)]
38pub struct VulnerabilityPattern {
39 name: String,
40 pattern: Regex,
41 severity: String,
42 description: String,
43 recommendation: String,
44 cvss_base_score: f32,
45 owasp_category: Option<String>,
46 confidence: f32,
47}
48
49impl SecurityAnalyzer {
50 pub fn new() -> Self {
51 let mut analyzer = Self {
52 patterns: HashMap::new(),
53 };
54 analyzer.initialize_patterns();
55 analyzer
56 }
57
58 fn initialize_patterns(&mut self) {
59 let sql_patterns = vec![
61 VulnerabilityPattern {
62 name: "SQL Injection".to_string(),
63 pattern: Regex::new(r#"(?i)(query|execute|exec)\s*\([^)]*\+[^)]*\)"#).unwrap(),
64 severity: "high".to_string(),
65 description: "Potential SQL injection vulnerability detected".to_string(),
66 recommendation: "Use parameterized queries or prepared statements".to_string(),
67 cvss_base_score: 8.1,
68 owasp_category: Some("A03:2021 – Injection".to_string()),
69 confidence: 0.8,
70 },
71 VulnerabilityPattern {
72 name: "SQL Injection Format".to_string(),
73 pattern: Regex::new(
74 r#"(?i)(query|execute|exec)\s*\(\s*['""][^'"]*%[sd][^'"]*['""]"#,
75 )
76 .unwrap(),
77 severity: "high".to_string(),
78 description: "SQL query using string formatting detected".to_string(),
79 recommendation: "Use parameterized queries instead of string formatting"
80 .to_string(),
81 cvss_base_score: 8.1,
82 owasp_category: Some("A03:2021 – Injection".to_string()),
83 confidence: 0.9,
84 },
85 ];
86 self.patterns.insert("injection".to_string(), sql_patterns);
87
88 let xss_patterns = vec![
90 VulnerabilityPattern {
91 name: "XSS via innerHTML".to_string(),
92 pattern: Regex::new(r#"(?i)\.innerHTML\s*=\s*[^;]*\+[^;]*"#).unwrap(),
93 severity: "high".to_string(),
94 description: "Potential XSS vulnerability through innerHTML assignment".to_string(),
95 recommendation: "Use textContent or properly sanitize HTML content".to_string(),
96 cvss_base_score: 7.5,
97 owasp_category: Some("A07:2021 – Cross-Site Scripting (XSS)".to_string()),
98 confidence: 0.8,
99 },
100 VulnerabilityPattern {
101 name: "XSS via document.write".to_string(),
102 pattern: Regex::new(r#"(?i)document\.write\s*\([^)]*\+[^)]*\)"#).unwrap(),
103 severity: "high".to_string(),
104 description: "Potential XSS vulnerability through document.write".to_string(),
105 recommendation: "Avoid document.write, use DOM manipulation methods".to_string(),
106 cvss_base_score: 7.5,
107 owasp_category: Some("A07:2021 – Cross-Site Scripting (XSS)".to_string()),
108 confidence: 0.9,
109 },
110 VulnerabilityPattern {
111 name: "XSS via eval".to_string(),
112 pattern: Regex::new(r#"(?i)eval\s*\([^)]*\+[^)]*\)"#).unwrap(),
113 severity: "critical".to_string(),
114 description: "Critical XSS vulnerability through eval function".to_string(),
115 recommendation: "Never use eval with user input, use JSON.parse for data"
116 .to_string(),
117 cvss_base_score: 9.3,
118 owasp_category: Some("A07:2021 – Cross-Site Scripting (XSS)".to_string()),
119 confidence: 0.95,
120 },
121 ];
122 self.patterns.insert("xss".to_string(), xss_patterns);
123
124 let csrf_patterns = vec![
126 VulnerabilityPattern {
127 name: "Missing CSRF Token".to_string(),
128 pattern: Regex::new(r#"(?i)<form[^>]*method\s*=\s*['""]post['""][^>]*>"#).unwrap(),
129 severity: "medium".to_string(),
130 description: "POST form without visible CSRF protection".to_string(),
131 recommendation: "Implement CSRF tokens for all state-changing operations"
132 .to_string(),
133 cvss_base_score: 6.5,
134 owasp_category: Some("A01:2021 – Broken Access Control".to_string()),
135 confidence: 0.6,
136 },
137 VulnerabilityPattern {
138 name: "AJAX without CSRF".to_string(),
139 pattern: Regex::new(
140 r#"(?i)\$\.post\s*\([^)]*\)|fetch\s*\([^)]*method:\s*['""]POST['""]"#,
141 )
142 .unwrap(),
143 severity: "medium".to_string(),
144 description: "AJAX POST request without visible CSRF protection".to_string(),
145 recommendation: "Include CSRF tokens in AJAX requests headers".to_string(),
146 cvss_base_score: 6.5,
147 owasp_category: Some("A01:2021 – Broken Access Control".to_string()),
148 confidence: 0.5,
149 },
150 ];
151 self.patterns.insert("csrf".to_string(), csrf_patterns);
152
153 let auth_patterns = vec![
155 VulnerabilityPattern {
156 name: "Hardcoded Password".to_string(),
157 pattern: Regex::new(r#"(?i)(password|pwd|passwd)\s*=\s*['""][^'""]{3,}['""]"#)
158 .unwrap(),
159 severity: "critical".to_string(),
160 description: "Hardcoded password detected".to_string(),
161 recommendation:
162 "Store passwords securely using environment variables or secure vaults"
163 .to_string(),
164 cvss_base_score: 9.1,
165 owasp_category: Some(
166 "A07:2021 – Identification and Authentication Failures".to_string(),
167 ),
168 confidence: 0.9,
169 },
170 VulnerabilityPattern {
171 name: "Weak Password Check".to_string(),
172 pattern: Regex::new(r#"(?i)len\s*\(\s*password\s*\)\s*[<>=]\s*[1-5]"#).unwrap(),
173 severity: "medium".to_string(),
174 description: "Weak password length requirement detected".to_string(),
175 recommendation: "Enforce stronger password requirements (minimum 8 characters)"
176 .to_string(),
177 cvss_base_score: 5.3,
178 owasp_category: Some(
179 "A07:2021 – Identification and Authentication Failures".to_string(),
180 ),
181 confidence: 0.8,
182 },
183 VulnerabilityPattern {
184 name: "Hardcoded API Key".to_string(),
185 pattern: Regex::new(
186 r#"(?i)(api_key|apikey|access_key)\s*=\s*['""][a-zA-Z0-9]{16,}['""]"#,
187 )
188 .unwrap(),
189 severity: "critical".to_string(),
190 description: "Hardcoded API key detected".to_string(),
191 recommendation: "Store API keys in environment variables or secure configuration"
192 .to_string(),
193 cvss_base_score: 9.1,
194 owasp_category: Some("A02:2021 – Cryptographic Failures".to_string()),
195 confidence: 0.95,
196 },
197 ];
198 self.patterns
199 .insert("authentication".to_string(), auth_patterns);
200
201 let crypto_patterns = vec![
203 VulnerabilityPattern {
204 name: "Weak Crypto Algorithm".to_string(),
205 pattern: Regex::new(r#"(?i)(md5|sha1|des|rc4)\s*\("#).unwrap(),
206 severity: "high".to_string(),
207 description: "Weak cryptographic algorithm detected".to_string(),
208 recommendation: "Use stronger algorithms like SHA-256, AES, or bcrypt".to_string(),
209 cvss_base_score: 7.4,
210 owasp_category: Some("A02:2021 – Cryptographic Failures".to_string()),
211 confidence: 0.9,
212 },
213 VulnerabilityPattern {
214 name: "Hardcoded Crypto Key".to_string(),
215 pattern: Regex::new(r#"(?i)(key|secret|token)\s*=\s*['""][a-fA-F0-9]{16,}['""]"#)
216 .unwrap(),
217 severity: "critical".to_string(),
218 description: "Hardcoded cryptographic key detected".to_string(),
219 recommendation: "Store keys securely using key management systems".to_string(),
220 cvss_base_score: 9.8,
221 owasp_category: Some("A02:2021 – Cryptographic Failures".to_string()),
222 confidence: 0.9,
223 },
224 VulnerabilityPattern {
225 name: "Weak Random Number Generation".to_string(),
226 pattern: Regex::new(r#"(?i)(Math\.random|random\.randint)\s*\("#).unwrap(),
227 severity: "medium".to_string(),
228 description: "Weak random number generation for security purposes".to_string(),
229 recommendation: "Use cryptographically secure random number generators".to_string(),
230 cvss_base_score: 5.9,
231 owasp_category: Some("A02:2021 – Cryptographic Failures".to_string()),
232 confidence: 0.7,
233 },
234 ];
235 self.patterns.insert("crypto".to_string(), crypto_patterns);
236
237 let exposure_patterns = vec![
239 VulnerabilityPattern {
240 name: "Debug Information Exposure".to_string(),
241 pattern: Regex::new(r#"(?i)(print|console\.log|debug|trace)\s*\([^)]*(?:password|token|key|secret)"#).unwrap(),
242 severity: "medium".to_string(),
243 description: "Sensitive information in debug output detected".to_string(),
244 recommendation: "Remove debug statements containing sensitive data".to_string(),
245 cvss_base_score: 5.3,
246 owasp_category: Some("A09:2021 – Security Logging and Monitoring Failures".to_string()),
247 confidence: 0.8,
248 },
249 VulnerabilityPattern {
250 name: "Error Information Disclosure".to_string(),
251 pattern: Regex::new(r#"(?i)except\s+\w+\s+as\s+\w+:\s*print\s*\(\s*\w+"#).unwrap(),
252 severity: "low".to_string(),
253 description: "Exception details exposed to user".to_string(),
254 recommendation: "Log errors securely without exposing internal details".to_string(),
255 cvss_base_score: 3.7,
256 owasp_category: Some("A09:2021 – Security Logging and Monitoring Failures".to_string()),
257 confidence: 0.6,
258 },
259 VulnerabilityPattern {
260 name: "Sensitive Data in URL".to_string(),
261 pattern: Regex::new(r#"(?i)(password|token|key|secret)=[^&\s]+"#).unwrap(),
262 severity: "high".to_string(),
263 description: "Sensitive information exposed in URL parameters".to_string(),
264 recommendation: "Use POST requests or secure headers for sensitive data".to_string(),
265 cvss_base_score: 7.5,
266 owasp_category: Some("A02:2021 – Cryptographic Failures".to_string()),
267 confidence: 0.9,
268 },
269 ];
270 self.patterns
271 .insert("data_exposure".to_string(), exposure_patterns);
272
273 let unsafe_patterns = vec![
275 VulnerabilityPattern {
276 name: "Command Injection".to_string(),
277 pattern: Regex::new(r#"(?i)(system|exec|popen|subprocess)\s*\([^)]*\+[^)]*\)"#)
278 .unwrap(),
279 severity: "critical".to_string(),
280 description: "Potential command injection vulnerability detected".to_string(),
281 recommendation: "Validate and sanitize input, use safe alternatives".to_string(),
282 cvss_base_score: 9.8,
283 owasp_category: Some("A03:2021 – Injection".to_string()),
284 confidence: 0.9,
285 },
286 VulnerabilityPattern {
287 name: "Path Traversal".to_string(),
288 pattern: Regex::new(r#"(?i)(open|file|read)\s*\([^)]*\.\./[^)]*\)"#).unwrap(),
289 severity: "high".to_string(),
290 description: "Potential path traversal vulnerability detected".to_string(),
291 recommendation: "Validate file paths and use safe path operations".to_string(),
292 cvss_base_score: 7.5,
293 owasp_category: Some("A01:2021 – Broken Access Control".to_string()),
294 confidence: 0.8,
295 },
296 VulnerabilityPattern {
297 name: "Deserialization of Untrusted Data".to_string(),
298 pattern: Regex::new(
299 r#"(?i)(pickle\.loads|yaml\.load|json\.loads)\s*\([^)]*input[^)]*\)"#,
300 )
301 .unwrap(),
302 severity: "critical".to_string(),
303 description: "Unsafe deserialization of user input".to_string(),
304 recommendation: "Validate and sanitize data before deserialization".to_string(),
305 cvss_base_score: 9.8,
306 owasp_category: Some("A08:2021 – Software and Data Integrity Failures".to_string()),
307 confidence: 0.85,
308 },
309 ];
310 self.patterns
311 .insert("unsafe_patterns".to_string(), unsafe_patterns);
312 }
313
314 pub fn calculate_cvss_score(
316 &self,
317 pattern: &VulnerabilityPattern,
318 context: Option<&str>,
319 ) -> CvssScore {
320 let mut base_score = pattern.cvss_base_score;
321
322 if let Some(ctx) = context {
324 if ctx.contains("test") || ctx.contains("spec") {
326 base_score *= 0.7;
327 }
328 if ctx.contains("main") || ctx.contains("prod") {
330 base_score *= 1.1;
331 }
332 }
333
334 base_score = base_score.clamp(0.0, 10.0);
336
337 let severity_level = match base_score {
338 0.0..=3.9 => "Low",
339 4.0..=6.9 => "Medium",
340 7.0..=8.9 => "High",
341 9.0..=10.0 => "Critical",
342 _ => "Unknown",
343 }
344 .to_string();
345
346 CvssScore {
347 base_score,
348 impact_score: base_score * 0.6, exploitability_score: base_score * 0.4, severity_level,
351 }
352 }
353
354 pub fn analyze_content_with_location(
356 &self,
357 content: &str,
358 file_path: Option<&str>,
359 vulnerability_types: &[String],
360 severity_threshold: &str,
361 ) -> Result<Vec<SecurityVulnerability>> {
362 let mut vulnerabilities = Vec::new();
363
364 let target_types = if vulnerability_types.contains(&"all".to_string()) {
365 self.patterns.keys().cloned().collect::<Vec<_>>()
366 } else {
367 vulnerability_types.to_vec()
368 };
369
370 let lines: Vec<&str> = content.lines().collect();
371
372 for vuln_type in target_types {
373 if let Some(patterns) = self.patterns.get(&vuln_type) {
374 for pattern in patterns {
375 if self.meets_severity_threshold(&pattern.severity, severity_threshold) {
376 for (line_idx, line) in lines.iter().enumerate() {
377 if let Some(capture) = pattern.pattern.find(line) {
378 let cvss_score = self.calculate_cvss_score(pattern, file_path);
379
380 vulnerabilities.push(SecurityVulnerability {
381 vulnerability_type: pattern.name.clone(),
382 severity: pattern.severity.clone(),
383 description: pattern.description.clone(),
384 location: Some(format!(
385 "Line {}: Position {}",
386 line_idx + 1,
387 capture.start()
388 )),
389 recommendation: pattern.recommendation.clone(),
390 cvss_score: Some(cvss_score.base_score),
391 owasp_category: pattern.owasp_category.clone(),
392 confidence: pattern.confidence,
393 file_path: file_path.map(|s| s.to_string()),
394 line_number: Some(line_idx + 1),
395 });
396 }
397 }
398 }
399 }
400 }
401 }
402
403 Ok(vulnerabilities)
404 }
405
406 pub fn analyze_content(
408 &self,
409 content: &str,
410 vulnerability_types: &[String],
411 severity_threshold: &str,
412 ) -> Result<Vec<SecurityVulnerability>> {
413 self.analyze_content_with_location(content, None, vulnerability_types, severity_threshold)
414 }
415
416 fn meets_severity_threshold(&self, severity: &str, threshold: &str) -> bool {
418 let severity_levels = ["low", "medium", "high", "critical"];
419 let severity_idx = severity_levels
420 .iter()
421 .position(|&s| s == severity)
422 .unwrap_or(0);
423 let threshold_idx = severity_levels
424 .iter()
425 .position(|&s| s == threshold)
426 .unwrap_or(0);
427
428 severity_idx >= threshold_idx
429 }
430
431 pub fn get_security_recommendations(
433 &self,
434 vulnerabilities: &[SecurityVulnerability],
435 ) -> Vec<String> {
436 let mut recommendations = Vec::new();
437
438 if vulnerabilities.is_empty() {
439 recommendations.push(
440 "No security vulnerabilities detected. Continue following security best practices."
441 .to_string(),
442 );
443 return recommendations;
444 }
445
446 let mut vuln_counts = HashMap::new();
448 let mut severity_counts = HashMap::new();
449 let mut owasp_categories = HashSet::new();
450
451 for vuln in vulnerabilities {
452 *vuln_counts
453 .entry(vuln.vulnerability_type.clone())
454 .or_insert(0) += 1;
455 *severity_counts.entry(vuln.severity.clone()).or_insert(0) += 1;
456 if let Some(ref category) = vuln.owasp_category {
457 owasp_categories.insert(category.clone());
458 }
459 }
460
461 if severity_counts.get("critical").unwrap_or(&0) > &0 {
463 recommendations.push("🚨 CRITICAL: Address critical vulnerabilities immediately - these pose severe security risks.".to_string());
464 }
465 if severity_counts.get("high").unwrap_or(&0) > &0 {
466 recommendations.push(
467 "⚠️ HIGH PRIORITY: High-severity vulnerabilities require urgent attention."
468 .to_string(),
469 );
470 }
471
472 if vuln_counts.contains_key("SQL Injection")
474 || vuln_counts.contains_key("SQL Injection Format")
475 {
476 recommendations.push("🛡️ Implement input validation and use parameterized queries for all database operations.".to_string());
477 }
478
479 if vuln_counts.contains_key("XSS via innerHTML")
480 || vuln_counts.contains_key("XSS via document.write")
481 || vuln_counts.contains_key("XSS via eval")
482 {
483 recommendations.push("🔒 Sanitize all user input and use safe DOM manipulation methods. Implement Content Security Policy (CSP).".to_string());
484 }
485
486 if vuln_counts.contains_key("Missing CSRF Token")
487 || vuln_counts.contains_key("AJAX without CSRF")
488 {
489 recommendations.push("🔐 Implement anti-CSRF tokens for all state-changing operations and AJAX requests.".to_string());
490 }
491
492 if vuln_counts.contains_key("Hardcoded Password")
493 || vuln_counts.contains_key("Hardcoded API Key")
494 || vuln_counts.contains_key("Hardcoded Crypto Key")
495 {
496 recommendations.push("🗝️ Use environment variables or secure key management systems for sensitive data.".to_string());
497 }
498
499 if vuln_counts.contains_key("Command Injection") {
500 recommendations.push(
501 "⚡ Validate all user input and use safe alternatives to system commands."
502 .to_string(),
503 );
504 }
505
506 if vuln_counts.contains_key("Weak Crypto Algorithm")
507 || vuln_counts.contains_key("Weak Random Number Generation")
508 {
509 recommendations.push(
510 "🔐 Upgrade to modern, secure cryptographic algorithms (SHA-256, AES-256, etc.)."
511 .to_string(),
512 );
513 }
514
515 if vuln_counts.contains_key("Path Traversal") {
516 recommendations.push(
517 "📁 Implement proper path validation and use safe file operations.".to_string(),
518 );
519 }
520
521 if vuln_counts.contains_key("Deserialization of Untrusted Data") {
522 recommendations.push(
523 "⚠️ Never deserialize untrusted data. Implement strict input validation."
524 .to_string(),
525 );
526 }
527
528 if !owasp_categories.is_empty() {
530 recommendations.push(format!(
531 "📋 OWASP Top 10 categories affected: {}",
532 owasp_categories
533 .iter()
534 .cloned()
535 .collect::<Vec<_>>()
536 .join(", ")
537 ));
538 }
539
540 recommendations
542 .push("🔍 Conduct regular security audits and penetration testing.".to_string());
543 recommendations.push(
544 "📚 Follow OWASP security guidelines and implement security training for developers."
545 .to_string(),
546 );
547 recommendations.push(
548 "🛡️ Implement proper error handling that doesn't expose sensitive information."
549 .to_string(),
550 );
551 recommendations
552 .push("📊 Set up security monitoring and logging for threat detection.".to_string());
553
554 recommendations
555 }
556
557 pub fn generate_security_report(
559 &self,
560 vulnerabilities: &[SecurityVulnerability],
561 ) -> serde_json::Value {
562 let mut severity_counts = HashMap::new();
563 let mut owasp_counts = HashMap::new();
564 let mut total_cvss_score = 0.0;
565 let mut high_confidence_vulns = 0;
566
567 for vuln in vulnerabilities {
568 *severity_counts.entry(vuln.severity.clone()).or_insert(0) += 1;
569 if let Some(ref owasp) = vuln.owasp_category {
570 *owasp_counts.entry(owasp.clone()).or_insert(0) += 1;
571 }
572 if let Some(score) = vuln.cvss_score {
573 total_cvss_score += score;
574 }
575 if vuln.confidence >= 0.8 {
576 high_confidence_vulns += 1;
577 }
578 }
579
580 let avg_cvss_score = if !vulnerabilities.is_empty() {
581 total_cvss_score / vulnerabilities.len() as f32
582 } else {
583 0.0
584 };
585
586 let security_score = match avg_cvss_score {
587 0.0..=3.9 => 85 + (15.0 * (4.0 - avg_cvss_score) / 4.0) as i32,
588 4.0..=6.9 => 60 + (25.0 * (7.0 - avg_cvss_score) / 3.0) as i32,
589 7.0..=8.9 => 30 + (30.0 * (9.0 - avg_cvss_score) / 2.0) as i32,
590 9.0..=10.0 => (30.0 * (10.0 - avg_cvss_score)) as i32,
591 _ => 0,
592 };
593
594 serde_json::json!({
595 "summary": {
596 "total_vulnerabilities": vulnerabilities.len(),
597 "high_confidence_findings": high_confidence_vulns,
598 "average_cvss_score": avg_cvss_score,
599 "security_score": security_score.max(0)
600 },
601 "severity_breakdown": severity_counts,
602 "owasp_categories": owasp_counts,
603 "recommendations": self.get_security_recommendations(vulnerabilities)
604 })
605 }
606
607 pub fn detect_injection_vulnerabilities(&self, content: &str) -> Result<Vec<Value>> {
609 let vulnerabilities = self.analyze_content(content, &["injection".to_string()], "low")?;
610
611 Ok(vulnerabilities
612 .into_iter()
613 .map(|v| {
614 serde_json::json!({
615 "type": v.vulnerability_type,
616 "severity": v.severity,
617 "description": v.description,
618 "location": v.location,
619 "recommendation": v.recommendation
620 })
621 })
622 .collect())
623 }
624
625 pub fn detect_authentication_issues(&self, content: &str) -> Result<Vec<Value>> {
627 let vulnerabilities =
628 self.analyze_content(content, &["authentication".to_string()], "low")?;
629
630 Ok(vulnerabilities
631 .into_iter()
632 .map(|v| {
633 serde_json::json!({
634 "type": v.vulnerability_type,
635 "severity": v.severity,
636 "description": v.description,
637 "location": v.location,
638 "recommendation": v.recommendation
639 })
640 })
641 .collect())
642 }
643
644 pub fn detect_data_exposure_issues(&self, content: &str) -> Result<Vec<Value>> {
646 let vulnerabilities =
647 self.analyze_content(content, &["data_exposure".to_string()], "low")?;
648
649 Ok(vulnerabilities
650 .into_iter()
651 .map(|v| {
652 serde_json::json!({
653 "type": v.vulnerability_type,
654 "severity": v.severity,
655 "description": v.description,
656 "location": v.location,
657 "recommendation": v.recommendation
658 })
659 })
660 .collect())
661 }
662}
663
664impl Default for SecurityAnalyzer {
665 fn default() -> Self {
666 Self::new()
667 }
668}
669
670#[cfg(test)]
671mod tests {
672 use super::*;
673
674 #[test]
675 fn test_sql_injection_detection() {
676 let analyzer = SecurityAnalyzer::new();
677
678 let vulnerable_code = r#"query("SELECT * FROM users WHERE id = " + user_id)"#;
679 let vulnerabilities = analyzer
680 .analyze_content(vulnerable_code, &["injection".to_string()], "low")
681 .unwrap();
682
683 assert!(!vulnerabilities.is_empty());
684 assert_eq!(vulnerabilities[0].vulnerability_type, "SQL Injection");
685 }
686
687 #[test]
688 fn test_hardcoded_password_detection() {
689 let analyzer = SecurityAnalyzer::new();
690
691 let vulnerable_code = r#"password = "admin123""#;
692 let vulnerabilities = analyzer
693 .analyze_content(vulnerable_code, &["authentication".to_string()], "low")
694 .unwrap();
695
696 assert!(!vulnerabilities.is_empty());
697 assert_eq!(vulnerabilities[0].vulnerability_type, "Hardcoded Password");
698 }
699
700 #[test]
701 fn test_weak_crypto_detection() {
702 let analyzer = SecurityAnalyzer::new();
703
704 let vulnerable_code = r#"hash = md5(password)"#;
705 let vulnerabilities = analyzer
706 .analyze_content(vulnerable_code, &["crypto".to_string()], "low")
707 .unwrap();
708
709 assert!(!vulnerabilities.is_empty());
710 assert_eq!(
711 vulnerabilities[0].vulnerability_type,
712 "Weak Crypto Algorithm"
713 );
714 }
715
716 #[test]
717 fn test_severity_threshold() {
718 let analyzer = SecurityAnalyzer::new();
719
720 assert!(analyzer.meets_severity_threshold("high", "medium"));
721 assert!(!analyzer.meets_severity_threshold("low", "high"));
722 assert!(analyzer.meets_severity_threshold("critical", "high"));
723 }
724
725 #[test]
726 fn test_security_recommendations() {
727 let analyzer = SecurityAnalyzer::new();
728
729 let vulnerabilities = vec![SecurityVulnerability {
730 vulnerability_type: "SQL Injection".to_string(),
731 severity: "high".to_string(),
732 description: "Test".to_string(),
733 location: None,
734 recommendation: "Test".to_string(),
735 cvss_score: None,
736 owasp_category: None,
737 confidence: 0.0,
738 file_path: None,
739 line_number: None,
740 }];
741
742 let recommendations = analyzer.get_security_recommendations(&vulnerabilities);
743 assert!(!recommendations.is_empty());
744 }
745}