diff --git a/csaf/csaf-cli/src/cmd/report/render.rs b/csaf/csaf-cli/src/cmd/report/render.rs
index 825d4b1..397c14a 100644
--- a/csaf/csaf-cli/src/cmd/report/render.rs
+++ b/csaf/csaf-cli/src/cmd/report/render.rs
@@ -39,33 +39,8 @@ struct HtmlReport<'r>(&'r ReportResult<'r>, &'r RenderOptions);
impl HtmlReport<'_> {
fn render_duplicates(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
- Self::title(f, "Duplicates", self.0.duplicates.duplicates.len())?;
-
- if !self.0.duplicates.duplicates.is_empty() {
- let num = self.0.duplicates.duplicates.len();
- let total: usize = self.0.duplicates.duplicates.values().sum();
- writeln!(
- f,
- "
{num} duplicates URLs found, totalling {total} redundant entries
",
- )?;
-
- writeln!(f, "The following URLs have duplicate entries:
")?;
-
- writeln!(
- f,
- r#"
-
-
-
- File |
- # Duplicates |
-
-
-
-
-"#
- )?;
-
+ let count = self.0.duplicates.duplicates.len();
+ let data = |f: &mut Formatter<'_>| {
for (k, v) in &self.0.duplicates.duplicates {
writeln!(
f,
@@ -78,45 +53,29 @@ impl HtmlReport<'_> {
k = html_escape::encode_text(&k),
)?;
}
+ Ok(())
+ };
+ if !self.0.duplicates.duplicates.is_empty() {
+ let total: usize = self.0.duplicates.duplicates.values().sum();
- writeln!(
+ Self::render_table(
f,
- r#"
-
-
-"#
+ count,
+ "Duplicates",
+ format!(
+ "{:?} duplicates URLs found, totalling {:?} redundant entries",
+ count, total
+ )
+ .as_str(),
+ data,
)?;
}
-
Ok(())
}
fn render_errors(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
- Self::title(f, "Errors", self.0.errors.len())?;
-
- if !self.0.errors.is_empty() {
- let num = self.0.errors.len();
- let s = match num {
- 1 => "",
- _ => "s",
- };
- writeln!(f, "{num} error{s} detected
")?;
-
- writeln!(
- f,
- r#"
-
-
-
- File |
- Error |
-
-
-
-
-"#
- )?;
-
+ let count = self.0.errors.len();
+ let data = |f: &mut Formatter<'_>| {
for (k, v) in self.0.errors {
let k: Cow = match (&self.1.base_url, Url::parse(k)) {
(Some(base_url), Ok(url)) => match base_url.make_relative(&url) {
@@ -138,16 +97,89 @@ impl HtmlReport<'_> {
v = html_escape::encode_text(&v),
)?;
}
+ Ok(())
+ };
+ Self::render_table(
+ f,
+ count,
+ "Error",
+ format!("{:?} error(s) detected", count).as_str(),
+ data,
+ )?;
+ Ok(())
+ }
+
+ fn render_table(
+ f: &mut Formatter<'_>,
+ count: usize,
+ title: &str,
+ sub_title: &str,
+ data: F,
+ ) -> std::fmt::Result
+ where
+ F: Fn(&mut Formatter<'_>) -> std::fmt::Result,
+ {
+ Self::title(f, title, count)?;
+ writeln!(f, "{sub_title}
")?;
+ writeln!(
+ f,
+ r#"
+
+
+
+ File |
+ {title} |
+
+
- writeln!(
- f,
- r#"
-
"#
- )?;
+ )?;
+ data(f)?;
+ writeln!(f, "
")?;
+
+ Ok(())
+ }
+
+ fn render_warnings(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ let mut count = 0;
+ for errors in self.0.warnings.values() {
+ count += errors.len();
}
+ let data = |f: &mut Formatter<'_>| {
+ for (k, v) in self.0.warnings {
+ let k: Cow = match (&self.1.base_url, Url::parse(k)) {
+ (Some(base_url), Ok(url)) => match base_url.make_relative(&url) {
+ Some(url) => Cow::Owned(url),
+ None => Cow::Borrowed(k),
+ },
+ _ => Cow::Borrowed(k),
+ };
+
+ for text in v {
+ writeln!(
+ f,
+ r#"
+
+ {k} |
+ {v} |
+
+ "#,
+ k = html_escape::encode_quoted_attribute(&k),
+ v = html_escape::encode_text(&text),
+ )?;
+ }
+ }
+ Ok(())
+ };
+ Self::render_table(
+ f,
+ count,
+ "Warning",
+ format!("{:?} warning(s) detected", count).as_str(),
+ data,
+ )?;
Ok(())
}
@@ -184,7 +216,7 @@ impl<'r> Display for HtmlReport<'r> {
self.render_duplicates(f)?;
self.render_errors(f)?;
-
+ self.render_warnings(f)?;
Ok(())
}
}
diff --git a/csaf/src/verification/check/informational_advisory/mod.rs b/csaf/src/verification/check/informational_advisory/mod.rs
index edb255e..c308972 100644
--- a/csaf/src/verification/check/informational_advisory/mod.rs
+++ b/csaf/src/verification/check/informational_advisory/mod.rs
@@ -10,7 +10,7 @@ pub fn check_vulnerabilities_not_exits(csaf: &Csaf) -> Vec {
}
Checking::new()
.require(
- "The casf file should not related to a vulnerability ",
+ "The csaf file should not related to a vulnerability ",
csaf.vulnerabilities.is_some(),
)
.done()
diff --git a/csaf/src/verification/check/security_incident_response/mod.rs b/csaf/src/verification/check/security_incident_response/mod.rs
index 9351429..d793d9d 100644
--- a/csaf/src/verification/check/security_incident_response/mod.rs
+++ b/csaf/src/verification/check/security_incident_response/mod.rs
@@ -3,7 +3,7 @@ use csaf::definitions::{NoteCategory, ReferenceCategory};
use csaf::Csaf;
pub fn check_csaf_document_notes(csaf: &Csaf) -> Vec {
- if !is_security_incident_response(csaf) && is_security_informational_advisory(csaf) {
+ if !is_security_incident_response(csaf) && !is_security_informational_advisory(csaf) {
return vec![];
}
let mut result = false;
@@ -24,7 +24,7 @@ pub fn check_csaf_document_notes(csaf: &Csaf) -> Vec {
}
pub fn check_csaf_document_references(csaf: &Csaf) -> Vec {
- if !is_security_incident_response(csaf) && is_security_informational_advisory(csaf) {
+ if !is_security_incident_response(csaf) && !is_security_informational_advisory(csaf) {
return vec![];
}
let mut result = false;
diff --git a/csaf/src/verification/check/vex/mod.rs b/csaf/src/verification/check/vex/mod.rs
index 3feb8c8..3cbe514 100644
--- a/csaf/src/verification/check/vex/mod.rs
+++ b/csaf/src/verification/check/vex/mod.rs
@@ -17,7 +17,7 @@ pub fn check_vulnerabilities_size(csaf: &Csaf) -> Vec {
result = false;
}
Checking::new()
- .require("The csaf file's Vulnerabilities is empty", result)
+ .require("The csaf file's Vulnerabilities is empty", !result)
.done()
}
@@ -108,7 +108,7 @@ pub fn check_vulnerabilities_cve_ids(csaf: &Csaf) -> Vec {
check_erroies.extend(
Checking::new()
.require(
- "The csaf file have a vulnerability",
+ "The csaf file have a vulnerability",
vuln.cve.is_none() | vuln.ids.is_none(),
)
.done(),
@@ -166,24 +166,24 @@ pub fn check_branches_relationships_product_match(csaf: &Csaf) -> Vec) {
+fn get_all_product_names(product_tree: &ProductTree, products: &mut HashSet) {
if let Some(relationships) = &product_tree.relationships {
for r in relationships {
let id = &r.full_product_name.product_id;
- products.push(id.clone().0);
+ products.insert(id.clone().0);
}
}
}
fn check_product(
- product_names: &mut [String],
+ product_names: &mut HashSet,
product_id_t: &ProductIdT,
erroies: &mut Vec,
) {
erroies.extend(
Checking::new()
.require(
- format!("product {:?} does not exits in product tree", &product_id_t),
+ format!("The product under the 'product status' section of the vulnerabilities division, identified as {:?}, is missing from the product tree.", &product_id_t),
product_names.contains(&product_id_t.0),
)
.done(),
@@ -195,10 +195,14 @@ pub fn check_all_products_v11ies_exits_in_product_tree(csaf: &Csaf) -> Vec Vec