mod action_report;
mod calibrate;
mod calibrate_autoroute;
mod config;
mod daemon;
mod detectors;
mod diff;
mod explain;
mod guard;
mod hook;
mod limits;
mod maintenance;
mod scan;
mod scan_system;
mod watch;
pub use action_report::{
ActionReportArgs, ActionReportCommand, ActionReportFormat, ActionReportVerifyArgs,
};
pub use calibrate::CalibrateArgs;
pub use calibrate_autoroute::{AutorouteCalibrationPolicy, CalibrateAutorouteArgs};
pub use config::ConfigArgs;
pub use daemon::{DaemonAction, DaemonArgs};
pub use detectors::{DetectorArgs, DetectorFormat};
pub use diff::DiffArgs;
pub use explain::ExplainArgs;
pub use guard::{GuardAction, GuardArgs};
pub use hook::HookCommand;
pub use limits::SourceLimitArgs;
pub use maintenance::{
BackendArgs, CompileExecutionPacksArgs, CompletionArgs, DoctorArgs, RepairArgs, UninstallArgs,
UpdateArgs,
};
pub use scan::{CliDedupScope, DaemonMode, DetectorMode, OutputFormat, ScanArgs, SeverityFilter};
pub use scan_system::{parse_space_bytes, ScanSystemArgs};
pub use watch::WatchArgs;
pub use watch::DEFAULT_WATCH_MAX_CONSECUTIVE_SCAN_FAILURES;
use clap::{FromArgMatches, Parser};
use std::ffi::OsString;
#[derive(clap::Args, Debug)]
pub struct BloomDiagnosticArgs {
#[arg(long, value_name = "PATH")]
pub fixture: std::path::PathBuf,
#[arg(long, value_name = "PATH")]
pub corpus_root: std::path::PathBuf,
}
#[derive(Parser)]
#[command(
name = "keyhog",
about = "KeyHog: The developer-first secret scanner.\nFind leaked credentials in your code before hackers do. Fast, accurate, and verifying.",
disable_version_flag = true
)]
pub struct Cli {
#[command(subcommand)]
pub command: Option<Command>,
#[arg(id = "build_version", short = 'V', long = "version")]
pub build_version: bool,
#[arg(long, requires = "build_version")]
pub full: bool,
}
#[derive(clap::Subcommand)]
pub enum Command {
#[command(verbatim_doc_comment)]
Scan(Box<ScanArgs>),
#[command(verbatim_doc_comment)]
Config(Box<ConfigArgs>),
#[command(verbatim_doc_comment, hide = true)]
ActionReport(ActionReportArgs),
#[command(verbatim_doc_comment, hide = true)]
CompileExecutionPacks(CompileExecutionPacksArgs),
#[command(verbatim_doc_comment)]
Hook {
#[command(subcommand)]
command: HookCommand,
},
#[command(verbatim_doc_comment)]
Detectors(DetectorArgs),
#[command(verbatim_doc_comment)]
Explain(ExplainArgs),
#[command(verbatim_doc_comment)]
Diff(DiffArgs),
#[command(verbatim_doc_comment)]
Calibrate(CalibrateArgs),
#[command(verbatim_doc_comment)]
CalibrateAutoroute(CalibrateAutorouteArgs),
#[command(verbatim_doc_comment)]
Watch(WatchArgs),
#[command(verbatim_doc_comment)]
Completion(CompletionArgs),
#[command(verbatim_doc_comment)]
Backend(BackendArgs),
#[command(verbatim_doc_comment)]
Doctor(DoctorArgs),
#[command(verbatim_doc_comment)]
BloomDiagnostic(BloomDiagnosticArgs),
#[command(verbatim_doc_comment)]
Update(UpdateArgs),
#[command(verbatim_doc_comment)]
Repair(RepairArgs),
#[command(verbatim_doc_comment)]
Uninstall(UninstallArgs),
#[command(verbatim_doc_comment)]
ScanSystem(ScanSystemArgs),
#[command(verbatim_doc_comment)]
Daemon(DaemonArgs),
#[command(verbatim_doc_comment)]
Guard(GuardArgs),
}
pub fn command() -> clap::Command {
use clap::CommandFactory;
let count = keyhog_core::embedded_detector_count();
let long_help = format!(
"Filter detectors by substring match (case-insensitive) against id, \
name, service, and keywords. Useful for finding detectors in the \
{count}-strong corpus (e.g. `keyhog detectors --search aws`)."
);
Cli::command()
.after_help(crate::exit_codes::help())
.mut_subcommand("scan", |sub| sub.after_help(crate::exit_codes::help()))
.mut_subcommand("detectors", move |sub| {
sub.mut_arg("search", move |arg| arg.long_help(long_help.clone()))
})
}
pub fn parse() -> Cli {
let _parse_span = keyhog_profile::span(keyhog_profile::Stage::Preprocess);
let matches = command().get_matches();
match cli_from_matches(&matches) {
Ok(cli) => cli,
Err(err) => err.exit(),
}
}
pub fn try_parse_from<I, T>(args: I) -> Result<Cli, clap::Error>
where
I: IntoIterator<Item = T>,
T: Into<OsString> + Clone,
{
let _parse_span = keyhog_profile::span(keyhog_profile::Stage::Preprocess);
let matches = command().try_get_matches_from(args)?;
cli_from_matches(&matches)
}
fn cli_from_matches(matches: &clap::ArgMatches) -> Result<Cli, clap::Error> {
let mut cli = Cli::from_arg_matches(matches)?;
mark_cli_value_sources(&mut cli, matches);
Ok(cli)
}
fn mark_cli_value_sources(cli: &mut Cli, matches: &clap::ArgMatches) {
use clap::parser::ValueSource;
match (&mut cli.command, matches.subcommand()) {
(Some(Command::Scan(args)), Some(("scan", subcommand_matches))) => {
args.mark_cli_value_sources(subcommand_matches);
}
(Some(Command::Config(args)), Some(("config", subcommand_matches))) => {
args.scan.mark_cli_value_sources(subcommand_matches);
}
(Some(Command::Detectors(args)), Some(("detectors", subcommand_matches))) => {
args.detectors_cli_explicit =
subcommand_matches.value_source("detectors") == Some(ValueSource::CommandLine);
}
(Some(Command::Explain(args)), Some(("explain", subcommand_matches))) => {
args.detectors_cli_explicit =
subcommand_matches.value_source("detectors") == Some(ValueSource::CommandLine);
}
(Some(Command::Watch(args)), Some(("watch", subcommand_matches))) => {
args.detectors_cli_explicit =
subcommand_matches.value_source("detectors") == Some(ValueSource::CommandLine);
}
(Some(Command::ScanSystem(args)), Some(("scan-system", subcommand_matches))) => {
args.detectors_cli_explicit =
subcommand_matches.value_source("detectors") == Some(ValueSource::CommandLine);
}
(Some(Command::Daemon(DaemonArgs { action })), Some(("daemon", daemon_matches))) => {
if let (
DaemonAction::Start {
detectors_cli_explicit,
..
},
Some(("start", start_matches)),
) = (action, daemon_matches.subcommand())
{
*detectors_cli_explicit =
start_matches.value_source("detectors") == Some(ValueSource::CommandLine);
}
}
_ => {}
}
}