forked from mirrors/kingfisher
- --quiet now suppresses scan summaries and rule statistics unless --rule-stats is explicitly provided
- Added X Consumer key detection and validation
This commit is contained in:
parent
3458c37d33
commit
2fd6cd30e1
3 changed files with 103 additions and 35 deletions
|
|
@ -3,6 +3,7 @@
|
|||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [1.38.0]
|
||||
- `--quiet` now suppresses scan summaries and rule statistics unless `--rule-stats` is explicitly provided
|
||||
- Added X Consumer key detection and validation
|
||||
|
||||
## [1.37.0]
|
||||
|
|
|
|||
|
|
@ -38,14 +38,50 @@ macro_rules! safe_println {
|
|||
pub fn print_scan_summary(
|
||||
start_time: Instant,
|
||||
datastore: &Arc<Mutex<findings_store::FindingsStore>>,
|
||||
_global_args: &global::GlobalArgs,
|
||||
global_args: &global::GlobalArgs,
|
||||
args: &scan::ScanArgs,
|
||||
// inputs: &FilesystemEnumeratorResult,
|
||||
rules_db: &RulesDatabase,
|
||||
matcher_stats: &Mutex<MatcherStats>,
|
||||
profiler: Option<&ConcurrentRuleProfiler>,
|
||||
) {
|
||||
// let duration = start_time.elapsed();
|
||||
if global_args.quiet {
|
||||
if args.rule_stats {
|
||||
if let Some(prof) = profiler {
|
||||
let stats = prof.generate_report();
|
||||
if !stats.is_empty() {
|
||||
let name_w = stats.iter().map(|s| s.rule_name.len()).max().unwrap_or(4);
|
||||
let id_w = stats.iter().map(|s| s.rule_id.len()).max().unwrap_or(2);
|
||||
safe_println!("\n{:-^1$}", " Rule Performance Stats ", name_w + id_w + 47);
|
||||
safe_println!(
|
||||
"{: <name_w$} {: <id_w$} {: >8} {: >15} {: >15}",
|
||||
"Rule",
|
||||
"ID",
|
||||
"Matches",
|
||||
"Slowest",
|
||||
"Average",
|
||||
name_w = name_w,
|
||||
id_w = id_w
|
||||
);
|
||||
safe_println!("{:-<width$}", "", width = name_w + id_w + 49);
|
||||
for rs in stats {
|
||||
safe_println!(
|
||||
"{: <name_w$} {: <id_w$} {: >8} {: >15?} {: >15?}",
|
||||
rs.rule_name,
|
||||
rs.rule_id,
|
||||
rs.total_matches,
|
||||
rs.slowest_match_time,
|
||||
rs.average_match_time,
|
||||
name_w = name_w,
|
||||
id_w = id_w
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let ds = datastore.lock().unwrap();
|
||||
|
||||
let num_rules = rules_db.num_rules();
|
||||
|
|
@ -53,17 +89,12 @@ pub fn print_scan_summary(
|
|||
let mut sorted_findings: Vec<_> = findings_by_rule.into_iter().collect();
|
||||
sorted_findings.sort_by(|a, b| b.1.cmp(&a.1));
|
||||
let duration = start_time.elapsed();
|
||||
// let ds = datastore.lock().unwrap();
|
||||
|
||||
// Get all matches
|
||||
let all_matches = ds.get_matches();
|
||||
|
||||
// Count total findings
|
||||
let total_findings = if args.no_dedup {
|
||||
// When no_dedup is true, count each origin of validated matches as a separate finding
|
||||
all_matches.iter().fold(0, |count, msg| {
|
||||
let (origin_set, _, match_item) = &**msg;
|
||||
// If this is a validated match, count each origin as a separate finding
|
||||
if match_item.validation_success {
|
||||
count + origin_set.len()
|
||||
} else {
|
||||
|
|
@ -73,14 +104,13 @@ pub fn print_scan_summary(
|
|||
} else {
|
||||
ds.get_num_matches()
|
||||
};
|
||||
// Count successful and failed validations
|
||||
|
||||
let (successful_validations, failed_validations) =
|
||||
all_matches.iter().fold((0, 0), |(success, fail), msg| {
|
||||
let (origin_set, _, match_item) = &**msg;
|
||||
if match_item.validation_success {
|
||||
if match_item.validation_response_status != StatusCode::CONTINUE.as_u16() {
|
||||
if args.no_dedup {
|
||||
// Count each origin of a successful validation as a separate success
|
||||
(success + origin_set.len(), fail)
|
||||
} else {
|
||||
(success + 1, fail)
|
||||
|
|
@ -88,17 +118,14 @@ pub fn print_scan_summary(
|
|||
} else {
|
||||
(success, fail)
|
||||
}
|
||||
} else if match_item.validation_response_status != StatusCode::CONTINUE.as_u16() {
|
||||
(success, fail + 1)
|
||||
} else {
|
||||
if match_item.validation_response_status != StatusCode::CONTINUE.as_u16() {
|
||||
(success, fail + 1)
|
||||
} else {
|
||||
(success, fail)
|
||||
}
|
||||
(success, fail)
|
||||
}
|
||||
});
|
||||
let matcher_stats = matcher_stats.lock().unwrap();
|
||||
|
||||
// Generate JSON or JSONL output
|
||||
if args.output_args.format == ReportOutputFormat::Json
|
||||
|| args.output_args.format == ReportOutputFormat::Jsonl
|
||||
{
|
||||
|
|
@ -107,15 +134,11 @@ pub fn print_scan_summary(
|
|||
"successful_validations": successful_validations,
|
||||
"failed_validations": failed_validations,
|
||||
"rules_applied": num_rules,
|
||||
// "git_repositories": num_git_repos,
|
||||
// "commits": num_commits,
|
||||
"blobs_scanned": matcher_stats.blobs_scanned,
|
||||
// "files_read": num_files,
|
||||
"bytes_scanned": matcher_stats.bytes_scanned,
|
||||
"scan_duration": duration.as_secs_f64(),
|
||||
"findings_by_rule": sorted_findings
|
||||
});
|
||||
// only printing to stdout, not to the file itself
|
||||
safe_println!("{}", summary.to_string());
|
||||
} else if args.output_args.format == ReportOutputFormat::Pretty
|
||||
|| args.output_args.output.is_some()
|
||||
|
|
@ -133,37 +156,23 @@ pub fn print_scan_summary(
|
|||
failed_validations.separate_with_commas()
|
||||
);
|
||||
safe_println!(" |Rules Applied...............: {}", num_rules.separate_with_commas());
|
||||
// safe_println!(" |Git Repositories............: {}",
|
||||
// num_git_repos.separate_with_commas()); safe_println!(
|
||||
// "|__Commits...................: {}",
|
||||
// num_commits.separate_with_commas()
|
||||
// );
|
||||
safe_println!(
|
||||
" |__Blobs Scanned.............: {}",
|
||||
matcher_stats.blobs_scanned.separate_with_commas()
|
||||
);
|
||||
// safe_println!(" |Files Read..................: {}",
|
||||
// num_files.separate_with_commas());
|
||||
safe_println!(
|
||||
" |Bytes Scanned...............: {}",
|
||||
HumanBytes(matcher_stats.bytes_scanned)
|
||||
);
|
||||
safe_println!(
|
||||
" |Scan Duration...............: {}",
|
||||
// HumanDuration(duration),
|
||||
humantime::format_duration(duration)
|
||||
);
|
||||
safe_println!(" |Scan Duration...............: {}", humantime::format_duration(duration));
|
||||
}
|
||||
|
||||
if args.rule_stats {
|
||||
if let Some(prof) = profiler {
|
||||
let stats = prof.generate_report();
|
||||
if !stats.is_empty() {
|
||||
// Calculate dynamic column widths
|
||||
let name_w = stats.iter().map(|s| s.rule_name.len()).max().unwrap_or(4);
|
||||
let id_w = stats.iter().map(|s| s.rule_id.len()).max().unwrap_or(2);
|
||||
|
||||
// Header
|
||||
safe_println!("\n{:-^1$}", " Rule Performance Stats ", name_w + id_w + 47);
|
||||
safe_println!(
|
||||
"{: <name_w$} {: <id_w$} {: >8} {: >15} {: >15}",
|
||||
|
|
@ -177,7 +186,6 @@ pub fn print_scan_summary(
|
|||
);
|
||||
safe_println!("{:-<width$}", "", width = name_w + id_w + 49);
|
||||
|
||||
// Rows
|
||||
for rs in stats {
|
||||
safe_println!(
|
||||
"{: <name_w$} {: <id_w$} {: >8} {: >15?} {: >15?}",
|
||||
|
|
|
|||
59
tests/int_quiet.rs
Normal file
59
tests/int_quiet.rs
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
use assert_cmd::Command;
|
||||
use predicates::prelude::*;
|
||||
|
||||
const FORMATS: [&str; 4] = ["pretty", "json", "jsonl", "bson"];
|
||||
|
||||
fn contains_bytes(haystack: &[u8], needle: &[u8]) -> bool {
|
||||
haystack.windows(needle.len()).any(|window| window == needle)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scan_quiet_suppresses_summary() {
|
||||
for format in FORMATS {
|
||||
Command::cargo_bin("kingfisher")
|
||||
.unwrap()
|
||||
.env("NO_COLOR", "1")
|
||||
.args([
|
||||
"scan",
|
||||
"testdata/slack_tokens.properties",
|
||||
"--confidence=low",
|
||||
"--format",
|
||||
format,
|
||||
"--no-update-check",
|
||||
"--no-validate",
|
||||
"--quiet",
|
||||
])
|
||||
.assert()
|
||||
.code(200)
|
||||
.stdout(predicate::function(|out: &[u8]| !contains_bytes(out, b"Scan Summary")))
|
||||
.stdout(predicate::function(|out: &[u8]| {
|
||||
!contains_bytes(out, b"Rule Performance Stats")
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scan_quiet_with_rule_stats_prints_rule_stats() {
|
||||
for format in FORMATS {
|
||||
Command::cargo_bin("kingfisher")
|
||||
.unwrap()
|
||||
.env("NO_COLOR", "1")
|
||||
.args([
|
||||
"scan",
|
||||
"testdata/slack_tokens.properties",
|
||||
"--confidence=low",
|
||||
"--format",
|
||||
format,
|
||||
"--no-update-check",
|
||||
"--quiet",
|
||||
"--no-validate",
|
||||
"--rule-stats",
|
||||
])
|
||||
.assert()
|
||||
.code(200)
|
||||
.stdout(predicate::function(|out: &[u8]| !contains_bytes(out, b"Scan Summary")))
|
||||
.stdout(predicate::function(|out: &[u8]| {
|
||||
contains_bytes(out, b"Rule Performance Stats")
|
||||
}));
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue