sentinel_modsec/operators/
comparison.rs1use super::traits::{Operator, OperatorResult};
4use crate::error::{Error, Result};
5
6pub struct ContainsOperator {
8 needle: String,
9}
10
11impl ContainsOperator {
12 pub fn new(needle: &str) -> Self {
13 Self {
14 needle: needle.to_string(),
15 }
16 }
17}
18
19impl Operator for ContainsOperator {
20 fn execute(&self, value: &str) -> OperatorResult {
21 if value.contains(&self.needle) {
22 OperatorResult::matched(self.needle.clone())
23 } else {
24 OperatorResult::no_match()
25 }
26 }
27
28 fn name(&self) -> &'static str {
29 "contains"
30 }
31}
32
33pub struct BeginsWithOperator {
35 prefix: String,
36}
37
38impl BeginsWithOperator {
39 pub fn new(prefix: &str) -> Self {
40 Self {
41 prefix: prefix.to_string(),
42 }
43 }
44}
45
46impl Operator for BeginsWithOperator {
47 fn execute(&self, value: &str) -> OperatorResult {
48 if value.starts_with(&self.prefix) {
49 OperatorResult::matched(self.prefix.clone())
50 } else {
51 OperatorResult::no_match()
52 }
53 }
54
55 fn name(&self) -> &'static str {
56 "beginsWith"
57 }
58}
59
60pub struct EndsWithOperator {
62 suffix: String,
63}
64
65impl EndsWithOperator {
66 pub fn new(suffix: &str) -> Self {
67 Self {
68 suffix: suffix.to_string(),
69 }
70 }
71}
72
73impl Operator for EndsWithOperator {
74 fn execute(&self, value: &str) -> OperatorResult {
75 if value.ends_with(&self.suffix) {
76 OperatorResult::matched(self.suffix.clone())
77 } else {
78 OperatorResult::no_match()
79 }
80 }
81
82 fn name(&self) -> &'static str {
83 "endsWith"
84 }
85}
86
87pub struct StreqOperator {
89 expected: String,
90}
91
92impl StreqOperator {
93 pub fn new(expected: &str) -> Self {
94 Self {
95 expected: expected.to_string(),
96 }
97 }
98}
99
100impl Operator for StreqOperator {
101 fn execute(&self, value: &str) -> OperatorResult {
102 if value == self.expected {
103 OperatorResult::matched(value.to_string())
104 } else {
105 OperatorResult::no_match()
106 }
107 }
108
109 fn name(&self) -> &'static str {
110 "streq"
111 }
112}
113
114pub struct EqOperator {
117 arg: String,
119}
120
121impl EqOperator {
122 pub fn new(value: &str) -> Self {
123 Self {
124 arg: value.to_string(),
125 }
126 }
127
128 fn target_value(&self) -> Option<i64> {
129 if self.arg.contains("%{") {
131 return None;
132 }
133 self.arg.parse().ok()
134 }
135}
136
137impl Operator for EqOperator {
138 fn execute(&self, value: &str) -> OperatorResult {
139 if let Some(target) = self.target_value() {
140 if let Ok(n) = value.parse::<i64>() {
141 if n == target {
142 return OperatorResult::matched(value.to_string());
143 }
144 }
145 }
146 OperatorResult::no_match()
149 }
150
151 fn name(&self) -> &'static str {
152 "eq"
153 }
154}
155
156pub struct GtOperator {
158 arg: String,
159}
160
161impl GtOperator {
162 pub fn new(value: &str) -> Self {
163 Self {
164 arg: value.to_string(),
165 }
166 }
167
168 fn target_value(&self) -> Option<i64> {
169 if self.arg.contains("%{") {
170 return None;
171 }
172 self.arg.parse().ok()
173 }
174}
175
176impl Operator for GtOperator {
177 fn execute(&self, value: &str) -> OperatorResult {
178 if let Some(target) = self.target_value() {
179 if let Ok(n) = value.parse::<i64>() {
180 if n > target {
181 return OperatorResult::matched(value.to_string());
182 }
183 }
184 }
185 OperatorResult::no_match()
186 }
187
188 fn name(&self) -> &'static str {
189 "gt"
190 }
191}
192
193pub struct LtOperator {
195 arg: String,
196}
197
198impl LtOperator {
199 pub fn new(value: &str) -> Self {
200 Self {
201 arg: value.to_string(),
202 }
203 }
204
205 fn target_value(&self) -> Option<i64> {
206 if self.arg.contains("%{") {
207 return None;
208 }
209 self.arg.parse().ok()
210 }
211}
212
213impl Operator for LtOperator {
214 fn execute(&self, value: &str) -> OperatorResult {
215 if let Some(target) = self.target_value() {
216 if let Ok(n) = value.parse::<i64>() {
217 if n < target {
218 return OperatorResult::matched(value.to_string());
219 }
220 }
221 }
222 OperatorResult::no_match()
223 }
224
225 fn name(&self) -> &'static str {
226 "lt"
227 }
228}
229
230pub struct GeOperator {
232 arg: String,
233}
234
235impl GeOperator {
236 pub fn new(value: &str) -> Self {
237 Self {
238 arg: value.to_string(),
239 }
240 }
241
242 fn target_value(&self) -> Option<i64> {
243 if self.arg.contains("%{") {
244 return None;
245 }
246 self.arg.parse().ok()
247 }
248}
249
250impl Operator for GeOperator {
251 fn execute(&self, value: &str) -> OperatorResult {
252 if let Some(target) = self.target_value() {
253 if let Ok(n) = value.parse::<i64>() {
254 if n >= target {
255 return OperatorResult::matched(value.to_string());
256 }
257 }
258 }
259 OperatorResult::no_match()
260 }
261
262 fn name(&self) -> &'static str {
263 "ge"
264 }
265}
266
267pub struct LeOperator {
269 arg: String,
270}
271
272impl LeOperator {
273 pub fn new(value: &str) -> Self {
274 Self {
275 arg: value.to_string(),
276 }
277 }
278
279 fn target_value(&self) -> Option<i64> {
280 if self.arg.contains("%{") {
281 return None;
282 }
283 self.arg.parse().ok()
284 }
285}
286
287impl Operator for LeOperator {
288 fn execute(&self, value: &str) -> OperatorResult {
289 if let Some(target) = self.target_value() {
290 if let Ok(n) = value.parse::<i64>() {
291 if n <= target {
292 return OperatorResult::matched(value.to_string());
293 }
294 }
295 }
296 OperatorResult::no_match()
297 }
298
299 fn name(&self) -> &'static str {
300 "le"
301 }
302}
303
304#[cfg(test)]
305mod tests {
306 use super::*;
307
308 #[test]
309 fn test_contains() {
310 let op = ContainsOperator::new("admin");
311 assert!(op.execute("/admin/users").matched);
312 assert!(!op.execute("/users").matched);
313 }
314
315 #[test]
316 fn test_begins_with() {
317 let op = BeginsWithOperator::new("/admin");
318 assert!(op.execute("/admin/users").matched);
319 assert!(!op.execute("/users/admin").matched);
320 }
321
322 #[test]
323 fn test_ends_with() {
324 let op = EndsWithOperator::new(".php");
325 assert!(op.execute("index.php").matched);
326 assert!(!op.execute("index.html").matched);
327 }
328
329 #[test]
330 fn test_streq() {
331 let op = StreqOperator::new("admin");
332 assert!(op.execute("admin").matched);
333 assert!(!op.execute("Admin").matched);
334 }
335
336 #[test]
337 fn test_numeric_operators() {
338 let eq = EqOperator::new("10");
339 assert!(eq.execute("10").matched);
340 assert!(!eq.execute("11").matched);
341
342 let gt = GtOperator::new("10");
343 assert!(gt.execute("11").matched);
344 assert!(!gt.execute("10").matched);
345
346 let lt = LtOperator::new("10");
347 assert!(lt.execute("9").matched);
348 assert!(!lt.execute("10").matched);
349 }
350}