forked from mirrors/kingfisher
- Condensed GitError formatting to report the exit status and the first informative lines from stdout/stderr, producing concise git clone failure logs. - Added support for scanning Google Cloud Storage buckets via --gcs-bucket, including optional prefixes and service-account authentication. - Added --skip-aws-account (now accepting comma-separated values) and --skip-aws-account-file to bypass live AWS validation for known canary/honey-token account IDs without triggering alerts. Kingfisher now ships with several canary AWS account IDs pre-seeded in the skip list and now reports matching findings as "Not Attempted" with the "Response" containing "(skip list entry)" so its clear that validation was intentionally skipped and why.
199 lines
5.6 KiB
Rust
199 lines
5.6 KiB
Rust
use std::io::IsTerminal;
|
|
|
|
use clap::{ArgAction, Args, Parser, Subcommand, ValueEnum};
|
|
use once_cell::sync::Lazy;
|
|
use strum::Display;
|
|
use sysinfo::{MemoryRefreshKind, RefreshKind, System};
|
|
use tracing::Level;
|
|
|
|
use crate::cli::commands::{
|
|
azure::AzureArgs, bitbucket::BitbucketArgs, gitea::GiteaArgs, github::GitHubArgs,
|
|
gitlab::GitLabArgs, huggingface::HuggingFaceArgs, rules::RulesArgs, scan::ScanArgs,
|
|
};
|
|
|
|
#[deny(missing_docs)]
|
|
#[derive(Parser, Debug)]
|
|
#[command(version = env!("CARGO_PKG_VERSION"))]
|
|
/// Kingfisher - Detect and validate secrets across files and full Git history
|
|
pub struct CommandLineArgs {
|
|
/// The command to execute
|
|
#[command(subcommand)]
|
|
pub command: Command,
|
|
|
|
/// Global arguments that apply to all subcommands
|
|
#[command(flatten)]
|
|
pub global_args: GlobalArgs,
|
|
}
|
|
impl CommandLineArgs {
|
|
/// Parse command-line arguments.
|
|
///
|
|
/// Automatically respects `NO_COLOR` and maps `--quiet` into disabling progress bars.
|
|
pub fn parse_args() -> Self {
|
|
// Use standard `Parser::parse` for simplicity
|
|
let mut args = CommandLineArgs::parse();
|
|
|
|
// Apply NO_COLOR environment variable
|
|
if std::env::var("NO_COLOR").is_ok() {
|
|
args.global_args.color = Mode::Never;
|
|
}
|
|
|
|
// If quiet is enabled, disable progress
|
|
if args.global_args.quiet {
|
|
args.global_args.progress = Mode::Never;
|
|
}
|
|
|
|
if let Some(suffix) = args.global_args.user_agent_suffix.as_mut() {
|
|
let trimmed = suffix.trim();
|
|
if trimmed.is_empty() {
|
|
args.global_args.user_agent_suffix = None;
|
|
} else if trimmed.len() != suffix.len() {
|
|
*suffix = trimmed.to_string();
|
|
}
|
|
}
|
|
|
|
args
|
|
}
|
|
}
|
|
|
|
/// Top-level subcommands
|
|
#[derive(Subcommand, Debug)]
|
|
pub enum Command {
|
|
/// Scan content for secrets and sensitive information
|
|
Scan(ScanArgs),
|
|
|
|
/// Interact with the GitHub API
|
|
#[command(name = "github")]
|
|
GitHub(GitHubArgs),
|
|
|
|
/// Interact with the GitLab API
|
|
#[command(name = "gitlab")]
|
|
GitLab(GitLabArgs),
|
|
|
|
/// Interact with the Gitea API
|
|
#[command(name = "gitea")]
|
|
Gitea(GiteaArgs),
|
|
|
|
/// Interact with the Bitbucket API
|
|
#[command(name = "bitbucket")]
|
|
Bitbucket(BitbucketArgs),
|
|
|
|
/// Interact with the Azure DevOps API
|
|
#[command(name = "azure")]
|
|
Azure(AzureArgs),
|
|
|
|
/// Interact with the Hugging Face Hub
|
|
#[command(name = "huggingface")]
|
|
HuggingFace(HuggingFaceArgs),
|
|
|
|
/// Manage rules
|
|
#[command(alias = "rule")]
|
|
Rules(RulesArgs),
|
|
|
|
/// Update the Kingfisher binary
|
|
#[command(name = "self-update")]
|
|
SelfUpdate,
|
|
}
|
|
|
|
pub static RAM_GB: Lazy<Option<f64>> = Lazy::new(|| {
|
|
if sysinfo::IS_SUPPORTED_SYSTEM {
|
|
let s = System::new_with_specifics(
|
|
RefreshKind::new().with_memory(MemoryRefreshKind::new().with_ram()),
|
|
);
|
|
Some(s.total_memory() as f64 / 1024.0 / 1024.0 / 1024.0)
|
|
} else {
|
|
None
|
|
}
|
|
});
|
|
|
|
/// Top-level global CLI arguments
|
|
#[derive(Args, Debug, Clone)]
|
|
#[command(next_help_heading = "Global Options")]
|
|
pub struct GlobalArgs {
|
|
/// Enable verbose output (up to 3 times for more detail)
|
|
#[arg(global = true, long = "verbose", short = 'v', action = ArgAction::Count)]
|
|
pub verbose: u8,
|
|
|
|
/// Suppress non-error messages and disable progress bars
|
|
#[arg(global = true, long, short)]
|
|
pub quiet: bool,
|
|
|
|
/// Ignore TLS certificate validation
|
|
#[arg(global = true, long)]
|
|
pub ignore_certs: bool,
|
|
|
|
/// Update the Kingfisher binary to the latest release
|
|
#[arg(global = true, long = "self-update", default_value_t = false)]
|
|
pub self_update: bool,
|
|
|
|
/// Disable automatic update checks
|
|
#[arg(global = true, long = "no-update-check", default_value_t = false)]
|
|
pub no_update_check: bool,
|
|
|
|
/// Append a custom suffix to the default Kingfisher user-agent string
|
|
#[arg(global = true, long = "user-agent-suffix", value_name = "SUFFIX")]
|
|
pub user_agent_suffix: Option<String>,
|
|
|
|
// Internal fields (not CLI arguments)
|
|
#[clap(skip)]
|
|
pub color: Mode,
|
|
|
|
#[clap(skip)]
|
|
pub progress: Mode,
|
|
}
|
|
|
|
impl Default for GlobalArgs {
|
|
fn default() -> Self {
|
|
Self {
|
|
verbose: 0,
|
|
quiet: false,
|
|
ignore_certs: false,
|
|
self_update: false,
|
|
no_update_check: false,
|
|
user_agent_suffix: None,
|
|
color: Mode::Auto,
|
|
progress: Mode::Auto,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl GlobalArgs {
|
|
pub fn use_color<T: IsTerminal>(&self, out: T) -> bool {
|
|
match self.color {
|
|
Mode::Never => false,
|
|
Mode::Always => true,
|
|
Mode::Auto => out.is_terminal(),
|
|
}
|
|
}
|
|
|
|
pub fn use_progress(&self) -> bool {
|
|
match self.progress {
|
|
Mode::Never => false,
|
|
Mode::Always => true,
|
|
Mode::Auto => std::io::stderr().is_terminal(),
|
|
}
|
|
}
|
|
|
|
pub fn log_level(&self) -> Level {
|
|
if self.quiet {
|
|
Level::INFO
|
|
} else {
|
|
match self.verbose {
|
|
0 => Level::INFO, // Default level if no `-v` is provided
|
|
1 => Level::DEBUG, // `-v`
|
|
2 => Level::TRACE, // `-vv`
|
|
_ => Level::TRACE, // `-vvv` or more
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Mode for enabling or disabling features based on terminal capabilities
|
|
/// Generic mode with `auto/never/always`.
|
|
#[derive(Copy, Clone, Debug, Display, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Default)]
|
|
#[strum(serialize_all = "kebab-case")]
|
|
pub enum Mode {
|
|
#[default]
|
|
Auto,
|
|
Never,
|
|
Always,
|
|
}
|