use crate::config::{self, Config};
use crate::finding::{ScanReport, ScanResult};
use crate::scanners;
use colored::Colorize;
use rayon::prelude::*;
use std::path::Path;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ScanMode {
Skill,
Agent,
}
pub fn run_scan(path: &Path, config: &Config, mode: ScanMode) -> ScanReport {
let all = match mode {
ScanMode::Skill => scanners::skill_scanners(),
ScanMode::Agent => scanners::agent_scanners(),
};
let n_active = all
.iter()
.filter(|s| config.is_scanner_enabled(s.name()))
.count();
let n_disabled = all.len() - n_active;
if n_disabled > 0 {
eprintln!(
"{}",
format!(
"Running {} scanner{}… ({} disabled)",
n_active,
if n_active == 1 { "" } else { "s" },
n_disabled
)
.dimmed()
);
} else {
eprintln!(
"{}",
format!(
"Running {} scanner{}…",
n_active,
if n_active == 1 { "" } else { "s" }
)
.dimmed()
);
}
let results: Vec<ScanResult> = all
.par_iter()
.map(|scanner| {
if !config.is_scanner_enabled(scanner.name()) {
ScanResult::skipped(scanner.name(), "disabled in config")
} else if scanner.is_available() {
scanner.scan(path, config)
} else {
ScanResult::skipped(
scanner.name(),
&format!("{} not found on PATH", scanner.name()),
)
}
})
.collect();
let suppressions = config::load_suppressions(path);
let skill_name = extract_skill_name(path);
ScanReport::from_results(&skill_name, results, &suppressions, config.strict.enabled)
}
fn extract_skill_name(path: &Path) -> String {
path.file_name()
.map(|n| n.to_string_lossy().to_string())
.unwrap_or_else(|| "unknown".to_string())
}