(paths: &[FindingPath])
| 231 | } |
| 232 | |
| 233 | fn render_paths(paths: &[FindingPath]) { |
| 234 | if paths.is_empty() { |
| 235 | return; |
| 236 | } |
| 237 | // Group paths by binary for compact display. |
| 238 | let mut by_binary: BTreeMap<&str, Vec<&crate::finding::ExfilPath>> = BTreeMap::new(); |
| 239 | for path in paths { |
| 240 | let FindingPath::Exfil(p) = path; |
| 241 | by_binary.entry(&p.binary).or_default().push(p); |
| 242 | } |
| 243 | for (binary, ps) in &by_binary { |
| 244 | println!(" Binary: {}", binary.cyan()); |
| 245 | let mut endpoints: BTreeSet<String> = BTreeSet::new(); |
| 246 | let mut methods: BTreeSet<String> = BTreeSet::new(); |
| 247 | for p in ps { |
| 248 | endpoints.insert(format!("{}:{}", p.endpoint_host, p.endpoint_port)); |
| 249 | if !p.method.is_empty() { |
| 250 | methods.insert(p.method.clone()); |
| 251 | } |
| 252 | } |
| 253 | println!( |
| 254 | " Endpoints: {}", |
| 255 | endpoints.iter().cloned().collect::<Vec<_>>().join(", ") |
| 256 | ); |
| 257 | if !methods.is_empty() { |
| 258 | println!( |
| 259 | " Methods: {}", |
| 260 | methods.iter().cloned().collect::<Vec<_>>().join(", ") |
| 261 | ); |
| 262 | } |
| 263 | } |
| 264 | println!(); |
| 265 | } |
| 266 | |
| 267 | // --------------------------------------------------------------------------- |
| 268 | // Tests |
no test coverage detected