diff --git a/csaf b/csaf index 62fb87c..1726fcf 160000 --- a/csaf +++ b/csaf @@ -1 +1 @@ -Subproject commit 62fb87c6b6d0754bf0a4df20fc9e3d387497d0fe +Subproject commit 1726fcf10d6e444e6e65a696ac9198c981858d23 diff --git a/csaf-lib/src/csaf/test_helper.rs b/csaf-lib/src/csaf/test_helper.rs index 929271f..cd70113 100644 --- a/csaf-lib/src/csaf/test_helper.rs +++ b/csaf-lib/src/csaf/test_helper.rs @@ -1,7 +1,9 @@ -use std::collections::HashMap; -use crate::csaf::csaf2_1::loader::load_document; -use crate::csaf::csaf2_1::schema::CommonSecurityAdvisoryFramework; +use crate::csaf::csaf2_0::loader::load_document as load_document_20; +use crate::csaf::csaf2_0::schema::CommonSecurityAdvisoryFramework as Csaf20; +use crate::csaf::csaf2_1::loader::load_document as load_document_21; +use crate::csaf::csaf2_1::schema::CommonSecurityAdvisoryFramework as Csaf21; use crate::csaf::validation::{Test, ValidationError}; +use std::collections::HashMap; /// Generic test helper that loads all test files matching a specific test number pattern /// and runs positive and negative validations against a test function. @@ -14,22 +16,21 @@ use crate::csaf::validation::{Test, ValidationError}; /// /// This function assumes tests with filenames ending with numbers starting with "0" /// are negative tests, and those starting with "1" are positive tests. -pub fn run_csaf21_tests( - test_number: &str, - test_function: Test, - expected_errors: HashMap<&str, &ValidationError>, +fn run_csaf_tests( + pattern: &str, + file_prefix: &str, + document_loader: fn(&str) -> std::io::Result, + test_function: Test, + expected_errors: &HashMap<&str, &ValidationError>, ) { use glob::glob; - // Find all test files matching the pattern - let pattern = &format!("../csaf/csaf_2.1/test/validator/data/mandatory/oasis_csaf_tc-csaf_2_1-2024-6-1-{}-*.json", test_number); - let file_prefix = &format!("oasis_csaf_tc-csaf_2_1-2024-6-1-{}-", test_number); - // Load and test each file for entry in glob(pattern).expect("Failed to parse glob pattern") { if let Ok(path) = entry { // Extract the file suffix (e.g., "01", "02", etc.) let file_name = path.file_name().unwrap().to_string_lossy(); + println!("{}", file_name); let test_num = file_name .strip_prefix(file_prefix) .unwrap() @@ -37,11 +38,11 @@ pub fn run_csaf21_tests( .unwrap(); // Load the document - let doc = load_document(path.to_string_lossy().as_ref()).unwrap(); + let doc = document_loader(path.to_string_lossy().as_ref()).unwrap(); // Check if this is expected to be a negative or positive test case if test_num.starts_with('0') { - // Negative test case - should fail with specific error + // Negative test case - should fail with a specific error let expected_error = expected_errors.get(test_num).expect( &format!("Missing expected error definition for negative test case {}", test_num) ); @@ -62,4 +63,28 @@ pub fn run_csaf21_tests( } } } -} \ No newline at end of file +} + +pub fn run_csaf20_tests( + test_number: &str, + test_function: Test, + expected_errors: &HashMap<&str, &ValidationError>, +) { + // Find all test files matching the pattern + let file_prefix = &format!("oasis_csaf_tc-csaf_2_0-2021-6-1-{}-", test_number); + let pattern = &format!("../csaf/csaf_2.0/test/validator/data/mandatory/{}*.json", file_prefix); + + run_csaf_tests(pattern, file_prefix, load_document_20, test_function, expected_errors); +} + +pub fn run_csaf21_tests( + test_number: &str, + test_function: Test, + expected_errors: &HashMap<&str, &ValidationError>, +) { + // Find all test files matching the pattern + let file_prefix = &format!("oasis_csaf_tc-csaf_2_1-2024-6-1-{}-", test_number); + let pattern = &format!("../csaf/csaf_2.1/test/validator/data/mandatory/{}*.json", file_prefix); + + run_csaf_tests(pattern, file_prefix, load_document_21, test_function, expected_errors); +} diff --git a/csaf-lib/src/csaf/validations/mod.rs b/csaf-lib/src/csaf/validations/mod.rs index d295fcf..3b16988 100644 --- a/csaf-lib/src/csaf/validations/mod.rs +++ b/csaf-lib/src/csaf/validations/mod.rs @@ -1,5 +1,7 @@ pub mod test_6_1_01; pub mod test_6_1_02; +pub mod test_6_1_03; + pub mod test_6_1_34; pub mod test_6_1_35; pub mod test_6_1_36; diff --git a/csaf-lib/src/csaf/validations/test_6_1_01.rs b/csaf-lib/src/csaf/validations/test_6_1_01.rs index 3541fed..8cc18bc 100644 --- a/csaf-lib/src/csaf/validations/test_6_1_01.rs +++ b/csaf-lib/src/csaf/validations/test_6_1_01.rs @@ -13,8 +13,8 @@ pub fn test_6_1_01_missing_definition_of_product_id( Ok(()) }); } + let references = gather_product_references(doc); - for (ref_id, ref_path) in references.iter() { if !definitions_set.contains(ref_id) { return Err(ValidationError { @@ -30,33 +30,20 @@ pub fn test_6_1_01_missing_definition_of_product_id( #[cfg(test)] mod tests { use std::collections::HashMap; - use crate::csaf::csaf2_0::loader::load_document as load_20; - use crate::csaf::test_helper::run_csaf21_tests; + use crate::csaf::test_helper::{run_csaf20_tests, run_csaf21_tests}; use crate::csaf::validation::ValidationError; use crate::csaf::validations::test_6_1_01::test_6_1_01_missing_definition_of_product_id; - static EXPECTED_ERROR: &str = "Missing definition of product_id: CSAFPID-9080700"; - static EXPECTED_INSTANCE_PATH: &str = "/product_tree/product_groups/0/product_ids/0"; - #[test] - fn test_6_1_01_csaf_2_0() { - let doc = load_20("../csaf/csaf_2.0/test/validator/data/mandatory/oasis_csaf_tc-csaf_2_0-2021-6-1-01-01.json").unwrap(); - assert_eq!( - test_6_1_01_missing_definition_of_product_id(&doc), - Err(ValidationError { - message: EXPECTED_ERROR.to_string(), - instance_path: EXPECTED_INSTANCE_PATH.to_string(), - }) - ); - } - - #[test] - fn test_6_1_01_csaf_2_1() { - run_csaf21_tests("01", test_6_1_01_missing_definition_of_product_id, HashMap::from([ - ("01", &ValidationError { - message: EXPECTED_ERROR.to_string(), - instance_path: EXPECTED_INSTANCE_PATH.to_string(), - }) - ])); + fn test_6_1_01() { + let error01 = ValidationError { + message: "Missing definition of product_id: CSAFPID-9080700".to_string(), + instance_path: "/product_tree/product_groups/0/product_ids/0".to_string(), + }; + let errors = &HashMap::from([ + ("01", &error01) + ]); + run_csaf20_tests("01", test_6_1_01_missing_definition_of_product_id, &errors); + run_csaf21_tests("01", test_6_1_01_missing_definition_of_product_id, &errors); } } diff --git a/csaf-lib/src/csaf/validations/test_6_1_02.rs b/csaf-lib/src/csaf/validations/test_6_1_02.rs index 4f1b648..7d40e0e 100644 --- a/csaf-lib/src/csaf/validations/test_6_1_02.rs +++ b/csaf-lib/src/csaf/validations/test_6_1_02.rs @@ -27,34 +27,21 @@ pub fn test_6_1_02_multiple_definition_of_product_id( #[cfg(test)] mod tests { - use std::collections::HashMap; - use crate::csaf::csaf2_0::loader::load_document as load_20; - use crate::csaf::test_helper::run_csaf21_tests; + use crate::csaf::test_helper::{run_csaf20_tests, run_csaf21_tests}; use crate::csaf::validation::ValidationError; use crate::csaf::validations::test_6_1_02::test_6_1_02_multiple_definition_of_product_id; - - static EXPECTED_ERROR: &str = "Duplicate definition for product ID CSAFPID-9080700"; - static EXPECTED_INSTANCE_PATH: &str = "/product_tree/full_product_names/1/product_id"; - - #[test] - fn test_test_6_1_02_csaf_2_0() { - let doc = load_20("../csaf/csaf_2.0/test/validator/data/mandatory/oasis_csaf_tc-csaf_2_0-2021-6-1-02-01.json").unwrap(); - assert_eq!( - test_6_1_02_multiple_definition_of_product_id(&doc), - Err(ValidationError { - message: EXPECTED_ERROR.to_string(), - instance_path: EXPECTED_INSTANCE_PATH.to_string(), - }) - ) - } + use std::collections::HashMap; #[test] - fn test_test_6_1_02_csaf_2_1() { - run_csaf21_tests("02", test_6_1_02_multiple_definition_of_product_id, HashMap::from([ - ("01", &ValidationError { - message: EXPECTED_ERROR.to_string(), - instance_path: EXPECTED_INSTANCE_PATH.to_string(), - }) - ])); + fn test_test_6_1_02() { + let error01 = ValidationError { + message: "Duplicate definition for product ID CSAFPID-9080700".to_string(), + instance_path: "/product_tree/full_product_names/1/product_id".to_string(), + }; + let errors = HashMap::from([ + ("01", &error01) + ]); + run_csaf20_tests("02", test_6_1_02_multiple_definition_of_product_id, &errors); + run_csaf21_tests("02", test_6_1_02_multiple_definition_of_product_id, &errors); } } diff --git a/csaf-lib/src/csaf/validations/test_6_1_03.rs b/csaf-lib/src/csaf/validations/test_6_1_03.rs new file mode 100644 index 0000000..3e83a62 --- /dev/null +++ b/csaf-lib/src/csaf/validations/test_6_1_03.rs @@ -0,0 +1,172 @@ +use crate::csaf::getter_traits::{CsafTrait, ProductTrait, ProductTreeTrait, RelationshipTrait}; +use crate::csaf::validation::ValidationError; +use std::collections::HashMap; + +/// Find the first cycle in the given `relation_map`, if any. +/// +/// # Returns +/// - Product ID where the cycle was first detected +/// - String representation of the whole cycle detected +/// - Index of the CSAF relation containing the product ID where the cycle was first detected +pub fn find_cycle<'a>( + relation_map: &'a HashMap>, + product_id: &'a str, + visited: &mut Vec<&'a str>, +) -> Option<(String, Vec, usize)> { + if visited.contains(&product_id) { + return Some((product_id.to_string(), vec!(product_id.to_string()), 0)); + } else { + visited.push(product_id); + } + if let Some(next_vec) = relation_map.get(product_id) { + for (next, r_i) in next_vec { + match find_cycle(relation_map, next, visited) { + None => {} + Some((cycle_end, mut cycle, r_i_res)) => { + if cycle.len() == 1 || cycle_end != *cycle.last().unwrap() { + // Back-trace the cycle to the first node + cycle.push(product_id.to_string()); + if cycle_end == product_id { + // Reverse the cycle when it is complete + cycle.reverse(); + return Some((cycle_end, cycle, *r_i)); + } + } + return Some((cycle_end, cycle, r_i_res)); + } + } + } + } + visited.pop(); + None +} + +pub fn test_6_1_03_circular_definition_of_product_id( + doc: &impl CsafTrait, +) -> Result<(), ValidationError> { + if let Some(tree) = doc.get_product_tree().as_ref() { + let mut relation_map = HashMap::>::new(); + + for (i_r, r) in tree.get_relationships().iter().enumerate() { + let rel_prod_id = r.get_full_product_name().get_product_id(); + if r.get_product_reference() == rel_prod_id { + return Err(ValidationError { + message: "Relationship references itself via product_reference".to_string(), + instance_path: format!("/product_tree/relationships/{}/product_reference", i_r), + }) + } else if r.get_relates_to_product_reference() == rel_prod_id { + return Err(ValidationError { + message: "Relationship references itself via relates_to_product_reference".to_string(), + instance_path: format!("/product_tree/relationships/{}/relates_to_product_reference", i_r), + }) + } else { + match relation_map.get_mut(r.get_product_reference()) { + Some(v) => { + v.insert(r.get_relates_to_product_reference().to_owned(), i_r); + }, + None => { + relation_map.insert( + r.get_product_reference().to_owned(), + HashMap::from([(r.get_relates_to_product_reference().to_owned(), i_r)]) + ); + } + } + } + } + + // Perform cycle check + for product_id in relation_map.keys() { + let mut vec: Vec<&str> = vec!(); + if let Some((_, cycle, relation_index)) = find_cycle(&relation_map, product_id, &mut vec) { + return Err(ValidationError { + message: format!("Found product relationship cycle: {}", cycle.join(" -> ")), + instance_path: format!("/product_tree/relationships/{}", relation_index), + }) + } + } + } + + Ok(()) +} + +#[cfg(test)] +mod tests { + use crate::csaf::test_helper::{run_csaf20_tests, run_csaf21_tests}; + use crate::csaf::validation::ValidationError; + use crate::csaf::validations::test_6_1_03::test_6_1_03_circular_definition_of_product_id; + use std::collections::HashMap; + + #[test] + fn test_test_6_1_03() { + let error01 = ValidationError { + message: "Relationship references itself via relates_to_product_reference".to_string(), + instance_path: "/product_tree/relationships/0/relates_to_product_reference".to_string(), + }; + let errors = HashMap::from([ + ("01", &error01) + ]); + run_csaf20_tests("03", test_6_1_03_circular_definition_of_product_id, &errors); + run_csaf21_tests("03", test_6_1_03_circular_definition_of_product_id, &errors); + } + + #[test] + fn test_find_cycle() { + // Create a relation map with a non-trivial cycle: B -> C -> D -> B + let mut relation_map = HashMap::new(); + + relation_map.insert( + "A".to_string(), + HashMap::from([("B".to_string(), 0)]) + ); + relation_map.insert( + "B".to_string(), + HashMap::from([("C".to_string(), 1), ("E".to_string(), 2)]) + ); + relation_map.insert( + "C".to_string(), + HashMap::from([("D".to_string(), 3), ("F".to_string(), 4)]) + ); + relation_map.insert( + "D".to_string(), + HashMap::from([("B".to_string(), 5)]) + ); + + // Also add some nodes that aren't part of the cycle + relation_map.insert( + "E".to_string(), + HashMap::from([("F".to_string(), 6)]) + ); + relation_map.insert( + "F".to_string(), + HashMap::from([("G".to_string(), 7)]) + ); + + // Test cycle detection starting from the first node + let mut visited = Vec::new(); + let result = super::find_cycle(&relation_map, "A", &mut visited); + assert!(result.is_some()); + let (cycle_end, cycle, relation_index) = result.unwrap(); + assert_eq!(cycle_end, "B"); + assert_eq!(cycle, vec!("B", "C", "D", "B")); + assert_eq!(relation_index, 1); + + // Test starting from a node that's part of the cycle + let mut visited = Vec::new(); + let result = super::find_cycle(&relation_map, "C", &mut visited); + assert!(result.is_some()); + let (cycle_end, cycle, relation_index) = result.unwrap(); + assert_eq!(cycle_end, "C"); + assert_eq!(cycle, vec!("C", "D", "B", "C")); + assert_eq!(relation_index, 3); + + // Test starting from a node that's not part of any cycle + let mut visited = Vec::new(); + let result = super::find_cycle(&relation_map, "E", &mut visited); + assert!(result.is_none()); + + // Test with empty visited Set and starting from a node not in the map + let mut visited = Vec::new(); + let result = super::find_cycle(&relation_map, "Z", &mut visited); + assert!(result.is_none()); + } +} diff --git a/csaf-lib/src/csaf/validations/test_6_1_34.rs b/csaf-lib/src/csaf/validations/test_6_1_34.rs index 512b288..571bf30 100644 --- a/csaf-lib/src/csaf/validations/test_6_1_34.rs +++ b/csaf-lib/src/csaf/validations/test_6_1_34.rs @@ -33,7 +33,7 @@ mod tests { run_csaf21_tests( "34", test_6_1_34_branches_recursion_depth, - HashMap::from([ + &HashMap::from([ ("01", &ValidationError { message: "Branches recursion depth too big (> 30)".to_string(), instance_path: "/product_tree/branches/0/branches/0/branches/0/branches/0\ diff --git a/csaf-lib/src/csaf/validations/test_6_1_35.rs b/csaf-lib/src/csaf/validations/test_6_1_35.rs index 4e7e197..0f4c522 100644 --- a/csaf-lib/src/csaf/validations/test_6_1_35.rs +++ b/csaf-lib/src/csaf/validations/test_6_1_35.rs @@ -71,7 +71,7 @@ mod tests { run_csaf21_tests( "35", test_6_1_35_contradicting_remediations, - HashMap::from([ + &HashMap::from([ ("01", &ValidationError { message: "Product CSAFPID-9080700 has contradicting remediations: no_fix_planned and vendor_fix".to_string(), instance_path: "/vulnerabilities/0/remediations/1".to_string(), diff --git a/csaf-lib/src/csaf/validations/test_6_1_36.rs b/csaf-lib/src/csaf/validations/test_6_1_36.rs index 00038d9..a771e8b 100644 --- a/csaf-lib/src/csaf/validations/test_6_1_36.rs +++ b/csaf-lib/src/csaf/validations/test_6_1_36.rs @@ -91,7 +91,7 @@ mod tests { run_csaf21_tests( "36", test_6_1_36_status_group_contradicting_remediation_categories, - HashMap::from([ + &HashMap::from([ ("01", &ValidationError { message: "Product CSAFPID-9080700 is listed as not affected but has conflicting remediation category vendor_fix".to_string(), instance_path: "/vulnerabilities/0/remediations/0".to_string() diff --git a/csaf-lib/src/csaf/validations/test_6_1_37.rs b/csaf-lib/src/csaf/validations/test_6_1_37.rs index 640896b..ad1d9a0 100644 --- a/csaf-lib/src/csaf/validations/test_6_1_37.rs +++ b/csaf-lib/src/csaf/validations/test_6_1_37.rs @@ -120,7 +120,7 @@ mod tests { fn test_test_6_1_37() { run_csaf21_tests( "37", - test_6_1_37_date_and_time, HashMap::from([ + test_6_1_37_date_and_time, &HashMap::from([ ("01", &ValidationError { message: "Invalid date-time string 2024-01-24 10:00:00.000Z, expected RFC3339-compliant format with non-empty timezone".to_string(), instance_path: "/document/tracking/initial_release_date".to_string(), diff --git a/csaf-lib/src/csaf/validations/test_6_1_38.rs b/csaf-lib/src/csaf/validations/test_6_1_38.rs index e59dc26..0001cc3 100644 --- a/csaf-lib/src/csaf/validations/test_6_1_38.rs +++ b/csaf-lib/src/csaf/validations/test_6_1_38.rs @@ -51,7 +51,7 @@ mod tests { instance_path: "/document/distribution/sharing_group/tlp/label".to_string(), }; - run_csaf21_tests("38", test_6_1_38_non_public_sharing_group_max_uuid, HashMap::from([ + run_csaf21_tests("38", test_6_1_38_non_public_sharing_group_max_uuid, &HashMap::from([ ("01", &expected_error), ("02", &expected_error), ("03", &expected_error), diff --git a/csaf-lib/src/csaf/validations/test_6_1_39.rs b/csaf-lib/src/csaf/validations/test_6_1_39.rs index 404927f..59be9f5 100644 --- a/csaf-lib/src/csaf/validations/test_6_1_39.rs +++ b/csaf-lib/src/csaf/validations/test_6_1_39.rs @@ -60,7 +60,7 @@ mod tests { instance_path: "/document/distribution/sharing_group/id".to_string(), }; - run_csaf21_tests("39", test_6_1_39_public_sharing_group_with_no_max_uuid, HashMap::from([ + run_csaf21_tests("39", test_6_1_39_public_sharing_group_with_no_max_uuid, &HashMap::from([ ("01", &expected_error), ("02", &expected_error), ])); diff --git a/csaf-lib/src/csaf/validations/test_6_1_40.rs b/csaf-lib/src/csaf/validations/test_6_1_40.rs index 3a0b88b..64e7638 100644 --- a/csaf-lib/src/csaf/validations/test_6_1_40.rs +++ b/csaf-lib/src/csaf/validations/test_6_1_40.rs @@ -64,7 +64,7 @@ mod tests { fn test_test_6_1_40() { run_csaf21_tests( "40", - test_6_1_40_invalid_sharing_group_name, HashMap::from([ + test_6_1_40_invalid_sharing_group_name, &HashMap::from([ ("01", &ValidationError { message: format!("Sharing group name \"{}\" is prohibited without max UUID.", NAME_PUBLIC), instance_path: "/document/distribution/sharing_group/name".to_string() diff --git a/csaf-lib/src/csaf/validations/test_6_1_41.rs b/csaf-lib/src/csaf/validations/test_6_1_41.rs index efe6004..f659f15 100644 --- a/csaf-lib/src/csaf/validations/test_6_1_41.rs +++ b/csaf-lib/src/csaf/validations/test_6_1_41.rs @@ -67,7 +67,7 @@ mod tests { fn test_test_6_1_41() { run_csaf21_tests( "41", - test_6_1_41_missing_sharing_group_name, HashMap::from([ + test_6_1_41_missing_sharing_group_name, &HashMap::from([ ("01", &ValidationError { message: format!("Max UUID requires sharing group name to be \"{}\".", NAME_PUBLIC), instance_path: "/document/distribution/sharing_group/name".to_string() diff --git a/csaf-lib/src/csaf/validations/test_6_1_42.rs b/csaf-lib/src/csaf/validations/test_6_1_42.rs index c931467..41b47c8 100644 --- a/csaf-lib/src/csaf/validations/test_6_1_42.rs +++ b/csaf-lib/src/csaf/validations/test_6_1_42.rs @@ -72,7 +72,7 @@ mod tests { fn test_test_6_1_42() { run_csaf21_tests( "42", - test_6_1_42_purl_consistency, HashMap::from([ + test_6_1_42_purl_consistency, &HashMap::from([ ("01", &ValidationError { message: ERROR_MESSAGE.to_string(), instance_path: "/product_tree/full_product_names/0/product_identification_helper/purls/1".to_string(), diff --git a/csaf-lib/src/csaf/validations/test_6_1_43.rs b/csaf-lib/src/csaf/validations/test_6_1_43.rs index 6d8c5bb..ea88846 100644 --- a/csaf-lib/src/csaf/validations/test_6_1_43.rs +++ b/csaf-lib/src/csaf/validations/test_6_1_43.rs @@ -41,7 +41,7 @@ mod tests { run_csaf21_tests( "43", - test_6_1_43_multiple_stars_in_model_number, HashMap::from([ + test_6_1_43_multiple_stars_in_model_number, &HashMap::from([ ("01", &expected_error), ("02", &expected_error), ]) diff --git a/csaf-lib/src/csaf/validations/test_6_1_44.rs b/csaf-lib/src/csaf/validations/test_6_1_44.rs index d0a35f1..7e74579 100644 --- a/csaf-lib/src/csaf/validations/test_6_1_44.rs +++ b/csaf-lib/src/csaf/validations/test_6_1_44.rs @@ -41,7 +41,7 @@ mod tests { run_csaf21_tests( "44", - test_6_1_44_multiple_stars_in_serial_number, HashMap::from([ + test_6_1_44_multiple_stars_in_serial_number, &HashMap::from([ ("01", &expected_error), ("02", &expected_error), ]) diff --git a/csaf-lib/src/csaf/validations/test_6_1_45.rs b/csaf-lib/src/csaf/validations/test_6_1_45.rs index ce0edb9..8cee77f 100644 --- a/csaf-lib/src/csaf/validations/test_6_1_45.rs +++ b/csaf-lib/src/csaf/validations/test_6_1_45.rs @@ -95,7 +95,7 @@ mod tests { run_csaf21_tests( "45", - test_6_1_45_inconsistent_disclosure_date, HashMap::from([ + test_6_1_45_inconsistent_disclosure_date, &HashMap::from([ ("01", &expected_error), ("02", &expected_error), ("03", &expected_error), diff --git a/csaf-lib/src/csaf/validations/test_6_1_46.rs b/csaf-lib/src/csaf/validations/test_6_1_46.rs index 5e4cb75..fc94eaf 100644 --- a/csaf-lib/src/csaf/validations/test_6_1_46.rs +++ b/csaf-lib/src/csaf/validations/test_6_1_46.rs @@ -32,7 +32,7 @@ mod tests { fn test_test_6_1_46() { run_csaf21_tests( "46", - test_6_1_46_invalid_ssvc, HashMap::from([ + test_6_1_46_invalid_ssvc, &HashMap::from([ ("01", &ValidationError { message: "Invalid SSVC object: missing field `selections`".to_string(), instance_path: "/vulnerabilities/0/metrics/0/content/ssvc_v1".to_string(), diff --git a/csaf-lib/src/csaf/validations/test_6_1_47.rs b/csaf-lib/src/csaf/validations/test_6_1_47.rs index 89df8ae..c035065 100644 --- a/csaf-lib/src/csaf/validations/test_6_1_47.rs +++ b/csaf-lib/src/csaf/validations/test_6_1_47.rs @@ -77,7 +77,7 @@ mod tests { run_csaf21_tests( "47", test_6_1_47_inconsistent_ssvc_id, - HashMap::from([ + &HashMap::from([ ("01", &ValidationError { message: "The SSVC ID 'CVE-1900-0002' does not match the document ID, the CVE ID or any ID in the IDs array of the vulnerability".to_string(), instance_path: instance_path.clone(), diff --git a/csaf-lib/src/csaf/validations/test_6_1_48.rs b/csaf-lib/src/csaf/validations/test_6_1_48.rs index 225f6a9..e85315a 100644 --- a/csaf-lib/src/csaf/validations/test_6_1_48.rs +++ b/csaf-lib/src/csaf/validations/test_6_1_48.rs @@ -30,7 +30,7 @@ pub fn test_6_1_48_ssvc_decision_points( Some(_) => { // Get value indices of decision point let reference_indices = DP_VAL_LOOKUP.get(&dp_key).unwrap(); - // Index of last seen value + // Index of last-seen value let mut last_index: i32 = -1; // Check if all values exist and are correctly ordered for (i_val, value) in selection.values.iter().map(|v| v.deref()).enumerate() { @@ -107,7 +107,7 @@ mod tests { run_csaf21_tests( "48", test_6_1_48_ssvc_decision_points, - HashMap::from([ + &HashMap::from([ ("01", &ValidationError { message: "The SSVC decision point 'ssvc::Mission Impact' (version 1.0.0) doesn't have the value 'Degraded'".to_string(), instance_path: "/vulnerabilities/0/metrics/0/content/ssvc_v1/selections/0/values/1".to_string(), diff --git a/csaf-lib/src/csaf/validations/test_6_1_49.rs b/csaf-lib/src/csaf/validations/test_6_1_49.rs index ebd6f22..2722c1d 100644 --- a/csaf-lib/src/csaf/validations/test_6_1_49.rs +++ b/csaf-lib/src/csaf/validations/test_6_1_49.rs @@ -6,7 +6,7 @@ use chrono::{DateTime, FixedOffset}; /// 6.1.49 Inconsistent SSVC Timestamp /// /// For each vulnerability, it is tested that the SSVC `timestamp` is earlier or equal to the `date` -/// of the newest item of the `revision_history` if the document status is `final` or `interim`. +/// of the newest item in the `revision_history` if the document status is `final` or `interim`. pub fn test_6_1_49_inconsistent_ssvc_timestamp( doc: &impl CsafTrait, ) -> Result<(), ValidationError> { @@ -14,7 +14,7 @@ pub fn test_6_1_49_inconsistent_ssvc_timestamp( let tracking = document.get_tracking(); let status = tracking.get_status(); - // Check if document status is "final" or "interim" + // Check if the document status is "final" or "interim" if status != DocumentStatus::Final && status != DocumentStatus::Interim { return Ok(()); } @@ -92,7 +92,7 @@ mod tests { run_csaf21_tests( "49", test_6_1_49_inconsistent_ssvc_timestamp, - HashMap::from([ + &HashMap::from([ ("01", &ValidationError { message: "SSVC timestamp (2024-07-13T10:00:00+00:00) for vulnerability at index 0 is later than the newest revision date (2024-01-24T10:00:00+00:00)".to_string(), instance_path: instance_path.clone(),