1use std::fmt;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum Severity {
10 Critical,
11 High,
12 Medium,
13 Low,
14}
15
16impl fmt::Display for Severity {
17 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
18 match self {
19 Severity::Critical => write!(f, "CRITICAL"),
20 Severity::High => write!(f, "HIGH"),
21 Severity::Medium => write!(f, "MEDIUM"),
22 Severity::Low => write!(f, "LOW"),
23 }
24 }
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum AttackCategory {
30 Injection,
31 Protocol,
32 Data,
33 File,
34}
35
36impl fmt::Display for AttackCategory {
38 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39 match self {
40 AttackCategory::Injection => write!(f, "injection"),
41 AttackCategory::Protocol => write!(f, "protocol"),
42 AttackCategory::Data => write!(f, "data"),
43 AttackCategory::File => write!(f, "file"),
44 }
45 }
46}
47
48#[derive(Debug, Clone, PartialEq, Eq)]
56pub struct DetectionResult {
57 pub attack_type: String,
61 pub category: AttackCategory,
63 pub severity: Severity,
71 pub matched_pattern: String,
74 pub offset: usize,
76 pub message: String,
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83
84 fn sample() -> DetectionResult {
85 DetectionResult {
86 attack_type: "xss".into(),
87 category: AttackCategory::Injection,
88 severity: Severity::Critical,
89 matched_pattern: "<script>".into(),
90 offset: 0,
91 message: "XSS detected".into(),
92 }
93 }
94
95 #[test]
96 fn severity_display_uppercase() {
97 assert_eq!(Severity::Critical.to_string(), "CRITICAL");
98 assert_eq!(Severity::High.to_string(), "HIGH");
99 assert_eq!(Severity::Medium.to_string(), "MEDIUM");
100 assert_eq!(Severity::Low.to_string(), "LOW");
101 }
102
103 #[test]
104 fn severity_equality() {
105 assert_eq!(Severity::Critical, Severity::Critical);
106 assert_ne!(Severity::Critical, Severity::High);
107 assert_ne!(Severity::Medium, Severity::Low);
108 }
109
110 #[test]
111 fn attack_category_display_tags() {
112 assert_eq!(AttackCategory::Injection.to_string(), "injection");
113 assert_eq!(AttackCategory::Protocol.to_string(), "protocol");
114 assert_eq!(AttackCategory::Data.to_string(), "data");
115 assert_eq!(AttackCategory::File.to_string(), "file");
116 }
117
118 #[test]
119 fn attack_category_equality() {
120 assert_eq!(AttackCategory::Injection, AttackCategory::Injection);
121 assert_ne!(AttackCategory::Injection, AttackCategory::Protocol);
122 assert_ne!(AttackCategory::Data, AttackCategory::File);
123 }
124
125 #[test]
126 fn detection_result_clone_and_equality() {
127 let a = sample();
128 assert_eq!(a, a.clone());
129 }
130
131 #[test]
132 fn detection_result_field_difference_changes_equality() {
133 let a = sample();
134 let b = DetectionResult {
135 severity: Severity::High,
136 ..a.clone()
137 };
138 assert_ne!(a, b);
139 }
140
141 #[test]
142 fn detection_result_debug_output() {
143 let dbg = format!("{:?}", sample());
144 assert!(dbg.contains("xss"));
145 assert!(dbg.contains("Injection"));
146 assert!(dbg.contains("CRITICAL") || dbg.contains("Critical"));
147 }
148}