Skip to main content

oxideshield_core/
severity.rs

1//! Severity levels for security findings
2
3use serde::{Deserialize, Serialize};
4use std::fmt;
5
6/// Severity level for security findings
7#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
8#[serde(rename_all = "lowercase")]
9#[derive(Default)]
10pub enum Severity {
11    /// Informational finding
12    Info,
13    /// Low severity
14    Low,
15    /// Medium severity
16    #[default]
17    Medium,
18    /// High severity
19    High,
20    /// Critical severity
21    Critical,
22}
23
24impl Severity {
25    /// Get all severity levels in order
26    pub fn all() -> &'static [Severity] {
27        &[
28            Severity::Info,
29            Severity::Low,
30            Severity::Medium,
31            Severity::High,
32            Severity::Critical,
33        ]
34    }
35
36    /// Get the numeric score (0-4)
37    pub fn score(&self) -> u8 {
38        match self {
39            Severity::Info => 0,
40            Severity::Low => 1,
41            Severity::Medium => 2,
42            Severity::High => 3,
43            Severity::Critical => 4,
44        }
45    }
46
47    /// Create from numeric score
48    pub fn from_score(score: u8) -> Self {
49        match score {
50            0 => Severity::Info,
51            1 => Severity::Low,
52            2 => Severity::Medium,
53            3 => Severity::High,
54            _ => Severity::Critical,
55        }
56    }
57
58    /// Get the CVSS-like string representation
59    pub fn as_str(&self) -> &'static str {
60        match self {
61            Severity::Info => "INFO",
62            Severity::Low => "LOW",
63            Severity::Medium => "MEDIUM",
64            Severity::High => "HIGH",
65            Severity::Critical => "CRITICAL",
66        }
67    }
68
69    /// Check if this severity meets or exceeds a threshold
70    pub fn meets_threshold(&self, threshold: Severity) -> bool {
71        *self >= threshold
72    }
73}
74
75impl fmt::Display for Severity {
76    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77        write!(f, "{}", self.as_str())
78    }
79}
80
81impl std::str::FromStr for Severity {
82    type Err = String;
83
84    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
85        match s.to_lowercase().as_str() {
86            "info" | "informational" => Ok(Severity::Info),
87            "low" => Ok(Severity::Low),
88            "medium" | "med" => Ok(Severity::Medium),
89            "high" => Ok(Severity::High),
90            "critical" | "crit" => Ok(Severity::Critical),
91            _ => Err(format!("Unknown severity: {}", s)),
92        }
93    }
94}
95
96#[cfg(test)]
97mod tests {
98    use super::*;
99
100    #[test]
101    fn test_severity_ordering() {
102        assert!(Severity::Info < Severity::Low);
103        assert!(Severity::Low < Severity::Medium);
104        assert!(Severity::Medium < Severity::High);
105        assert!(Severity::High < Severity::Critical);
106    }
107
108    #[test]
109    fn test_severity_from_str() {
110        assert_eq!("high".parse::<Severity>().unwrap(), Severity::High);
111        assert_eq!("CRITICAL".parse::<Severity>().unwrap(), Severity::Critical);
112    }
113
114    #[test]
115    fn test_severity_threshold() {
116        assert!(Severity::High.meets_threshold(Severity::Medium));
117        assert!(!Severity::Low.meets_threshold(Severity::High));
118    }
119}