use crate::csaf_traits::resolve_product_groups;
use crate::csaf_traits::{CsafTrait, FlagTrait, VulnerabilityTrait, WithOptionalGroupIds, WithOptionalProductIds};
use crate::schema::csaf2_1::schema::LabelOfTheFlag;
use crate::validation::ValidationError;
use std::collections::HashMap;
type VexJustificationInfo = (LabelOfTheFlag, usize, Option<String>);
pub fn test_6_1_33_multiple_flags_with_vex_codes_per_product(doc: &impl CsafTrait) -> Result<(), Vec<ValidationError>> {
let mut errors: Option<Vec<ValidationError>> = None;
for (vuln_i, vulnerability) in doc.get_vulnerabilities().iter().enumerate() {
let mut product_id_to_flags_map: HashMap<String, Vec<VexJustificationInfo>> = HashMap::new();
if let Some(flags) = vulnerability.get_flags() {
for (flag_i, flag) in flags.iter().enumerate() {
let label = flag.get_label();
if let Some(product_ids) = flag.get_product_ids() {
for product_id in product_ids {
product_id_to_flags_map
.entry(product_id.to_owned())
.or_default()
.push((label, flag_i, None));
}
}
if let Some(group_ids) = flag.get_group_ids() {
for group_id in group_ids {
if let Some(resolved_product_ids) = resolve_product_groups(doc, [group_id]) {
for product_id in resolved_product_ids {
product_id_to_flags_map.entry(product_id).or_default().push((
label,
flag_i,
Some(group_id.to_owned()),
));
}
}
}
}
}
}
for (product_id, flag_flag_i_group_ids_arr) in product_id_to_flags_map {
if flag_flag_i_group_ids_arr.len() > 1 {
let labels: Vec<LabelOfTheFlag> =
flag_flag_i_group_ids_arr.iter().map(|(label, _, _)| *label).collect();
for (label, flag_i, group_id) in flag_flag_i_group_ids_arr {
errors.get_or_insert_default().push(test_6_1_33_err_generator(
&product_id,
&labels,
label,
group_id,
vuln_i,
flag_i,
));
}
}
}
}
errors.map_or(Ok(()), Err)
}
fn test_6_1_33_err_generator(
product_id: &str,
labels: &[LabelOfTheFlag],
label: LabelOfTheFlag,
group_id: Option<String>,
vuln_i: usize,
flag_i: usize,
) -> ValidationError {
let labels_joined = {
let mut labels_str: Vec<_> = labels.iter().map(|l| l.to_string()).collect();
labels_str.sort();
labels_str.join(", ")
};
let group_id_str = {
if let Some(group_id) = group_id {
format!("(via group: {group_id})")
} else {
"".to_string()
}
};
ValidationError {
message: format!(
"Product '{product_id}' is associated with multiple flag labels: [{labels_joined}] {group_id_str}, it has flag label {label} on this path"
),
instance_path: format!("/vulnerabilities/{vuln_i}/flags/{flag_i}"),
}
}
crate::test_validation::impl_validator!(
ValidatorForTest6_1_33,
test_6_1_33_multiple_flags_with_vex_codes_per_product
);
#[cfg(test)]
mod tests {
use super::*;
use crate::csaf2_0::testcases::TESTS_2_0;
use crate::csaf2_1::testcases::TESTS_2_1;
#[test]
fn test_test_6_1_33() {
let case_01 = Err(vec![
test_6_1_33_err_generator(
"CSAFPID-9080700",
&[
LabelOfTheFlag::ComponentNotPresent,
LabelOfTheFlag::VulnerableCodeCannotBeControlledByAdversary,
],
LabelOfTheFlag::ComponentNotPresent,
Some("CSAFGID-0001".to_string()),
0,
0,
),
test_6_1_33_err_generator(
"CSAFPID-9080700",
&[
LabelOfTheFlag::ComponentNotPresent,
LabelOfTheFlag::VulnerableCodeCannotBeControlledByAdversary,
],
LabelOfTheFlag::VulnerableCodeCannotBeControlledByAdversary,
None,
0,
1,
),
]);
TESTS_2_0.test_6_1_33.expect(case_01.clone(), Ok(()));
TESTS_2_1.test_6_1_33.expect(case_01, Ok(()));
}
}