Skip to content

Commit

Permalink
re-nameing render of report
Browse files Browse the repository at this point in the history
  • Loading branch information
bxf12315 committed Feb 12, 2024
1 parent 3f39b2f commit 9a915e4
Show file tree
Hide file tree
Showing 6 changed files with 320 additions and 305 deletions.
1 change: 0 additions & 1 deletion csaf/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ walker-common = { version = "0.6.0-alpha.7", path = "../common" }
sequoia-openpgp = { version = "1", default-features = false }

csaf = { version = "0.5", default-features = false, optional = true }
clap = "4.4.8"
html-escape = "0.2.13"

[features]
Expand Down
1 change: 0 additions & 1 deletion csaf/csaf-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ comrak = { version = "0.20.0" }
csaf = { version = "0.5.0", default-features = false }
env_logger = "0.10.0"
flexible-time = "0.1.1"
html-escape = "0.2.13"
humantime = "2"
indicatif = { version = "0.17.3", features = [] }
indicatif-log-bridge = "0.2.1"
Expand Down
12 changes: 10 additions & 2 deletions csaf/csaf-cli/src/cmd/report/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{
use async_trait::async_trait;
use csaf_walker::{
discover::{AsDiscovered, DiscoveredAdvisory, DiscoveredContext, DiscoveredVisitor},
report::{render, DocumentKey, Duplicates, ReportResult},
report::{render_to_html, DocumentKey, Duplicates, ReportRenderOption, ReportResult},
retrieve::RetrievingVisitor,
validation::{ValidatedAdvisory, ValidationError, ValidationVisitor},
verification::{
Expand Down Expand Up @@ -159,7 +159,15 @@ impl Report {

fn render(render: RenderOptions, report: ReportResult) -> anyhow::Result<()> {
let mut out = std::fs::File::create(&render.output)?;
render::render_to_html(&mut out, &report, render.output, render.base_url)?;

render_to_html(
&mut out,
&report,
ReportRenderOption {
output: render.output,
base_url: render.base_url,
},
)?;

Ok(())
}
Expand Down
7 changes: 3 additions & 4 deletions csaf/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,11 @@
pub mod discover;
pub mod model;
pub mod report;
pub mod retrieve;
pub mod source;
pub mod validation;
pub mod visitors;
pub mod walker;

pub mod report;
#[cfg(feature = "csaf")]
pub mod verification;
pub mod visitors;
pub mod walker;
309 changes: 307 additions & 2 deletions csaf/src/report/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
pub mod render;

use crate::discover::DiscoveredAdvisory;
use std::borrow::Cow;
use std::collections::{BTreeMap, HashSet};
use std::fmt::{Display, Formatter};
use std::path::PathBuf;
use url::Url;
use walker_common::report;
use walker_common::utils::url::Urlify;

#[derive(Clone, Debug)]
Expand Down Expand Up @@ -36,3 +37,307 @@ impl DocumentKey {
}
}
}

#[derive(Clone, Debug)]
pub struct ReportRenderOption {
pub output: PathBuf,

pub base_url: Option<Url>,
}

pub fn render_to_html<W: std::io::Write>(
out: &mut W,
report: &ReportResult,
options: ReportRenderOption,
) -> anyhow::Result<()> {
report::render(
out,
"CSAF Report",
HtmlReport {
result: report,
base_url: &options.base_url,
},
&Default::default(),
)?;

Ok(())
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Title {
Duplicates,
Warnings,
Errors,
}

impl Display for Title {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::Duplicates => f.write_str("Duplicates"),
Self::Warnings => f.write_str("Warnings"),
Self::Errors => f.write_str("Errors"),
}
}
}

struct HtmlReport<'r> {
result: &'r ReportResult<'r>,
base_url: &'r Option<Url>,
}

impl HtmlReport<'_> {
fn render_duplicates(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let count = self.result.duplicates.duplicates.len();
let data = |f: &mut Formatter<'_>| {
for (k, v) in &self.result.duplicates.duplicates {
let (_url, label) = self.link_document(k);
writeln!(
f,
r#"
<tr>
<td><code>{label}<code></td>
<td class="text-right">{v}</td>
</tr>
"#,
label = html_escape::encode_text(&label),
)?;
}
Ok(())
};

if !self.result.duplicates.duplicates.is_empty() {
let total: usize = self.result.duplicates.duplicates.values().sum();

Self::render_table(
f,
count,
Title::Duplicates,
format!(
"{:?} duplicates URLs found, totalling {:?} redundant entries",
count, total
)
.as_str(),
data,
)?;
}
Ok(())
}

