1use crate::FynxResult;
4
5pub trait SecurityModule: Send + Sync {
9 fn id(&self) -> &'static str;
11
12 fn version(&self) -> &'static str;
14
15 fn description(&self) -> &'static str;
17
18 fn init(&mut self) -> FynxResult<()> {
24 Ok(())
25 }
26
27 fn shutdown(&mut self) -> FynxResult<()> {
33 Ok(())
34 }
35}
36
37#[async_trait::async_trait]
39pub trait Scanner: SecurityModule {
40 async fn scan(&self, target: &str) -> FynxResult<ScanResult>;
50}
51
52pub trait Analyzer: SecurityModule {
54 fn analyze(&self, data: &[u8]) -> FynxResult<AnalysisResult>;
64}
65
66#[derive(Debug, Clone)]
68#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
69pub struct ScanResult {
70 pub target: String,
72 pub findings: Vec<Finding>,
74}
75
76#[derive(Debug, Clone)]
78#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
79pub struct Finding {
80 pub severity: Severity,
82 pub description: String,
84 pub metadata: Option<String>,
86}
87
88#[derive(Debug, Clone, Copy, PartialEq, Eq)]
90#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
91pub enum Severity {
92 Info,
94 Low,
96 Medium,
98 High,
100 Critical,
102}
103
104impl PartialOrd for Severity {
105 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
106 Some(self.cmp(other))
107 }
108}
109
110impl Ord for Severity {
111 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
112 let self_val = match self {
113 Severity::Info => 0,
114 Severity::Low => 1,
115 Severity::Medium => 2,
116 Severity::High => 3,
117 Severity::Critical => 4,
118 };
119 let other_val = match other {
120 Severity::Info => 0,
121 Severity::Low => 1,
122 Severity::Medium => 2,
123 Severity::High => 3,
124 Severity::Critical => 4,
125 };
126 self_val.cmp(&other_val)
127 }
128}
129
130#[derive(Debug, Clone)]
132#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
133pub struct AnalysisResult {
134 pub matches: Vec<Match>,
136}
137
138#[derive(Debug, Clone)]
140#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
141pub struct Match {
142 pub offset: usize,
144 pub length: usize,
146 pub rule: String,
148}
149
150#[cfg(test)]
151mod tests {
152 use super::*;
153
154 struct TestModule;
155
156 impl SecurityModule for TestModule {
157 fn id(&self) -> &'static str {
158 "test_module"
159 }
160
161 fn version(&self) -> &'static str {
162 "0.1.0"
163 }
164
165 fn description(&self) -> &'static str {
166 "Test security module"
167 }
168 }
169
170 #[test]
171 fn test_security_module() {
172 let mut module = TestModule;
173 assert_eq!(module.id(), "test_module");
174 assert_eq!(module.version(), "0.1.0");
175 assert!(module.init().is_ok());
176 assert!(module.shutdown().is_ok());
177 }
178
179 #[test]
180 fn test_severity_ordering() {
181 assert!(Severity::Critical > Severity::High);
182 assert!(Severity::High > Severity::Medium);
183 assert!(Severity::Medium > Severity::Low);
184 assert!(Severity::Low > Severity::Info);
185 }
186
187 #[test]
188 fn test_scan_result() {
189 let result = ScanResult {
190 target: "example.com".to_string(),
191 findings: vec![Finding {
192 severity: Severity::High,
193 description: "Test finding".to_string(),
194 metadata: None,
195 }],
196 };
197
198 assert_eq!(result.target, "example.com");
199 assert_eq!(result.findings.len(), 1);
200 }
201}