syncable_cli/analyzer/vulnerability/
core.rs

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