use crate::malware_db::MalwareSignature;
use crate::rules::custom::YamlRule;
use serde::{Deserialize, Serialize};
use std::collections::HashSet;
use std::path::Path;
use super::severity::SeverityConfig;
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct Config {
pub scan: ScanConfig,
pub watch: WatchConfig,
pub text_files: TextFilesConfig,
pub ignore: IgnoreConfig,
#[serde(default)]
pub baseline: BaselineConfig,
#[serde(default)]
pub severity: SeverityConfig,
#[serde(default)]
pub disabled_rules: HashSet<String>,
#[serde(default)]
pub rules: Vec<YamlRule>,
#[serde(default)]
pub malware_signatures: Vec<MalwareSignature>,
}
impl Config {
pub fn effective_disabled_rules(&self) -> HashSet<String> {
let mut disabled = self.disabled_rules.clone();
disabled.extend(self.severity.ignore.iter().cloned());
disabled
}
pub fn is_rule_disabled(&self, rule_id: &str) -> bool {
self.disabled_rules.contains(rule_id) || self.severity.ignore.contains(rule_id)
}
pub fn get_rule_severity(&self, rule_id: &str) -> Option<crate::rules::RuleSeverity> {
if self.is_rule_disabled(rule_id) {
return None;
}
self.severity.get_rule_severity(rule_id)
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct ScanConfig {
pub format: Option<String>,
pub strict: bool,
pub scan_type: Option<String>,
pub recursive: bool,
pub ci: bool,
pub verbose: bool,
pub min_confidence: Option<String>,
pub skip_comments: bool,
pub fix_hint: bool,
pub compact: bool,
pub no_malware_scan: bool,
pub watch: bool,
pub malware_db: Option<String>,
pub custom_rules: Option<String>,
pub output: Option<String>,
pub deep_scan: bool,
pub fix: bool,
pub fix_dry_run: bool,
pub warn_only: bool,
pub min_severity: Option<String>,
pub min_rule_severity: Option<String>,
pub strict_secrets: bool,
pub remote: Option<String>,
pub git_ref: Option<String>,
pub remote_auth: Option<String>,
pub parallel_clones: Option<usize>,
pub remote_list: Option<String>,
pub awesome_claude_code: bool,
pub badge: bool,
pub badge_format: Option<String>,
pub summary: bool,
pub all_clients: bool,
pub client: Option<String>,
pub no_cve_scan: bool,
pub cve_db: Option<String>,
pub sbom: bool,
pub sbom_format: Option<String>,
pub sbom_npm: bool,
pub sbom_cargo: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct WatchConfig {
pub debounce_ms: u64,
pub poll_interval_ms: u64,
}
impl Default for WatchConfig {
fn default() -> Self {
Self {
debounce_ms: 300,
poll_interval_ms: 500,
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct BaselineConfig {
pub enabled: bool,
pub check_drift: bool,
pub save_to: Option<String>,
pub compare_with: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct TextFilesConfig {
pub extensions: HashSet<String>,
pub special_names: HashSet<String>,
}
impl Default for TextFilesConfig {
fn default() -> Self {
let extensions: HashSet<String> = [
"md",
"txt",
"rst",
"json",
"yaml",
"yml",
"toml",
"xml",
"ini",
"conf",
"cfg",
"env",
"sh",
"bash",
"zsh",
"fish",
"py",
"rb",
"pl",
"pm",
"lua",
"r",
"js",
"ts",
"jsx",
"tsx",
"html",
"css",
"scss",
"sass",
"less",
"rs",
"go",
"c",
"cpp",
"h",
"hpp",
"cc",
"cxx",
"java",
"kt",
"kts",
"scala",
"clj",
"groovy",
"cs",
"fs",
"vb",
"swift",
"m",
"mm",
"php",
"ex",
"exs",
"hs",
"ml",
"vim",
"el",
"lisp",
"dockerfile",
"makefile",
"cmake",
"gradle",
]
.into_iter()
.map(String::from)
.collect();
let special_names: HashSet<String> = [
"Dockerfile",
"Makefile",
"Rakefile",
"Gemfile",
"Podfile",
"Vagrantfile",
"Procfile",
"LICENSE",
"README",
"CHANGELOG",
"CONTRIBUTING",
"AUTHORS",
"CMakeLists.txt",
"Justfile",
]
.into_iter()
.map(String::from)
.collect();
Self {
extensions,
special_names,
}
}
}
impl TextFilesConfig {
pub fn is_text_file(&self, path: &Path) -> bool {
if let Some(ext) = path.extension().and_then(|e| e.to_str())
&& self.extensions.contains(&ext.to_lowercase())
{
return true;
}
if let Some(name) = path.file_name().and_then(|n| n.to_str()) {
if self.special_names.contains(name) {
return true;
}
let name_lower = name.to_lowercase();
if self
.special_names
.iter()
.any(|n| n.to_lowercase() == name_lower)
{
return true;
}
}
false
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct IgnoreConfig {
pub patterns: Vec<String>,
}