use clap::{Args, Parser, Subcommand};
use serde::Deserialize;
#[derive(Debug, Clone, Copy, clap::ValueEnum, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum OutputFormat {
Terminal,
Json,
Sarif,
}
#[derive(Debug, Clone, Copy, clap::ValueEnum, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum SeverityFilter {
Low,
Medium,
High,
Critical,
}
#[derive(Args, Debug, Clone)]
pub struct ScanArgs {
#[arg(default_value = ".")]
pub path: String,
#[arg(long)]
pub config: Option<String>,
#[arg(short, long, value_enum, default_value = "terminal")]
pub format: OutputFormat,
#[arg(short, long, value_enum)]
pub severity: Option<SeverityFilter>,
#[arg(short, long)]
pub rules: Option<String>,
#[arg(long, default_value_t = false)]
pub no_builtins: bool,
#[arg(long, default_value_t = false)]
pub changed: bool,
#[arg(long)]
pub baseline: Option<String>,
#[arg(long)]
pub write_baseline: Option<String>,
#[arg(long, default_value_t = false)]
pub explain: bool,
#[arg(short, long)]
pub quiet: bool,
#[arg(long, default_value_t = 1_048_576)]
pub max_file_size: u64,
}
#[derive(Args, Debug, Clone)]
pub struct InitArgs {
#[arg(long, default_value = ".")]
pub path: String,
#[arg(long, default_value = ".foxguard.yml")]
pub config_path: String,
#[arg(long, default_value = ".git/hooks/pre-commit")]
pub hook_path: String,
#[arg(long, default_value = ".foxguard/baseline.json")]
pub baseline: String,
#[arg(long, default_value = ".foxguard/secrets-baseline.json")]
pub secrets_baseline: String,
#[arg(long, default_value_t = false)]
pub no_baseline: bool,
#[arg(long, default_value_t = false)]
pub force: bool,
}
#[derive(Args, Debug, Clone)]
pub struct BaselineArgs {
#[command(flatten)]
pub scan: ScanArgs,
#[arg(long, default_value = ".foxguard/baseline.json")]
pub output: String,
}
#[derive(Args, Debug, Clone)]
pub struct SecretsArgs {
#[arg(default_value = ".")]
pub path: String,
#[arg(long)]
pub config: Option<String>,
#[arg(short, long, value_enum, default_value = "terminal")]
pub format: OutputFormat,
#[arg(long, default_value_t = false)]
pub changed: bool,
#[arg(long)]
pub baseline: Option<String>,
#[arg(long)]
pub write_baseline: Option<String>,
#[arg(long = "exclude-path")]
pub exclude_paths: Vec<String>,
#[arg(long)]
pub exclude_path_file: Option<String>,
#[arg(long = "ignore-rule")]
pub ignored_rules: Vec<String>,
#[arg(long, default_value_t = 1_048_576)]
pub max_file_size: u64,
}
#[derive(Subcommand, Debug, Clone)]
pub enum Command {
Init(InitArgs),
Baseline(BaselineArgs),
Secrets(SecretsArgs),
}
#[derive(Parser, Debug)]
#[command(
name = "foxguard",
about = "Fast local security guard for changed files, built-in rules, and Semgrep-compatible YAML",
version
)]
pub struct Cli {
#[command(subcommand)]
pub command: Option<Command>,
#[command(flatten)]
pub scan: ScanArgs,
}
impl SeverityFilter {
pub fn to_severity(&self) -> crate::Severity {
match self {
SeverityFilter::Low => crate::Severity::Low,
SeverityFilter::Medium => crate::Severity::Medium,
SeverityFilter::High => crate::Severity::High,
SeverityFilter::Critical => crate::Severity::Critical,
}
}
}