2025-06-24 20:27:06 +02:00
|
|
|
pub mod clean;
|
2026-02-25 04:02:11 -05:00
|
|
|
pub mod config;
|
2025-06-16 16:46:22 +02:00
|
|
|
pub mod index;
|
|
|
|
|
pub mod list;
|
2025-06-24 20:27:06 +02:00
|
|
|
pub mod scan;
|
2025-06-16 16:46:22 +02:00
|
|
|
|
2026-02-25 21:16:36 -05:00
|
|
|
use crate::cli::{Commands, IndexMode, ScanMode};
|
2025-06-23 20:59:49 +02:00
|
|
|
use crate::errors::NyxResult;
|
2026-02-25 21:16:36 -05:00
|
|
|
use crate::patterns::{Severity, SeverityFilter};
|
2025-06-28 17:36:14 +02:00
|
|
|
use crate::utils::config::{AnalysisMode, Config};
|
2025-06-24 20:27:06 +02:00
|
|
|
use std::path::Path;
|
2025-06-16 16:46:22 +02:00
|
|
|
|
|
|
|
|
pub fn handle_command(
|
|
|
|
|
command: Commands,
|
|
|
|
|
database_dir: &Path,
|
2026-02-25 04:02:11 -05:00
|
|
|
config_dir: &Path,
|
2025-06-24 20:27:06 +02:00
|
|
|
config: &mut Config,
|
2025-06-23 20:59:49 +02:00
|
|
|
) -> NyxResult<()> {
|
2025-06-16 16:46:22 +02:00
|
|
|
match command {
|
2025-06-24 20:27:06 +02:00
|
|
|
Commands::Scan {
|
|
|
|
|
path,
|
2026-02-25 21:16:36 -05:00
|
|
|
index,
|
|
|
|
|
format,
|
|
|
|
|
severity,
|
|
|
|
|
mode,
|
|
|
|
|
all_targets,
|
|
|
|
|
keep_nonprod_severity,
|
|
|
|
|
quiet,
|
|
|
|
|
fail_on,
|
|
|
|
|
no_rank,
|
|
|
|
|
show_suppressed,
|
|
|
|
|
show_all,
|
|
|
|
|
include_quality,
|
|
|
|
|
max_low,
|
|
|
|
|
max_low_per_file,
|
|
|
|
|
max_low_per_rule,
|
|
|
|
|
rollup_examples,
|
|
|
|
|
show_instances,
|
|
|
|
|
min_score,
|
|
|
|
|
min_confidence,
|
|
|
|
|
// Deprecated aliases
|
2025-06-24 20:27:06 +02:00
|
|
|
no_index,
|
|
|
|
|
rebuild_index,
|
|
|
|
|
high_only,
|
2025-06-28 17:36:14 +02:00
|
|
|
ast_only,
|
|
|
|
|
cfg_only,
|
2025-06-24 20:27:06 +02:00
|
|
|
} => {
|
2026-02-25 21:16:36 -05:00
|
|
|
// ── Resolve deprecated aliases ──────────────────────────────
|
2025-06-24 20:27:06 +02:00
|
|
|
|
2026-02-25 21:16:36 -05:00
|
|
|
// Index mode: explicit --index wins, then deprecated flags
|
|
|
|
|
let effective_index = if no_index {
|
|
|
|
|
IndexMode::Off
|
|
|
|
|
} else if rebuild_index {
|
|
|
|
|
IndexMode::Rebuild
|
|
|
|
|
} else {
|
|
|
|
|
index
|
2025-06-28 17:36:14 +02:00
|
|
|
};
|
|
|
|
|
|
2026-02-25 21:16:36 -05:00
|
|
|
// Analysis mode: explicit --mode wins, then deprecated flags
|
|
|
|
|
let effective_mode = if ast_only {
|
|
|
|
|
ScanMode::Ast
|
|
|
|
|
} else if cfg_only {
|
|
|
|
|
ScanMode::Cfg
|
|
|
|
|
} else if all_targets {
|
|
|
|
|
ScanMode::Full
|
|
|
|
|
} else {
|
|
|
|
|
mode
|
2025-06-28 17:36:14 +02:00
|
|
|
};
|
|
|
|
|
|
2026-02-25 21:16:36 -05:00
|
|
|
// Severity filter: explicit --severity wins, then --high-only
|
|
|
|
|
let severity_filter = if let Some(ref expr) = severity {
|
|
|
|
|
Some(SeverityFilter::parse(expr).map_err(|e| {
|
|
|
|
|
crate::errors::NyxError::Msg(format!("invalid --severity expression: {e}"))
|
|
|
|
|
})?)
|
|
|
|
|
} else if high_only {
|
|
|
|
|
Some(SeverityFilter::parse("HIGH").unwrap())
|
|
|
|
|
} else {
|
|
|
|
|
None
|
2025-06-28 17:36:14 +02:00
|
|
|
};
|
|
|
|
|
|
2026-02-25 21:16:36 -05:00
|
|
|
// Fail-on threshold
|
|
|
|
|
let fail_on_sev = if let Some(ref expr) = fail_on {
|
|
|
|
|
Some(expr.trim().parse::<Severity>().map_err(|e| {
|
|
|
|
|
crate::errors::NyxError::Msg(format!("invalid --fail-on value: {e}"))
|
|
|
|
|
})?)
|
|
|
|
|
} else {
|
|
|
|
|
None
|
2026-02-25 04:02:11 -05:00
|
|
|
};
|
|
|
|
|
|
2026-02-25 21:16:36 -05:00
|
|
|
// ── Apply to config ─────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
match effective_mode {
|
|
|
|
|
ScanMode::Full => config.scanner.mode = AnalysisMode::Full,
|
|
|
|
|
ScanMode::Ast => config.scanner.mode = AnalysisMode::Ast,
|
|
|
|
|
ScanMode::Cfg | ScanMode::Taint => config.scanner.mode = AnalysisMode::Taint,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if keep_nonprod_severity {
|
|
|
|
|
config.scanner.include_nonprod = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if quiet {
|
|
|
|
|
config.output.quiet = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if no_rank {
|
|
|
|
|
config.output.attack_surface_ranking = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Min-score: CLI wins, then config
|
|
|
|
|
if let Some(s) = min_score {
|
|
|
|
|
config.output.min_score = Some(s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Min-confidence: CLI wins, then config
|
|
|
|
|
if let Some(ref expr) = min_confidence {
|
|
|
|
|
config.output.min_confidence =
|
|
|
|
|
Some(expr.parse::<crate::evidence::Confidence>().map_err(|e| {
|
|
|
|
|
crate::errors::NyxError::Msg(format!("invalid --min-confidence value: {e}"))
|
|
|
|
|
})?);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if show_all {
|
|
|
|
|
config.output.show_all = true;
|
|
|
|
|
}
|
|
|
|
|
if include_quality {
|
|
|
|
|
config.output.include_quality = true;
|
|
|
|
|
}
|
|
|
|
|
// CLI values override config defaults (clap provides defaults)
|
|
|
|
|
config.output.max_low = max_low;
|
|
|
|
|
config.output.max_low_per_file = max_low_per_file;
|
|
|
|
|
config.output.max_low_per_rule = max_low_per_rule;
|
|
|
|
|
config.output.rollup_examples = rollup_examples;
|
|
|
|
|
|
|
|
|
|
scan::handle(
|
|
|
|
|
&path,
|
|
|
|
|
effective_index,
|
|
|
|
|
format,
|
|
|
|
|
severity_filter,
|
|
|
|
|
fail_on_sev,
|
|
|
|
|
show_suppressed,
|
|
|
|
|
show_instances.as_deref(),
|
|
|
|
|
database_dir,
|
|
|
|
|
config,
|
|
|
|
|
)?;
|
2026-02-25 04:02:11 -05:00
|
|
|
}
|
|
|
|
|
Commands::Index { action } => {
|
|
|
|
|
index::handle(action, database_dir, config)?;
|
|
|
|
|
}
|
|
|
|
|
Commands::List { verbose } => {
|
|
|
|
|
list::handle(verbose, database_dir)?;
|
|
|
|
|
}
|
|
|
|
|
Commands::Clean { project, all } => {
|
|
|
|
|
clean::handle(project, all, database_dir)?;
|
|
|
|
|
}
|
|
|
|
|
Commands::Config { action } => {
|
|
|
|
|
use crate::cli::ConfigAction;
|
|
|
|
|
match action {
|
|
|
|
|
ConfigAction::Show => self::config::show(config)?,
|
|
|
|
|
ConfigAction::Path => self::config::path(config_dir)?,
|
|
|
|
|
ConfigAction::AddRule {
|
|
|
|
|
lang,
|
|
|
|
|
matcher,
|
|
|
|
|
kind,
|
|
|
|
|
cap,
|
|
|
|
|
} => self::config::add_rule(config_dir, &lang, &matcher, &kind, &cap)?,
|
|
|
|
|
ConfigAction::AddTerminator { lang, name } => {
|
|
|
|
|
self::config::add_terminator(config_dir, &lang, &name)?
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-06-16 16:46:22 +02:00
|
|
|
}
|
|
|
|
|
}
|
2026-02-25 04:02:11 -05:00
|
|
|
Ok(())
|
2025-06-24 20:27:06 +02:00
|
|
|
}
|