use crate::dependencies::DependencyScanner;
use super::ScanArgs;
use anyhow::Result;
use colored::*;
pub fn run(args: &ScanArgs) -> Result<()> {
eprintln!("{}", "🔎 Forge Guard — Dependency Scanner".bold());
eprintln!(" Depth: {}", args.depth);
eprintln!(
" Fail fast: {}",
if args.fail_fast { "yes" } else { "no" }
);
let mut scanner = DependencyScanner::new(args.depth);
if args.update {
eprintln!("\n📥 Updating vulnerability database...");
scanner.update_database()?;
}
eprintln!("\n🔍 Scanning dependencies...");
let vulnerabilities = scanner.scan()?;
if vulnerabilities.is_empty() {
println!("\n{} No vulnerabilities found!", "✅".green().bold());
} else {
println!(
"\n{} {} vulnerabilities found:",
"⚠️".yellow().bold(),
vulnerabilities.len()
);
for vuln in &vulnerabilities {
let severity_color = match vuln.severity.to_lowercase().as_str() {
"critical" | "high" => "red",
"medium" => "yellow",
_ => "blue",
};
println!(
"\n [{}] {}",
vuln.severity.color(severity_color).bold(),
vuln.name.bold()
);
println!(" Package: {}", vuln.package);
println!(" Version: {}", vuln.version);
println!(" Detail: {}", vuln.description);
println!(" Fix: {}", vuln.recommended_fix);
if args.fail_fast && vuln.severity.to_lowercase() == "critical" {
anyhow::bail!("Critical vulnerability found: {}", vuln.name);
}
}
}
if args.shared.json {
println!("{}", serde_json::to_string_pretty(&vulnerabilities)?);
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_scan_arg_defaults() {
let args = ScanArgs {
shared: super::super::SharedFlags {
chain: "ethereum".into(),
project: std::path::PathBuf::from("."),
json: false,
markdown: false,
strict: false,
offline: false,
production: false,
report: false,
parallelism: 4,
},
depth: 1,
update: false,
vulnerable_only: false,
fail_fast: false,
};
assert_eq!(args.depth, 1);
assert!(!args.update);
assert!(!args.vulnerable_only);
assert!(!args.fail_fast);
}
#[test]
fn test_scan_with_all_options() {
let args = ScanArgs {
shared: super::super::SharedFlags::default(),
depth: 2,
update: true,
vulnerable_only: true,
fail_fast: true,
};
assert_eq!(args.depth, 2);
assert!(args.update);
assert!(args.vulnerable_only);
assert!(args.fail_fast);
}
#[test]
fn test_scan_fail_fast_display() {
assert_eq!(if true { "yes" } else { "no" }, "yes");
assert_eq!(if false { "yes" } else { "no" }, "no");
}
#[test]
fn test_dependency_scanner_creation() {
let scanner = DependencyScanner::new(1);
assert!(scanner.scan().is_ok());
}
#[test]
fn test_dependency_scanner_depth_0() {
let scanner = DependencyScanner::new(0);
let results = scanner.scan().unwrap();
assert!(results.len() <= 50);
}
}