From aa29ee0e991426bb5f4e91507ec06c1a36c9e0e7 Mon Sep 17 00:00:00 2001 From: Mick Grove Date: Mon, 23 Feb 2026 22:34:23 -0700 Subject: [PATCH] added '--fast' mode which sets maximum scan speed. Omits git commit context and will not base64 decode --- CHANGELOG.md | 1 + README.md | 8 ++++++++ src/cli/commands/scan.rs | 9 +++++++++ src/direct_validate.rs | 1 + src/main.rs | 1 + src/reporter.rs | 1 + src/reporter/json_format.rs | 1 + tests/int_allowlist.rs | 1 + tests/int_bitbucket.rs | 1 + tests/int_dedup.rs | 1 + tests/int_github.rs | 1 + tests/int_gitlab.rs | 2 ++ tests/int_redact.rs | 1 + tests/int_slack.rs | 2 ++ tests/int_validation_cache.rs | 1 + tests/int_vulnerable_files.rs | 2 ++ 16 files changed, 34 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 96ab59f..d6ebada 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. ## [v1.85.0] +- Added `--fast` mode: sets `--commit-metadata=false` and `--no-base64` for maximum scan speed. Findings will omit Git commit context (author, date, commit hash) and will not include Base64-decoded secrets. - SQLite database scanning: kingfisher now detects and extracts SQLite files (`.db`, `.sqlite`, `.sqlite3`, etc.), dumping each table as SQL text with named columns so secrets stored in database rows are scannable. Controlled by the existing `--extract-archives` flag. - Python bytecode (.pyc) scanning: extracts string constants from compiled Python (`.pyc`, `.pyo`) files via marshal parsing so secrets embedded in bytecode are scannable. Controlled by `--extract-archives`. - Performance: pipelined ODB enumeration — scanning now begins while blob OIDs are still being discovered, overlapping I/O with pattern matching. diff --git a/README.md b/README.md index 5899efa..c53a717 100644 --- a/README.md +++ b/README.md @@ -312,6 +312,10 @@ kingfisher scan /path/to/code # Scan without validation kingfisher scan ~/src/myrepo --no-validate +# Fast mode: run as fast as possible by disabling Git commit metadata and Base64 decoding +# (findings omit commit context and Base64-encoded secrets) +kingfisher scan ~/src/myrepo --fast + # Display only secrets confirmed active by third‑party APIs kingfisher scan /path/to/repo --only-valid @@ -394,6 +398,10 @@ cat /path/to/file.py | kingfisher scan - # Limit maximum file size scanned (default: 256 MB) kingfisher scan /some/file --max-file-size 500 +# Fast mode: equivalent to --commit-metadata=false --no-base64 for maximum speed +# No Git commit metadata (author, date, hash) or Base64 decoding in findings +kingfisher scan /path/to/repo --fast + # Scan using a rule family kingfisher scan /path/to/repo --rule kingfisher.aws diff --git a/src/cli/commands/scan.rs b/src/cli/commands/scan.rs index 49a3e35..d77a8ac 100644 --- a/src/cli/commands/scan.rs +++ b/src/cli/commands/scan.rs @@ -151,6 +151,10 @@ pub struct ScanArgs { #[arg(global = true, long, default_value_t = false)] pub no_base64: bool, + /// Fast mode: equivalent to --commit-metadata=false --no-base64 + #[arg(global = true, long, default_value_t = false)] + pub fast: bool, + /// Timeout for Git repository scanning in seconds #[arg(global = true, long, default_value_t = 1800, value_name = "SECONDS")] pub git_repo_timeout: u64, @@ -486,6 +490,11 @@ impl ScanCommandArgs { self.scan_args.no_dedup = true; } + if self.scan_args.fast { + self.scan_args.no_base64 = true; + self.scan_args.input_specifier_args.commit_metadata = false; + } + if self.scan_args.access_map && self.scan_args.no_validate { bail!("--access-map cannot be used with --no-validate"); } diff --git a/src/direct_validate.rs b/src/direct_validate.rs index a7da825..a962a07 100644 --- a/src/direct_validate.rs +++ b/src/direct_validate.rs @@ -961,6 +961,7 @@ pub(crate) fn create_minimal_scan_args() -> crate::cli::commands::scan::ScanArgs skip_aws_account_file: None, output_args: OutputArgs { output: None, format: ReportOutputFormat::Pretty }, no_base64: false, + fast: false, no_inline_ignore: false, no_ignore_if_contains: false, validation_timeout: 10, diff --git a/src/main.rs b/src/main.rs index a374f30..77bebe8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -577,6 +577,7 @@ fn create_default_scan_args() -> cli::commands::scan::ScanArgs { skip_aws_account_file: None, output_args: OutputArgs { output: None, format: ReportOutputFormat::Pretty }, no_base64: false, + fast: false, no_inline_ignore: false, no_ignore_if_contains: false, validation_timeout: 10, diff --git a/src/reporter.rs b/src/reporter.rs index 9f4219c..779a25b 100644 --- a/src/reporter.rs +++ b/src/reporter.rs @@ -1781,6 +1781,7 @@ mod tests { view_report: false, redact: false, no_base64: false, + fast: false, git_repo_timeout: 1_800, output_args: OutputArgs { output: None, format: ReportOutputFormat::Pretty }, baseline_file: None, diff --git a/src/reporter/json_format.rs b/src/reporter/json_format.rs index b262462..8fabe4e 100644 --- a/src/reporter/json_format.rs +++ b/src/reporter/json_format.rs @@ -193,6 +193,7 @@ mod tests { skip_aws_account: Vec::new(), skip_aws_account_file: None, no_base64: false, + fast: false, no_inline_ignore: false, no_ignore_if_contains: false, validation_timeout: 10, diff --git a/tests/int_allowlist.rs b/tests/int_allowlist.rs index cddaac9..e684b14 100644 --- a/tests/int_allowlist.rs +++ b/tests/int_allowlist.rs @@ -155,6 +155,7 @@ fn run_skiplist(skip_regex: Vec, skip_skipword: Vec) -> Result Result<()> { skip_aws_account: Vec::new(), skip_aws_account_file: None, no_base64: false, + fast: false, extra_ignore_comments: Vec::new(), no_inline_ignore: false, no_ignore_if_contains: false, diff --git a/tests/int_dedup.rs b/tests/int_dedup.rs index b2ac6a5..cd9d498 100644 --- a/tests/int_dedup.rs +++ b/tests/int_dedup.rs @@ -174,6 +174,7 @@ rules: skip_aws_account: Vec::new(), skip_aws_account_file: None, no_base64: false, + fast: false, extra_ignore_comments: Vec::new(), no_inline_ignore: false, no_ignore_if_contains: false, diff --git a/tests/int_github.rs b/tests/int_github.rs index e388699..97a23c8 100644 --- a/tests/int_github.rs +++ b/tests/int_github.rs @@ -161,6 +161,7 @@ fn test_github_remote_scan() -> Result<()> { skip_aws_account: Vec::new(), skip_aws_account_file: None, no_base64: false, + fast: false, extra_ignore_comments: Vec::new(), no_inline_ignore: false, no_ignore_if_contains: false, diff --git a/tests/int_gitlab.rs b/tests/int_gitlab.rs index 4603a30..39e7b5b 100644 --- a/tests/int_gitlab.rs +++ b/tests/int_gitlab.rs @@ -160,6 +160,7 @@ fn test_gitlab_remote_scan() -> Result<()> { skip_aws_account: Vec::new(), skip_aws_account_file: None, no_base64: false, + fast: false, no_inline_ignore: false, no_ignore_if_contains: false, validation_retries: 1, @@ -325,6 +326,7 @@ fn test_gitlab_remote_scan_no_history() -> Result<()> { skip_aws_account: Vec::new(), skip_aws_account_file: None, no_base64: false, + fast: false, extra_ignore_comments: Vec::new(), no_inline_ignore: false, no_ignore_if_contains: false, diff --git a/tests/int_redact.rs b/tests/int_redact.rs index 70114aa..da52595 100644 --- a/tests/int_redact.rs +++ b/tests/int_redact.rs @@ -137,6 +137,7 @@ async fn test_redact_hashes_finding_values() -> Result<()> { skip_aws_account: Vec::new(), skip_aws_account_file: None, no_base64: false, + fast: false, extra_ignore_comments: Vec::new(), no_inline_ignore: false, no_ignore_if_contains: false, diff --git a/tests/int_slack.rs b/tests/int_slack.rs index b3660af..7a2c44f 100644 --- a/tests/int_slack.rs +++ b/tests/int_slack.rs @@ -143,6 +143,7 @@ impl TestContext { skip_aws_account: Vec::new(), skip_aws_account_file: None, no_base64: false, + fast: false, no_inline_ignore: false, no_ignore_if_contains: false, validation_retries: 1, @@ -294,6 +295,7 @@ async fn test_scan_slack_messages() -> Result<()> { skip_aws_account: Vec::new(), skip_aws_account_file: None, no_base64: false, + fast: false, extra_ignore_comments: Vec::new(), no_inline_ignore: false, no_ignore_if_contains: false, diff --git a/tests/int_validation_cache.rs b/tests/int_validation_cache.rs index 7ba7909..4407ba5 100644 --- a/tests/int_validation_cache.rs +++ b/tests/int_validation_cache.rs @@ -217,6 +217,7 @@ async fn test_validation_cache_and_depvars() -> Result<()> { skip_aws_account: Vec::new(), skip_aws_account_file: None, no_base64: false, + fast: false, extra_ignore_comments: Vec::new(), no_inline_ignore: false, no_ignore_if_contains: false, diff --git a/tests/int_vulnerable_files.rs b/tests/int_vulnerable_files.rs index 8f058bf..4c9b86d 100644 --- a/tests/int_vulnerable_files.rs +++ b/tests/int_vulnerable_files.rs @@ -160,6 +160,7 @@ impl TestContext { skip_aws_account: Vec::new(), skip_aws_account_file: None, no_base64: false, + fast: false, extra_ignore_comments: Vec::new(), no_inline_ignore: false, no_ignore_if_contains: false, @@ -301,6 +302,7 @@ impl TestContext { skip_aws_account: Vec::new(), skip_aws_account_file: None, no_base64: false, + fast: false, no_inline_ignore: false, no_ignore_if_contains: false, validation_retries: 1,