syncable_cli/analyzer/vulnerability/
core.rs1use std::collections::HashMap;
2use std::path::Path;
3use std::time::Instant;
4use log::{info, warn};
5use rayon::prelude::*;
6
7use crate::analyzer::dependency_parser::{DependencyInfo, Language};
8use crate::analyzer::tool_management::ToolInstaller;
9use super::types::{VulnerabilityReport, VulnerableDependency, VulnerabilityError, VulnerabilitySeverity};
10use super::checkers::{
11 RustVulnerabilityChecker, JavaScriptVulnerabilityChecker, PythonVulnerabilityChecker,
12 GoVulnerabilityChecker, JavaVulnerabilityChecker, LanguageVulnerabilityChecker
13};
14
15pub struct VulnerabilityChecker;
16
17impl VulnerabilityChecker {
18 pub fn new() -> Self {
19 Self
20 }
21
22 pub async fn check_all_dependencies(
24 &self,
25 dependencies: &HashMap<Language, Vec<DependencyInfo>>,
26 project_path: &Path,
27 ) -> Result<VulnerabilityReport, VulnerabilityError> {
28 let start_time = Instant::now();
29 info!("Starting comprehensive vulnerability check");
30
31 let mut installer = ToolInstaller::new();
33 let languages: Vec<Language> = dependencies.keys().cloned().collect();
34
35 info!("🔧 Checking and installing required vulnerability scanning tools...");
36 installer.ensure_tools_for_languages(&languages)
37 .map_err(|e| VulnerabilityError::CommandError(format!("Tool installation failed: {}", e)))?;
38
39 installer.print_tool_status(&languages);
40
41 let mut all_vulnerable_deps = Vec::new();
42
43 let results: Vec<_> = dependencies.par_iter()
45 .map(|(language, deps)| {
46 self.check_language_dependencies(language, deps, project_path)
47 })
48 .collect();
49
50 for result in results {
52 match result {
53 Ok(mut vuln_deps) => all_vulnerable_deps.append(&mut vuln_deps),
54 Err(e) => warn!("Error checking vulnerabilities: {}", e),
55 }
56 }
57
58 all_vulnerable_deps.sort_by(|a, b| {
60 let a_max = a.vulnerabilities.iter()
61 .map(|v| &v.severity)
62 .max()
63 .unwrap_or(&VulnerabilitySeverity::Info);
64 let b_max = b.vulnerabilities.iter()
65 .map(|v| &v.severity)
66 .max()
67 .unwrap_or(&VulnerabilitySeverity::Info);
68 b_max.cmp(a_max)
69 });
70
71 let mut critical_count = 0;
73 let mut high_count = 0;
74 let mut medium_count = 0;
75 let mut low_count = 0;
76 let mut total_vulnerabilities = 0;
77
78 for dep in &all_vulnerable_deps {
79 for vuln in &dep.vulnerabilities {
80 total_vulnerabilities += 1;
81 match vuln.severity {
82 VulnerabilitySeverity::Critical => critical_count += 1,
83 VulnerabilitySeverity::High => high_count += 1,
84 VulnerabilitySeverity::Medium => medium_count += 1,
85 VulnerabilitySeverity::Low => low_count += 1,
86 VulnerabilitySeverity::Info => {},
87 }
88 }
89 }
90
91 Ok(VulnerabilityReport {
92 checked_at: chrono::Utc::now(),
93 total_vulnerabilities,
94 critical_count,
95 high_count,
96 medium_count,
97 low_count,
98 vulnerable_dependencies: all_vulnerable_deps,
99 })
100 }
101
102 fn check_language_dependencies(
103 &self,
104 language: &Language,
105 dependencies: &[DependencyInfo],
106 project_path: &Path,
107 ) -> Result<Vec<VulnerableDependency>, VulnerabilityError> {
108 info!("Checking {} dependencies for {:?}", dependencies.len(), language);
109
110 match language {
111 Language::Rust => {
112 let checker = RustVulnerabilityChecker::new();
113 checker.check_vulnerabilities(dependencies, project_path)
114 },
115 Language::JavaScript | Language::TypeScript => {
116 let checker = JavaScriptVulnerabilityChecker::new();
117 checker.check_vulnerabilities(dependencies, project_path)
118 },
119 Language::Python => {
120 let checker = PythonVulnerabilityChecker::new();
121 checker.check_vulnerabilities(dependencies, project_path)
122 },
123 Language::Go => {
124 let checker = GoVulnerabilityChecker::new();
125 checker.check_vulnerabilities(dependencies, project_path)
126 },
127 Language::Java | Language::Kotlin => {
128 let checker = JavaVulnerabilityChecker::new();
129 checker.check_vulnerabilities(dependencies, project_path)
130 },
131 _ => {
132 warn!("Vulnerability checking not yet implemented for {:?}", language);
133 Ok(vec![])
134 }
135 }
136 }
137}