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