fn render_errors(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let count = self.result.errors.len();
let data = |f: &mut Formatter<'_>| {
for (k, v) in self.result.errors {
let (url, label) = self.link_document(k);

writeln!(
f,
r#"
<tr>
<td><a href="{url}" target="_blank" style="white-space: nowrap;">{label}</a></td>
<td><code>{v}</code></td>
</tr>
"#,
url = html_escape::encode_quoted_attribute(&url),
label = html_escape::encode_text(&label),
v = html_escape::encode_text(&v),
)?;
}
Ok(())
};
Self::render_table(
f,
count,
Title::Errors,
format!("{:?} error(s) detected", count).as_str(),
data,
)?;
Ok(())
}

fn render_table<F>(
f: &mut Formatter<'_>,
count: usize,
title: Title,
sub_title: &str,
data: F,
) -> std::fmt::Result
where
F: Fn(&mut Formatter<'_>) -> std::fmt::Result,
{
Self::title(f, title, count)?;
writeln!(f, "<p>{sub_title}</p>")?;
writeln!(
f,
r#"
<table class="table">
<thead>
<tr>
<th scope="col">File</th>
<th scope="col">{title}</th>
</tr>
</thead>
<tbody>
"#
)?;
data(f)?;
writeln!(f, "</tbody></table>")?;

Ok(())
}

fn render_warnings(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut count = 0;
for warnings in self.result.warnings.values() {
count += warnings.len();
}

let data = |f: &mut Formatter<'_>| {
for (k, v) in self.result.warnings {
let (url, label) = self.link_document(k);

writeln!(
f,
r#"
<tr>
<td><a href="{url}" target="_blank" style="white-space: nowrap;">{label}</a></td>
<td><ul>
"#,
url = html_escape::encode_quoted_attribute(&url),
label = html_escape::encode_text(&label),
)?;

for text in v {
writeln!(
f,
r#"
<li>
<code>{v}</code>
</li>
"#,
v = html_escape::encode_text(&text),
)?;
}

writeln!(
f,
r#"
</ul>
</td>
</tr>
"#
)?;
}

Ok(())
};
Self::render_table(
f,
count,
Title::Warnings,
format!("{:?} warning(s) detected", count).as_str(),
data,
)?;
Ok(())
}

fn gen_link(&self, key: &DocumentKey) -> Option<(String, String)> {
let label = key.url.clone();

// the full URL of the document
let url = key.distribution_url.join(&key.url).ok()?;

let url = match &self.base_url {
Some(base_url) => base_url
.make_relative(&url)
.unwrap_or_else(|| url.to_string()),
None => url.to_string(),
};

Some((url, label))
}

/// create a link towards a document, returning url and label
fn link_document(&self, key: &DocumentKey) -> (String, String) {
self.gen_link(key)
.unwrap_or_else(|| (key.url.clone(), key.url.clone()))
}

fn title(f: &mut Formatter<'_>, title: Title, count: usize) -> std::fmt::Result {
write!(f, "<h2>{title}")?;

let (class, text) = if count > 0 {
(
match title {
Title::Warnings => "text-bg-warning",
_ => "text-bg-danger",
},
count.to_string(),
)
} else {
("text-bg-light", "None".to_string())
};

write!(
f,
r#" <span class="badge {class} rounded-pill">{text}</span>"#,
)?;

writeln!(f, "</h2>")?;

Ok(())
}

fn render_total(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
writeln!(
f,
r#"
<h2>Summary</h2>
<dl class="row">
<dt class="col-sm-2">Total</dt>
<dd class="col-sm-10">{total}</dd>
</dl>
"#,
total = self.result.total
)
}
}

impl<'r> Display for HtmlReport<'r> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.render_total(f)?;
self.render_duplicates(f)?;
self.render_errors(f)?;
self.render_warnings(f)?;
Ok(())
}
}

#[cfg(test)]
mod test {
use super::*;
use reqwest::Url;

#[test]
fn test_link() {
let details = ReportResult {
total: 0,
duplicates: &Default::default(),
errors: &Default::default(),
warnings: &Default::default(),
};
let _output = PathBuf::default();
let base_url = Some(Url::parse("file:///foo/bar/").unwrap());
let report = HtmlReport {
result: &details,
base_url: &base_url,
};

let (url, _label) = report.link_document(&DocumentKey {
distribution_url: Url::parse("file:///foo/bar/distribution/").unwrap(),
url: "2023/cve.json".to_string(),
});

assert_eq!(url, "distribution/2023/cve.json");
}
}
Loading

0 comments on commit 9a915e4

Please sign in to comment.