syncable-cli 0.37.1

A Rust-based CLI that analyzes code repositories and generates Infrastructure as Code configurations
Documentation
use log::{info, warn};
use rayon::prelude::*;
use std::collections::HashMap;
use std::path::Path;
use std::time::Instant;

use super::checkers::{
    GoVulnerabilityChecker, JavaScriptVulnerabilityChecker, JavaVulnerabilityChecker,
    LanguageVulnerabilityChecker, MutableLanguageVulnerabilityChecker, PythonVulnerabilityChecker,
    RustVulnerabilityChecker,
};
use super::types::{
    VulnerabilityError, VulnerabilityReport, VulnerabilitySeverity, VulnerableDependency,
};
use crate::analyzer::dependency_parser::{DependencyInfo, Language};
use crate::analyzer::tool_management::ToolInstaller;

#[derive(Default)]
pub struct VulnerabilityChecker;

impl VulnerabilityChecker {
    pub fn new() -> Self {
        Self
    }

    /// Check all dependencies for vulnerabilities
    pub async fn check_all_dependencies(
        &self,
        dependencies: &HashMap<Language, Vec<DependencyInfo>>,
        project_path: &Path,
    ) -> Result<VulnerabilityReport, VulnerabilityError> {
        let _start_time = Instant::now();
        info!("Starting comprehensive vulnerability check");

        // Auto-install required tools
        let mut installer = ToolInstaller::new();
        let languages: Vec<Language> = dependencies.keys().cloned().collect();

        info!("🔧 Checking and installing required vulnerability scanning tools...");
        installer
            .ensure_tools_for_languages(&languages)
            .map_err(|e| {
                VulnerabilityError::CommandError(format!("Tool installation failed: {}", e))
            })?;

        installer.print_tool_status(&languages);

        let mut all_vulnerable_deps = Vec::new();

        // Process each language in parallel
        let results: Vec<_> = dependencies
            .par_iter()
            .map(|(language, deps)| self.check_language_dependencies(language, deps, project_path))
            .collect();

        // Collect results
        for result in results {
            match result {
                Ok(mut vuln_deps) => all_vulnerable_deps.append(&mut vuln_deps),
                Err(e) => warn!("Error checking vulnerabilities: {}", e),
            }
        }

        // Sort by severity
        all_vulnerable_deps.sort_by(|a, b| {
            let a_max = a
                .vulnerabilities
                .iter()
                .map(|v| &v.severity)
                .max()
                .unwrap_or(&VulnerabilitySeverity::Info);
            let b_max = b
                .vulnerabilities
                .iter()
                .map(|v| &v.severity)
                .max()
                .unwrap_or(&VulnerabilitySeverity::Info);
            b_max.cmp(a_max)
        });

        // Count vulnerabilities by severity
        let mut critical_count = 0;
        let mut high_count = 0;
        let mut medium_count = 0;
        let mut low_count = 0;
        let mut total_vulnerabilities = 0;

        for dep in &all_vulnerable_deps {
            for vuln in &dep.vulnerabilities {
                total_vulnerabilities += 1;
                match vuln.severity {
                    VulnerabilitySeverity::Critical => critical_count += 1,
                    VulnerabilitySeverity::High => high_count += 1,
                    VulnerabilitySeverity::Medium => medium_count += 1,
                    VulnerabilitySeverity::Low => low_count += 1,
                    VulnerabilitySeverity::Info => {}
                }
            }
        }

        Ok(VulnerabilityReport {
            checked_at: chrono::Utc::now(),
            total_vulnerabilities,
            critical_count,
            high_count,
            medium_count,
            low_count,
            vulnerable_dependencies: all_vulnerable_deps,
        })
    }

    fn check_language_dependencies(
        &self,
        language: &Language,
        dependencies: &[DependencyInfo],
        project_path: &Path,
    ) -> Result<Vec<VulnerableDependency>, VulnerabilityError> {
        info!(
            "Checking {} dependencies for {:?}",
            dependencies.len(),
            language
        );

        match language {
            Language::Rust => {
                let checker = RustVulnerabilityChecker::new();
                checker.check_vulnerabilities(dependencies, project_path)
            }
            Language::JavaScript | Language::TypeScript => {
                let mut checker = JavaScriptVulnerabilityChecker::new();
                checker.check_vulnerabilities(dependencies, project_path)
            }
            Language::Python => {
                let mut checker = PythonVulnerabilityChecker::new();
                checker.check_vulnerabilities(dependencies, project_path)
            }
            Language::Go => {
                let mut checker = GoVulnerabilityChecker::new();
                checker.check_vulnerabilities(dependencies, project_path)
            }
            Language::Java | Language::Kotlin => {
                let mut checker = JavaVulnerabilityChecker::new();
                checker.check_vulnerabilities(dependencies, project_path)
            }
            _ => {
                warn!(
                    "Vulnerability checking not yet implemented for {:?}",
                    language
                );
                Ok(vec![])
            }
        }
    }
}