uv-sbom 2.0.1

SBOM generation tool for uv projects - Generate CycloneDX SBOMs from uv.lock files
Documentation
//! Vulnerability view structs for read model
//!
//! These structs provide a query-optimized view of vulnerability data
//! with pre-computed categorization and summary information.

/// View representation of a vulnerability report
///
/// Provides pre-categorized vulnerabilities with summary statistics
/// for efficient dashboard and reporting queries.
#[derive(Debug, Clone, Default)]
pub struct VulnerabilityReportView {
    /// Vulnerabilities that require action (CRITICAL, HIGH, MEDIUM)
    pub actionable: Vec<VulnerabilityView>,
    /// Vulnerabilities for information only (LOW, NONE)
    pub informational: Vec<VulnerabilityView>,
    /// Whether the severity threshold was exceeded
    #[allow(dead_code)]
    pub threshold_exceeded: bool,
    /// Summary statistics
    pub summary: VulnerabilitySummary,
}

/// View representation of a single vulnerability
#[derive(Debug, Clone)]
pub struct VulnerabilityView {
    /// BOM reference identifier
    pub bom_ref: String,
    /// Vulnerability ID (e.g., CVE-2024-1234)
    pub id: String,
    /// BOM reference of the affected component
    pub affected_component: String,
    /// Name of the affected component
    pub affected_component_name: String,
    /// Version of the affected component
    pub affected_version: String,
    /// CVSS score (0.0 - 10.0)
    pub cvss_score: Option<f32>,
    /// CVSS vector string
    pub cvss_vector: Option<String>,
    /// Severity level
    pub severity: SeverityView,
    /// Version that fixes the vulnerability
    pub fixed_version: Option<String>,
    /// Description of the vulnerability
    pub description: Option<String>,
    /// URL to vulnerability source
    pub source_url: Option<String>,
}

/// Severity level for display purposes
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum SeverityView {
    /// Critical severity (CVSS 9.0-10.0)
    Critical,
    /// High severity (CVSS 7.0-8.9)
    High,
    /// Medium severity (CVSS 4.0-6.9)
    Medium,
    /// Low severity (CVSS 0.1-3.9)
    Low,
    /// No severity / Unknown
    #[default]
    None,
}

impl SeverityView {
    /// Returns the display name of the severity
    pub fn as_str(&self) -> &'static str {
        match self {
            SeverityView::Critical => "CRITICAL",
            SeverityView::High => "HIGH",
            SeverityView::Medium => "MEDIUM",
            SeverityView::Low => "LOW",
            SeverityView::None => "NONE",
        }
    }

    /// Returns true if this severity requires action
    #[allow(dead_code)]
    pub fn is_actionable(&self) -> bool {
        matches!(
            self,
            SeverityView::Critical | SeverityView::High | SeverityView::Medium
        )
    }
}

/// Summary statistics for vulnerabilities
#[derive(Debug, Clone, Default)]
pub struct VulnerabilitySummary {
    /// Total number of vulnerabilities
    pub total_count: usize,
    /// Number of actionable vulnerabilities
    #[allow(dead_code)]
    pub actionable_count: usize,
    /// Number of informational vulnerabilities
    #[allow(dead_code)]
    pub informational_count: usize,
    /// Number of unique affected packages
    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_is_actionable() {
        assert!(SeverityView::Critical.is_actionable());
        assert!(SeverityView::High.is_actionable());
        assert!(SeverityView::Medium.is_actionable());
        assert!(!SeverityView::Low.is_actionable());
        assert!(!SeverityView::None.is_actionable());
    }

    #[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);
    }
}