Render compact output (one-line-per-finding-line for demos and CI). Returns exit code: 0 = pass, 1 = any findings present.
(findings: &[Finding], _policy_path: &str, _credentials_path: &str)
| 78 | /// Render compact output (one-line-per-finding-line for demos and CI). |
| 79 | /// Returns exit code: 0 = pass, 1 = any findings present. |
| 80 | pub fn render_compact(findings: &[Finding], _policy_path: &str, _credentials_path: &str) -> i32 { |
| 81 | let active: Vec<&Finding> = findings.iter().filter(|f| !f.accepted).collect(); |
| 82 | let accepted: Vec<&Finding> = findings.iter().filter(|f| f.accepted).collect(); |
| 83 | |
| 84 | for finding in &active { |
| 85 | for path in &finding.paths { |
| 86 | let FindingPath::Exfil(p) = path; |
| 87 | println!(" {} {}", "•".yellow(), format_path_line(&finding.query, p)); |
| 88 | } |
| 89 | if !finding.paths.is_empty() { |
| 90 | println!(); |
| 91 | } |
| 92 | } |
| 93 | |
| 94 | for finding in &accepted { |
| 95 | println!( |
| 96 | " {} {}", |
| 97 | "ACCEPTED".dimmed(), |
| 98 | category_label(&finding.query).dimmed() |
| 99 | ); |
| 100 | } |
| 101 | if !accepted.is_empty() { |
| 102 | println!(); |
| 103 | } |
| 104 | |
| 105 | let accepted_note = if accepted.is_empty() { |
| 106 | String::new() |
| 107 | } else { |
| 108 | format!(", {} accepted", accepted.len()) |
| 109 | }; |
| 110 | |
| 111 | let path_count: usize = active.iter().map(|f| f.paths.len()).sum(); |
| 112 | if path_count > 0 { |
| 113 | println!( |
| 114 | " {} {path_count} finding path(s) require review{accepted_note}", |
| 115 | " REVIEW ".black().bold().on_yellow() |
| 116 | ); |
| 117 | 1 |
| 118 | } else { |
| 119 | println!( |
| 120 | " {} no findings{accepted_note}", |
| 121 | " PASS ".white().bold().on_green() |
| 122 | ); |
| 123 | 0 |
| 124 | } |
| 125 | } |
| 126 | |
| 127 | // --------------------------------------------------------------------------- |
| 128 | // Full terminal report |