Skip to main content

security_rust/
result.rs

1// Copyright (c) 2026 erik <erik@erik.xyz> — https://erik.xyz
2
3use std::fmt;
4
5#[derive(Debug, Clone, PartialEq, Eq)]
6pub enum Severity {
7    Critical,
8    High,
9    Medium,
10    Low,
11}
12
13impl fmt::Display for Severity {
14    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
15        match self {
16            Severity::Critical => write!(f, "CRITICAL"),
17            Severity::High => write!(f, "HIGH"),
18            Severity::Medium => write!(f, "MEDIUM"),
19            Severity::Low => write!(f, "LOW"),
20        }
21    }
22}
23
24#[derive(Debug, Clone, PartialEq, Eq)]
25pub enum AttackCategory {
26    Injection,
27    Protocol,
28    Data,
29    File,
30}
31
32/// 小写的分类标签,供日志直接打印。不是处置结论,故不跟 [`Severity`] 的大写风格。
33impl fmt::Display for AttackCategory {
34    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35        match self {
36            AttackCategory::Injection => write!(f, "injection"),
37            AttackCategory::Protocol => write!(f, "protocol"),
38            AttackCategory::Data => write!(f, "data"),
39            AttackCategory::File => write!(f, "file"),
40        }
41    }
42}
43
44#[derive(Debug, Clone, PartialEq, Eq)]
45pub struct DetectionResult {
46    pub attack_type: String,
47    pub category: AttackCategory,
48    pub severity: Severity,
49    pub matched_pattern: String,
50    pub offset: usize,
51    pub message: String,
52}
53
54#[cfg(test)]
55mod tests {
56    use super::*;
57
58    fn sample() -> DetectionResult {
59        DetectionResult {
60            attack_type: "xss".into(),
61            category: AttackCategory::Injection,
62            severity: Severity::Critical,
63            matched_pattern: "<script>".into(),
64            offset: 0,
65            message: "XSS detected".into(),
66        }
67    }
68
69    #[test]
70    fn severity_display_uppercase() {
71        assert_eq!(Severity::Critical.to_string(), "CRITICAL");
72        assert_eq!(Severity::High.to_string(), "HIGH");
73        assert_eq!(Severity::Medium.to_string(), "MEDIUM");
74        assert_eq!(Severity::Low.to_string(), "LOW");
75    }
76
77    #[test]
78    fn severity_equality() {
79        assert_eq!(Severity::Critical, Severity::Critical);
80        assert_ne!(Severity::Critical, Severity::High);
81        assert_ne!(Severity::Medium, Severity::Low);
82    }
83
84    #[test]
85    fn attack_category_display_tags() {
86        assert_eq!(AttackCategory::Injection.to_string(), "injection");
87        assert_eq!(AttackCategory::Protocol.to_string(), "protocol");
88        assert_eq!(AttackCategory::Data.to_string(), "data");
89        assert_eq!(AttackCategory::File.to_string(), "file");
90    }
91
92    #[test]
93    fn attack_category_equality() {
94        assert_eq!(AttackCategory::Injection, AttackCategory::Injection);
95        assert_ne!(AttackCategory::Injection, AttackCategory::Protocol);
96        assert_ne!(AttackCategory::Data, AttackCategory::File);
97    }
98
99    #[test]
100    fn detection_result_clone_and_equality() {
101        let a = sample();
102        assert_eq!(a, a.clone());
103    }
104
105    #[test]
106    fn detection_result_field_difference_changes_equality() {
107        let a = sample();
108        let b = DetectionResult {
109            severity: Severity::High,
110            ..a.clone()
111        };
112        assert_ne!(a, b);
113    }
114
115    #[test]
116    fn detection_result_debug_output() {
117        let dbg = format!("{:?}", sample());
118        assert!(dbg.contains("xss"));
119        assert!(dbg.contains("Injection"));
120        assert!(dbg.contains("CRITICAL") || dbg.contains("Critical"));
121    }
122}