2026-02-25 21:16:36 -05:00
|
|
|
use clap::{Parser, Subcommand, ValueEnum};
|
2025-06-16 16:46:22 +02:00
|
|
|
|
|
|
|
|
#[derive(Parser)]
|
2025-06-17 10:55:50 +02:00
|
|
|
#[command(name = "nyx")]
|
2025-06-16 16:46:22 +02:00
|
|
|
#[command(about = "A fast vulnerability scanner with project indexing")]
|
|
|
|
|
#[command(version)]
|
|
|
|
|
pub struct Cli {
|
|
|
|
|
#[command(subcommand)]
|
|
|
|
|
pub(crate) command: Commands,
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-25 04:02:11 -05:00
|
|
|
impl Commands {
|
|
|
|
|
/// Whether this command produces structured (machine-readable) output on
|
|
|
|
|
/// stdout, meaning human status messages must be suppressed entirely.
|
|
|
|
|
pub fn is_structured_output(&self) -> bool {
|
2026-02-25 21:16:36 -05:00
|
|
|
matches!(self, Commands::Scan { format, .. } if *format == OutputFormat::Json || *format == OutputFormat::Sarif)
|
2026-02-25 04:02:11 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-25 21:16:36 -05:00
|
|
|
/// Output format for scan results.
|
|
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueEnum, Default)]
|
|
|
|
|
pub enum OutputFormat {
|
|
|
|
|
#[default]
|
|
|
|
|
Console,
|
|
|
|
|
Json,
|
|
|
|
|
Sarif,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl std::fmt::Display for OutputFormat {
|
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
|
match self {
|
|
|
|
|
OutputFormat::Console => write!(f, "console"),
|
|
|
|
|
OutputFormat::Json => write!(f, "json"),
|
|
|
|
|
OutputFormat::Sarif => write!(f, "sarif"),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Index mode for scan operations.
|
|
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueEnum, Default)]
|
|
|
|
|
pub enum IndexMode {
|
|
|
|
|
/// Use index if available, build if missing (default)
|
|
|
|
|
#[default]
|
|
|
|
|
Auto,
|
|
|
|
|
/// Skip indexing entirely, scan filesystem directly
|
|
|
|
|
Off,
|
|
|
|
|
/// Force rebuild index before scanning
|
|
|
|
|
Rebuild,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Analysis mode for scan operations.
|
|
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueEnum, Default)]
|
|
|
|
|
pub enum ScanMode {
|
|
|
|
|
/// Run all analyses: AST patterns + CFG + taint (default)
|
|
|
|
|
#[default]
|
|
|
|
|
Full,
|
|
|
|
|
/// Run AST pattern queries only (no CFG/taint)
|
|
|
|
|
Ast,
|
|
|
|
|
/// Run CFG structural analyses + taint only (no AST patterns)
|
|
|
|
|
Cfg,
|
|
|
|
|
/// Alias for cfg (CFG + taint analysis)
|
|
|
|
|
Taint,
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-16 16:46:22 +02:00
|
|
|
#[derive(Subcommand)]
|
|
|
|
|
pub enum Commands {
|
|
|
|
|
/// Scan project for vulnerabilities
|
|
|
|
|
Scan {
|
|
|
|
|
/// Path to scan (defaults to current directory)
|
|
|
|
|
#[arg(default_value = ".")]
|
|
|
|
|
path: String,
|
|
|
|
|
|
2026-02-25 21:16:36 -05:00
|
|
|
/// Index mode: auto (default), off (no index), rebuild (force rebuild)
|
|
|
|
|
#[arg(long, value_enum, default_value_t = IndexMode::Auto)]
|
|
|
|
|
index: IndexMode,
|
|
|
|
|
|
|
|
|
|
/// Output format
|
|
|
|
|
#[arg(short, long, value_enum, default_value_t = OutputFormat::Console)]
|
|
|
|
|
format: OutputFormat,
|
|
|
|
|
|
|
|
|
|
/// Severity filter expression: HIGH, HIGH,MEDIUM, or >=MEDIUM
|
|
|
|
|
///
|
|
|
|
|
/// Filters findings AFTER all severity normalization (e.g. nonprod
|
|
|
|
|
/// downgrades). Only findings matching the expression are emitted.
|
|
|
|
|
/// Case-insensitive. Shell-quote expressions containing ">".
|
2025-06-16 16:46:22 +02:00
|
|
|
#[arg(long)]
|
2026-02-25 21:16:36 -05:00
|
|
|
severity: Option<String>,
|
|
|
|
|
|
|
|
|
|
/// Analysis mode: full (default), ast, cfg, taint
|
|
|
|
|
#[arg(long, value_enum, default_value_t = ScanMode::Full)]
|
|
|
|
|
mode: ScanMode,
|
|
|
|
|
|
|
|
|
|
/// Scan all targets (alias for --mode full)
|
|
|
|
|
#[arg(long, hide = true)]
|
|
|
|
|
all_targets: bool,
|
2025-06-16 16:46:22 +02:00
|
|
|
|
2026-02-25 21:16:36 -05:00
|
|
|
/// Preserve original severity for test/vendor/build paths
|
|
|
|
|
///
|
|
|
|
|
/// By default, findings in non-production paths are downgraded by one
|
|
|
|
|
/// severity tier. This flag preserves original severity.
|
|
|
|
|
#[arg(long, alias = "include-nonprod")]
|
|
|
|
|
keep_nonprod_severity: bool,
|
|
|
|
|
|
|
|
|
|
/// Suppress all human-readable status output
|
2025-06-16 16:46:22 +02:00
|
|
|
#[arg(long)]
|
2026-02-25 21:16:36 -05:00
|
|
|
quiet: bool,
|
2025-06-16 16:46:22 +02:00
|
|
|
|
2026-02-25 21:16:36 -05:00
|
|
|
/// Exit with code 1 if any finding meets or exceeds this severity
|
|
|
|
|
///
|
|
|
|
|
/// Useful for CI gating. Example: --fail-on HIGH
|
|
|
|
|
#[arg(long)]
|
|
|
|
|
fail_on: Option<String>,
|
2025-06-16 16:46:22 +02:00
|
|
|
|
2026-02-25 21:16:36 -05:00
|
|
|
/// Disable attack-surface ranking (findings are sorted by exploitability by default)
|
2025-06-16 16:46:22 +02:00
|
|
|
#[arg(long)]
|
2026-02-25 21:16:36 -05:00
|
|
|
no_rank: bool,
|
2025-06-28 17:36:14 +02:00
|
|
|
|
2026-02-25 21:16:36 -05:00
|
|
|
/// Show inline-suppressed findings (dimmed, tagged [SUPPRESSED])
|
2025-06-28 17:36:14 +02:00
|
|
|
#[arg(long)]
|
2026-02-25 21:16:36 -05:00
|
|
|
show_suppressed: bool,
|
2025-06-28 17:36:14 +02:00
|
|
|
|
2026-02-25 21:16:36 -05:00
|
|
|
/// Show all findings: disables category filtering, rollups, and LOW budgets
|
|
|
|
|
#[arg(long = "all")]
|
|
|
|
|
show_all: bool,
|
|
|
|
|
|
|
|
|
|
/// Include Quality findings (excluded by default)
|
2025-06-28 17:36:14 +02:00
|
|
|
#[arg(long)]
|
2026-02-25 21:16:36 -05:00
|
|
|
include_quality: bool,
|
|
|
|
|
|
|
|
|
|
/// Maximum total LOW findings to show
|
|
|
|
|
#[arg(long, default_value_t = 20)]
|
|
|
|
|
max_low: u32,
|
|
|
|
|
|
|
|
|
|
/// Maximum LOW findings per file
|
|
|
|
|
#[arg(long, default_value_t = 1)]
|
|
|
|
|
max_low_per_file: u32,
|
|
|
|
|
|
|
|
|
|
/// Maximum LOW findings per rule
|
|
|
|
|
#[arg(long, default_value_t = 10)]
|
|
|
|
|
max_low_per_rule: u32,
|
2025-06-28 17:36:14 +02:00
|
|
|
|
2026-02-25 21:16:36 -05:00
|
|
|
/// Number of example locations in rollup findings
|
|
|
|
|
#[arg(long, default_value_t = 5)]
|
|
|
|
|
rollup_examples: u32,
|
|
|
|
|
|
|
|
|
|
/// Show all instances for a specific rule (bypasses rollup for that rule)
|
2025-06-28 17:36:14 +02:00
|
|
|
#[arg(long)]
|
2026-02-25 21:16:36 -05:00
|
|
|
show_instances: Option<String>,
|
2026-02-25 04:02:11 -05:00
|
|
|
|
2026-02-25 21:16:36 -05:00
|
|
|
/// Minimum attack-surface score to include in output
|
|
|
|
|
///
|
|
|
|
|
/// Findings with a rank score below this threshold are suppressed.
|
|
|
|
|
/// Requires ranking to be enabled (has no effect with --no-rank).
|
|
|
|
|
/// Example: --min-score 50
|
2026-02-25 04:02:11 -05:00
|
|
|
#[arg(long)]
|
2026-02-25 21:16:36 -05:00
|
|
|
min_score: Option<u32>,
|
|
|
|
|
|
|
|
|
|
/// Minimum confidence level to include in output
|
|
|
|
|
///
|
|
|
|
|
/// Values: low, medium, high. Findings below this level are dropped.
|
|
|
|
|
/// JSON/SARIF include all unless filtered.
|
|
|
|
|
#[arg(long)]
|
|
|
|
|
min_confidence: Option<String>,
|
|
|
|
|
|
|
|
|
|
// ── Deprecated aliases (hidden) ─────────────────────────────────
|
|
|
|
|
/// Deprecated: use --index off
|
|
|
|
|
#[arg(long, hide = true)]
|
|
|
|
|
no_index: bool,
|
|
|
|
|
|
|
|
|
|
/// Deprecated: use --index rebuild
|
|
|
|
|
#[arg(long, hide = true)]
|
|
|
|
|
rebuild_index: bool,
|
|
|
|
|
|
|
|
|
|
/// Deprecated: use --severity HIGH
|
|
|
|
|
#[arg(long, hide = true)]
|
|
|
|
|
high_only: bool,
|
|
|
|
|
|
|
|
|
|
/// Deprecated: use --mode ast
|
|
|
|
|
#[arg(long, hide = true)]
|
|
|
|
|
ast_only: bool,
|
|
|
|
|
|
|
|
|
|
/// Deprecated: use --mode cfg
|
|
|
|
|
#[arg(long, hide = true)]
|
|
|
|
|
cfg_only: bool,
|
2025-06-16 16:46:22 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/// Manage project indexes
|
|
|
|
|
Index {
|
|
|
|
|
#[command(subcommand)]
|
|
|
|
|
action: IndexAction,
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/// List all indexed projects
|
|
|
|
|
List {
|
|
|
|
|
/// Show detailed information
|
|
|
|
|
#[arg(short, long)]
|
|
|
|
|
verbose: bool,
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/// Remove project from index
|
|
|
|
|
Clean {
|
|
|
|
|
/// Project name or path to clean
|
|
|
|
|
project: Option<String>,
|
|
|
|
|
|
|
|
|
|
/// Clean all projects
|
|
|
|
|
#[arg(long)]
|
|
|
|
|
all: bool,
|
|
|
|
|
},
|
2026-02-25 04:02:11 -05:00
|
|
|
|
|
|
|
|
/// Manage analysis configuration
|
|
|
|
|
Config {
|
|
|
|
|
#[command(subcommand)]
|
|
|
|
|
action: ConfigAction,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Subcommand)]
|
|
|
|
|
pub enum ConfigAction {
|
|
|
|
|
/// Print effective merged configuration as TOML
|
|
|
|
|
Show,
|
|
|
|
|
|
|
|
|
|
/// Print configuration directory path
|
|
|
|
|
Path,
|
|
|
|
|
|
|
|
|
|
/// Add a label rule to nyx.local
|
|
|
|
|
AddRule {
|
|
|
|
|
/// Language slug (e.g. javascript, rust, python)
|
|
|
|
|
#[arg(long)]
|
|
|
|
|
lang: String,
|
|
|
|
|
|
|
|
|
|
/// Function or property name to match
|
|
|
|
|
#[arg(long)]
|
|
|
|
|
matcher: String,
|
|
|
|
|
|
|
|
|
|
/// Rule kind: source, sanitizer, or sink
|
|
|
|
|
#[arg(long)]
|
|
|
|
|
kind: String,
|
|
|
|
|
|
|
|
|
|
/// Capability: env_var, html_escape, shell_escape, url_encode, json_parse, file_io, or all
|
|
|
|
|
#[arg(long)]
|
|
|
|
|
cap: String,
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/// Add a terminator function to nyx.local
|
|
|
|
|
AddTerminator {
|
|
|
|
|
/// Language slug (e.g. javascript, rust, python)
|
|
|
|
|
#[arg(long)]
|
|
|
|
|
lang: String,
|
|
|
|
|
|
|
|
|
|
/// Function name that terminates execution (e.g. process.exit)
|
|
|
|
|
#[arg(long)]
|
|
|
|
|
name: String,
|
|
|
|
|
},
|
2025-06-16 16:46:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Subcommand)]
|
|
|
|
|
pub enum IndexAction {
|
|
|
|
|
/// Build or update index for current project
|
|
|
|
|
Build {
|
|
|
|
|
/// Path to index (defaults to current directory)
|
|
|
|
|
#[arg(default_value = ".")]
|
|
|
|
|
path: String,
|
|
|
|
|
|
|
|
|
|
/// Force full rebuild
|
|
|
|
|
#[arg(short, long)]
|
|
|
|
|
force: bool,
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/// Show index status and statistics
|
|
|
|
|
Status {
|
|
|
|
|
/// Project path to check
|
|
|
|
|
#[arg(default_value = ".")]
|
|
|
|
|
path: String,
|
|
|
|
|
},
|
|
|
|
|
}
|