use std::sync::Arc;
use super::error::FindingBuildError;
use super::types::Finding;
impl Finding {
#[must_use]
pub fn group_by_target<'a>(
findings: &'a [Finding],
) -> std::collections::HashMap<&'a str, Vec<&'a Finding>> {
let mut map: std::collections::HashMap<&'a str, Vec<&'a Finding>> =
std::collections::HashMap::new();
for f in findings {
map.entry(f.target()).or_default().push(f);
}
for group in map.values_mut() {
group.sort_by(|a, b| b.severity.cmp(&a.severity));
}
map
}
pub fn merge_chain(a: &Finding, b: &Finding) -> Result<Finding, FindingBuildError> {
let severity = std::cmp::max(a.severity, b.severity);
let kind = if b.kind.is_actionable() {
b.kind
} else {
a.kind
};
let mut evidence = a.evidence.clone();
evidence.extend(b.evidence.iter().cloned());
let mut tags: Vec<Arc<str>> = a.tags.iter().chain(b.tags.iter()).cloned().collect();
tags.sort();
tags.dedup();
let mut cve_ids: Vec<Arc<str>> =
a.cve_ids.iter().chain(b.cve_ids.iter()).cloned().collect();
cve_ids.sort();
cve_ids.dedup();
let mut merged_cwes: Vec<Arc<str>> =
a.cwe_ids.iter().chain(b.cwe_ids.iter()).cloned().collect();
merged_cwes.sort();
merged_cwes.dedup();
let mut references: Vec<Arc<str>> = a
.references
.iter()
.chain(b.references.iter())
.cloned()
.collect();
references.sort();
references.dedup();
let mut matched_values: Vec<Arc<str>> = a
.matched_values
.iter()
.chain(b.matched_values.iter())
.cloned()
.collect();
matched_values.sort();
matched_values.dedup();
let cvss_score = match (a.cvss_score, b.cvss_score) {
(Some(x), Some(y)) => Some(x.max(y)),
(Some(x), None) | (None, Some(x)) => Some(x),
(None, None) => None,
};
let confidence = match (a.confidence, b.confidence) {
(Some(x), Some(y)) => Some(x.max(y)),
(Some(x), None) | (None, Some(x)) => Some(x),
(None, None) => None,
};
let mut builder = Finding::builder(a.scanner(), a.target(), severity)
.title(format!("{} → {}", a.title(), b.title()))
.detail(format!("{}\n---\n{}", a.detail(), b.detail()))
.kind(kind);
for ev in &evidence {
builder = builder.evidence(ev.clone());
}
for tag in &tags {
builder = builder.tag(tag.to_string());
}
for cve in &cve_ids {
builder = builder.cve(cve.to_string());
}
for cwe in &merged_cwes {
builder = builder.cwe(cwe.to_string());
}
for r in &references {
builder = builder.reference(r.to_string());
}
for mv in &matched_values {
builder = builder.matched_value(mv.to_string());
}
if let Some(s) = cvss_score {
builder = builder.cvss_score(s);
}
if let Some(c) = confidence {
builder = builder.confidence(c);
}
builder.build()
}
}