From 1a121e3b4a4a96acd8617926a88daccee68ff8d1 Mon Sep 17 00:00:00 2001 From: Michael Lux Date: Mon, 17 Feb 2025 17:06:54 +0100 Subject: [PATCH 1/3] Rename validation profile to validation preset throughout. Replaced all occurrences of 'ValidationProfile' with 'ValidationPreset' to better align terminology with its purpose. Updated associated functions, traits, and usage in documentation and CLI arguments accordingly. --- README.md | 7 ++--- csaf | 2 +- csaf-lib/src/csaf/csaf2_0/validation.rs | 10 ++++---- csaf-lib/src/csaf/csaf2_1/validation.rs | 10 ++++---- csaf-lib/src/csaf/validation.rs | 34 ++++++++++++------------- csaf-validator/src/main.rs | 16 +++++++----- 6 files changed, 42 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 5c60e8e..304798a 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,8 @@ This is work-in-progress. If you want to build `csaf-validator` on your own, please install Rust (see https://rustup.rs) and then run ```bash -# make sure, submodules are up-to-date +# make sure submodules are up-to-date +git submodule init git submodule update --remote # run the tests @@ -35,7 +36,7 @@ Arguments: Options: -c, --csaf-version Version of CSAF to use [default: 2.0] - -p, --profile The profile to use [default: basic] + -p, --preset The validation preset (formerly known as "profile") to use [default: basic] -o, --only-test Run only the selected test -h, --help Print help -V, --version Print version @@ -48,7 +49,7 @@ Some examples to use are included below. Please note that the validation is not csaf-validator --csaf-version 2.0 my-csaf-2-0-document.json # validate a CSAF 2.0 document with profile full -csaf-validator --csaf-version 2.0 --profile full my-csaf-2-0-document.json +csaf-validator --csaf-version 2.0 --preset full my-csaf-2-0-document.json # validate a CSAF 2.1 document with a specific test csaf-validator --csaf-version 2.1 --only-test 6.1.34 my-csaf-2-1-document.json diff --git a/csaf b/csaf index b01a0a7..ed6e732 160000 --- a/csaf +++ b/csaf @@ -1 +1 @@ -Subproject commit b01a0a7a176f257e47bf6e4e3ac42d0def9620b1 +Subproject commit ed6e7320fb8cd7819df44638c1cb3c4803bd3c55 diff --git a/csaf-lib/src/csaf/csaf2_0/validation.rs b/csaf-lib/src/csaf/csaf2_0/validation.rs index 3a30948..8a6da0a 100644 --- a/csaf-lib/src/csaf/csaf2_0/validation.rs +++ b/csaf-lib/src/csaf/csaf2_0/validation.rs @@ -1,15 +1,15 @@ use super::product_helper::*; use super::schema::CommonSecurityAdvisoryFramework; -use crate::csaf::validation::{Test, Validatable, ValidationProfile}; +use crate::csaf::validation::{Test, Validatable, ValidationPreset}; use std::collections::{HashMap, HashSet}; use crate::csaf::helpers::find_duplicates; impl Validatable for CommonSecurityAdvisoryFramework { - fn profiles(&self) -> HashMap> { + fn presets(&self) -> HashMap> { HashMap::from([ - (ValidationProfile::Basic, Vec::from(["6.1.1", "6.1.2"])), - (ValidationProfile::Extended, Vec::from(["6.1.1", "6.1.2"])), - (ValidationProfile::Full, Vec::from(["6.1.1", "6.1.2"])), + (ValidationPreset::Basic, Vec::from(["6.1.1", "6.1.2"])), + (ValidationPreset::Extended, Vec::from(["6.1.1", "6.1.2"])), + (ValidationPreset::Full, Vec::from(["6.1.1", "6.1.2"])), ]) } diff --git a/csaf-lib/src/csaf/csaf2_1/validation.rs b/csaf-lib/src/csaf/csaf2_1/validation.rs index 9eea792..24e88ff 100644 --- a/csaf-lib/src/csaf/csaf2_1/validation.rs +++ b/csaf-lib/src/csaf/csaf2_1/validation.rs @@ -1,18 +1,18 @@ use super::product_helper::*; use super::schema::CommonSecurityAdvisoryFramework; use crate::csaf::helpers::find_duplicates; -use crate::csaf::validation::{Test, Validatable, ValidationProfile}; +use crate::csaf::validation::{Test, Validatable, ValidationPreset}; use std::collections::{HashMap, HashSet}; impl Validatable for CommonSecurityAdvisoryFramework { - fn profiles(&self) -> HashMap> { + fn presets(&self) -> HashMap> { HashMap::from([ ( - ValidationProfile::Basic, + ValidationPreset::Basic, Vec::from(["6.1.1", "6.1.2", "6.1.34"]), ), - (ValidationProfile::Extended, Vec::from(["6.1.1", "6.1.2"])), - (ValidationProfile::Full, Vec::from(["6.1.1", "6.1.2"])), + (ValidationPreset::Extended, Vec::from(["6.1.1", "6.1.2"])), + (ValidationPreset::Full, Vec::from(["6.1.1", "6.1.2"])), ]) } diff --git a/csaf-lib/src/csaf/validation.rs b/csaf-lib/src/csaf/validation.rs index 4da8782..c255ab9 100644 --- a/csaf-lib/src/csaf/validation.rs +++ b/csaf-lib/src/csaf/validation.rs @@ -4,28 +4,28 @@ use std::str::FromStr; pub enum ValidationError {} #[derive(Debug, PartialEq, Eq, Hash, Clone)] -pub enum ValidationProfile { +pub enum ValidationPreset { Basic, Extended, Full, } -impl FromStr for ValidationProfile { +impl FromStr for ValidationPreset { type Err = (); - fn from_str(input: &str) -> Result { + fn from_str(input: &str) -> Result { match input { - "basic" => Ok(ValidationProfile::Basic), - "extended" => Ok(ValidationProfile::Extended), - "full" => Ok(ValidationProfile::Full), + "basic" => Ok(ValidationPreset::Basic), + "extended" => Ok(ValidationPreset::Extended), + "full" => Ok(ValidationPreset::Full), _ => Err(()), } } } pub trait Validate { - /// Validates this object according to a validation profile - fn validate_profile(&'static self, profile: ValidationProfile); + /// Validates this object according to a validation preset + fn validate_preset(&'static self, preset: ValidationPreset); /// Validates this object according to a specific test ID. fn validate_by_test(&self, version: &str); @@ -38,10 +38,10 @@ pub type Test = /// This trait MUST be implemented by the struct that represents a CSAF document /// in the respective version. /// -/// It can then be used to validate documents with either [validate_by_profile] or [validate_by_test]. +/// It can then be used to validate documents with either [validate_by_preset] or [validate_by_test]. pub trait Validatable { - /// Returns a hashmap containing the test ID per profile - fn profiles(&self) -> HashMap>; + /// Returns a hashmap containing the test ID per preset + fn presets(&self) -> HashMap>; /// Returns a hashmap containing the test function per test ID fn tests(&self) -> HashMap<&str, Test>; @@ -49,16 +49,16 @@ pub trait Validatable { fn doc(&self) -> &VersionedDocument; } -/// Executes all tests of the specified [profile] against the [target] +/// Executes all tests of the specified [preset] against the [target] /// (which is of type [VersionedDocument], e.g. a CSAF 2.0 document). -pub fn validate_by_profile( +pub fn validate_by_preset( target: &impl Validatable, - profile: ValidationProfile, + preset: ValidationPreset, ) { - println!("Validating document with {:?} profile... \n", profile); + println!("Validating document with {:?} preset... \n", preset); // Loop through tests - if let Some(tests) = target.profiles().get(&profile) { + if let Some(tests) = target.presets().get(&preset) { for test_id in tests { println!("Executing Test {}... ", test_id); validate_by_test(target, test_id); @@ -66,7 +66,7 @@ pub fn validate_by_profile( println!() } } else { - println!("No tests found for profile") + println!("No tests found for preset") } } diff --git a/csaf-validator/src/main.rs b/csaf-validator/src/main.rs index 64fb9c5..c7c3eb2 100644 --- a/csaf-validator/src/main.rs +++ b/csaf-validator/src/main.rs @@ -1,7 +1,8 @@ +use std::str::FromStr; use anyhow::{bail, Result}; use csaf_lib::csaf::csaf2_0::loader::load_document as load_document_2_0; use csaf_lib::csaf::csaf2_1::loader::load_document as load_document_2_1; -use csaf_lib::csaf::validation::{validate_by_profile, validate_by_test, ValidationProfile}; +use csaf_lib::csaf::validation::{validate_by_preset, validate_by_test, ValidationPreset}; use clap::Parser; /// A validator for CSAF documents @@ -15,9 +16,9 @@ struct Args { #[arg(short, long, default_value = "2.0")] csaf_version: String, - /// The profile to use + /// The validation preset (formerly known as "profile") to use #[arg(short, long, default_value = "basic")] - profile: String, + preset: String, /// Run only the selected test #[arg(short, long)] @@ -26,7 +27,10 @@ struct Args { fn main() -> Result<()> { let args = Args::parse(); - let profile = ValidationProfile::Basic; + let preset = match ValidationPreset::from_str(args.preset.as_str()) { + Ok(preset) => preset, + Err(_) => bail!(format!("Invalid validation preset: {}", args.preset)), + }; // TODO: it would be nice to return the validatable from this match, but this is beyond my // rust generics knowledge, so a little bit of duplicate code here @@ -45,10 +49,10 @@ fn main() -> Result<()> { } else { let result = match args.csaf_version.as_str() { "2.0" => { - validate_by_profile(&load_document_2_0(args.path.as_str())?, profile) + validate_by_preset(&load_document_2_0(args.path.as_str())?, preset) } "2.1" => { - validate_by_profile(&load_document_2_1(args.path.as_str())?, profile) + validate_by_preset(&load_document_2_1(args.path.as_str())?, preset) } _ => bail!("invalid version"), }; From a25eb6b26039da85d81fefe8ea61af653d3cbb8c Mon Sep 17 00:00:00 2001 From: Christian Banse Date: Wed, 19 Feb 2025 00:23:18 +0100 Subject: [PATCH 2/3] Apply suggestions from code review Co-authored-by: tschmidtb51 <65305130+tschmidtb51@users.noreply.github.com> --- README.md | 2 +- csaf-validator/src/main.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 304798a..440442d 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ Arguments: Options: -c, --csaf-version Version of CSAF to use [default: 2.0] - -p, --preset The validation preset (formerly known as "profile") to use [default: basic] + -p, --preset The validation preset to use [default: basic] -o, --only-test Run only the selected test -h, --help Print help -V, --version Print version diff --git a/csaf-validator/src/main.rs b/csaf-validator/src/main.rs index c7c3eb2..804c8c2 100644 --- a/csaf-validator/src/main.rs +++ b/csaf-validator/src/main.rs @@ -16,7 +16,7 @@ struct Args { #[arg(short, long, default_value = "2.0")] csaf_version: String, - /// The validation preset (formerly known as "profile") to use + /// The validation preset to use #[arg(short, long, default_value = "basic")] preset: String, From b013f7c0a3d9b107b61913342f0d1e1dcdc21878 Mon Sep 17 00:00:00 2001 From: Michael Lux Date: Mon, 24 Feb 2025 16:27:16 +0100 Subject: [PATCH 3/3] Update CLI option and refactor test handling logic Renamed the `--only-test` option to `--test-id` for clarity and updated its functionality to perform multiple tests. Simplified the main logic by introducing a `process_document` function to handle tests and validation presets for better modularization and readability. --- README.md | 6 ++-- csaf-validator/src/main.rs | 60 ++++++++++++++++++-------------------- 2 files changed, 32 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index 440442d..ff2cb56 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Arguments: Options: -c, --csaf-version Version of CSAF to use [default: 2.0] -p, --preset The validation preset to use [default: basic] - -o, --only-test Run only the selected test + -t, --test-id Run only the selected tests, may be specified multiple times -h, --help Print help -V, --version Print version ``` @@ -51,6 +51,6 @@ csaf-validator --csaf-version 2.0 my-csaf-2-0-document.json # validate a CSAF 2.0 document with profile full csaf-validator --csaf-version 2.0 --preset full my-csaf-2-0-document.json -# validate a CSAF 2.1 document with a specific test -csaf-validator --csaf-version 2.1 --only-test 6.1.34 my-csaf-2-1-document.json +# validate a CSAF 2.1 document with one specific test +csaf-validator --csaf-version 2.1 --test-id 6.1.34 my-csaf-2-1-document.json ``` diff --git a/csaf-validator/src/main.rs b/csaf-validator/src/main.rs index 804c8c2..77642a9 100644 --- a/csaf-validator/src/main.rs +++ b/csaf-validator/src/main.rs @@ -2,7 +2,7 @@ use std::str::FromStr; use anyhow::{bail, Result}; use csaf_lib::csaf::csaf2_0::loader::load_document as load_document_2_0; use csaf_lib::csaf::csaf2_1::loader::load_document as load_document_2_1; -use csaf_lib::csaf::validation::{validate_by_preset, validate_by_test, ValidationPreset}; +use csaf_lib::csaf::validation::{validate_by_preset, validate_by_test, Validatable, ValidationPreset}; use clap::Parser; /// A validator for CSAF documents @@ -20,43 +20,41 @@ struct Args { #[arg(short, long, default_value = "basic")] preset: String, - /// Run only the selected test - #[arg(short, long)] - only_test: Option, + /// Run only the selected tests, may be specified multiple times + #[arg(short, long, action = clap::ArgAction::Append)] + test_id: Vec, } fn main() -> Result<()> { let args = Args::parse(); - let preset = match ValidationPreset::from_str(args.preset.as_str()) { - Ok(preset) => preset, - Err(_) => bail!(format!("Invalid validation preset: {}", args.preset)), - }; - // TODO: it would be nice to return the validatable from this match, but this is beyond my - // rust generics knowledge, so a little bit of duplicate code here - if let Some(test_id) = args.only_test { - let result = match args.csaf_version.as_str() { - "2.0" => { - validate_by_test(&load_document_2_0(args.path.as_str())?, test_id.as_str()) - } - "2.1" => { - validate_by_test(&load_document_2_1(args.path.as_str())?, test_id.as_str()) - } - _ => bail!("invalid version"), - }; + match args.csaf_version.as_str() { + "2.0" => { + process_document(load_document_2_0(args.path.as_str())?, &args) + } + "2.1" => { + process_document(load_document_2_1(args.path.as_str())?, &args) + } + _ => bail!(format!("Invalid CSAF version: {}", args.csaf_version)), + } +} - Ok(result) +fn process_document(document: T, args: &Args) -> Result<()> +where + T: Validatable, +{ + if !args.test_id.is_empty() { + for test_id in &args.test_id { + println!("\nExecuting Test {}... ", test_id); + validate_by_test(&document, test_id.as_str()); + } + Ok(()) } else { - let result = match args.csaf_version.as_str() { - "2.0" => { - validate_by_preset(&load_document_2_0(args.path.as_str())?, preset) - } - "2.1" => { - validate_by_preset(&load_document_2_1(args.path.as_str())?, preset) - } - _ => bail!("invalid version"), + let preset = match ValidationPreset::from_str(args.preset.as_str()) { + Ok(preset) => preset, + Err(_) => bail!(format!("Invalid validation preset: {}", args.preset)), }; - - Ok(result) + validate_by_preset(&document, preset); + Ok(()) } }