#[derive(Debug, Clone, Default)]
pub struct VulnerabilityReportView {
pub actionable: Vec<VulnerabilityView>,
pub informational: Vec<VulnerabilityView>,
pub summary: VulnerabilitySummary,
}
impl VulnerabilityReportView {
pub fn counts_by_severity(&self) -> VulnerabilityCountsBySeverity {
let all = self.actionable.iter().chain(self.informational.iter());
let mut counts = VulnerabilityCountsBySeverity::default();
for v in all {
match v.severity {
SeverityView::Critical => counts.critical += 1,
SeverityView::High => counts.high += 1,
SeverityView::Medium => counts.medium += 1,
SeverityView::Low => counts.low += 1,
SeverityView::None => {}
}
}
counts
}
}
#[derive(Debug, Clone, Default)]
pub struct VulnerabilityCountsBySeverity {
pub critical: usize,
pub high: usize,
pub medium: usize,
pub low: usize,
}
#[derive(Debug, Clone)]
pub struct VulnerabilityView {
pub bom_ref: String,
pub id: String,
pub affected_component: String,
pub affected_component_name: String,
pub affected_version: String,
pub cvss_score: Option<f32>,
pub cvss_vector: Option<String>,
pub severity: SeverityView,
pub fixed_version: Option<String>,
pub description: Option<String>,
pub source_url: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum SeverityView {
Critical,
High,
Medium,
Low,
#[default]
None,
}
impl SeverityView {
pub fn as_str(&self) -> &'static str {
match self {
SeverityView::Critical => "CRITICAL",
SeverityView::High => "HIGH",
SeverityView::Medium => "MEDIUM",
SeverityView::Low => "LOW",
SeverityView::None => "NONE",
}
}
}
#[derive(Debug, Clone, Default)]
pub struct VulnerabilitySummary {
pub total_count: usize,
pub affected_package_count: usize,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_severity_view_as_str() {
assert_eq!(SeverityView::Critical.as_str(), "CRITICAL");
assert_eq!(SeverityView::High.as_str(), "HIGH");
assert_eq!(SeverityView::Medium.as_str(), "MEDIUM");
assert_eq!(SeverityView::Low.as_str(), "LOW");
assert_eq!(SeverityView::None.as_str(), "NONE");
}
#[test]
fn test_severity_view_ordering() {
assert!(SeverityView::Critical < SeverityView::High);
assert!(SeverityView::High < SeverityView::Medium);
assert!(SeverityView::Medium < SeverityView::Low);
assert!(SeverityView::Low < SeverityView::None);
}
#[test]
fn test_severity_view_default() {
assert_eq!(SeverityView::default(), SeverityView::None);
}
fn make_vuln(severity: SeverityView) -> VulnerabilityView {
VulnerabilityView {
bom_ref: String::new(),
id: String::new(),
affected_component: String::new(),
affected_component_name: String::new(),
affected_version: String::new(),
cvss_score: None,
cvss_vector: None,
severity,
fixed_version: None,
description: None,
source_url: None,
}
}
#[test]
fn test_counts_by_severity_all_zero() {
let report = VulnerabilityReportView::default();
let counts = report.counts_by_severity();
assert_eq!(counts.critical, 0);
assert_eq!(counts.high, 0);
assert_eq!(counts.medium, 0);
assert_eq!(counts.low, 0);
}
#[test]
fn test_counts_by_severity_only_actionable() {
let report = VulnerabilityReportView {
actionable: vec![
make_vuln(SeverityView::Critical),
make_vuln(SeverityView::High),
make_vuln(SeverityView::High),
make_vuln(SeverityView::Medium),
],
..Default::default()
};
let counts = report.counts_by_severity();
assert_eq!(counts.critical, 1);
assert_eq!(counts.high, 2);
assert_eq!(counts.medium, 1);
assert_eq!(counts.low, 0);
}
#[test]
fn test_counts_by_severity_only_informational() {
let report = VulnerabilityReportView {
informational: vec![
make_vuln(SeverityView::Low),
make_vuln(SeverityView::Low),
make_vuln(SeverityView::None),
],
..Default::default()
};
let counts = report.counts_by_severity();
assert_eq!(counts.critical, 0);
assert_eq!(counts.high, 0);
assert_eq!(counts.medium, 0);
assert_eq!(counts.low, 2);
}
#[test]
fn test_counts_by_severity_mixed() {
let report = VulnerabilityReportView {
actionable: vec![
make_vuln(SeverityView::Critical),
make_vuln(SeverityView::Medium),
],
informational: vec![make_vuln(SeverityView::Low), make_vuln(SeverityView::None)],
..Default::default()
};
let counts = report.counts_by_severity();
assert_eq!(counts.critical, 1);
assert_eq!(counts.high, 0);
assert_eq!(counts.medium, 1);
assert_eq!(counts.low, 1);
}
}