1mod modifier;
2mod transformation;
3mod value;
4
5pub use modifier::*;
6pub use value::*;
7
8use crate::basevalue::BaseValue;
9use crate::error::ParserError;
10use crate::error::ParserError::{IPParsing, InvalidYAML};
11use crate::event::{Event, EventValue};
12use crate::field::transformation::{encode_base64, encode_base64_offset, windash_variations};
13use crate::field::ValueTransformer::{Base64, Base64offset, Windash};
14use crate::wildcard::{tokenize, WildcardToken};
15use cidr::IpCidr;
16use regex::Regex;
17use serde_yml::Value;
18use std::str::FromStr;
19
20#[derive(Debug)]
22pub struct Field {
23 pub name: String,
24 pub values: Vec<FieldValue>,
25 pub(crate) modifier: Modifier,
26}
27
28impl FromStr for Field {
29 type Err = ParserError;
30
31 fn from_str(s: &str) -> Result<Self, Self::Err> {
32 let result = Self {
33 name: s.split("|").next().unwrap_or("").to_string(),
34 values: vec![],
35 modifier: Modifier::from_str(s)?,
36 };
37
38 Ok(result)
39 }
40}
41
42impl Field {
43 pub(crate) fn new<S: AsRef<str>>(
44 name_with_modifiers: S,
45 values: Vec<FieldValue>,
46 ) -> Result<Field, ParserError> {
47 match Self::from_str(name_with_modifiers.as_ref()) {
48 Ok(mut field) => {
49 field.values = values;
50 match field.bootstrap() {
51 Ok(_) => Ok(field),
52 Err(err) => Err(err),
53 }
54 }
55 Err(err) => Err(err),
56 }
57 }
58
59 pub(crate) fn from_yaml<S: AsRef<str>>(name: S, value: Value) -> Result<Field, ParserError> {
60 let field_values = match value {
61 Value::Bool(_) | Value::Number(_) | Value::String(_) | Value::Null => {
62 vec![FieldValue::try_from(value)?]
63 }
64 Value::Sequence(seq) => {
65 let mut result = Vec::with_capacity(seq.len());
66 for item in seq {
67 result.push(FieldValue::try_from(item)?);
68 }
69 result
70 }
71 _ => return Err(InvalidYAML(format!("{:?}", value))),
72 };
73 Self::new(name, field_values)
74 }
75
76 fn bootstrap(&mut self) -> Result<(), ParserError> {
77 if self.values.is_empty() {
78 return Err(ParserError::EmptyValues(self.name.to_string()));
79 }
80
81 if self.modifier.exists.is_some() {
82 if self.values.len() != 1 {
83 return Err(ParserError::InvalidValueForExists());
84 }
85 if let FieldValue::Base(BaseValue::Boolean(b)) = self.values[0] {
86 self.modifier.exists = Some(b);
87 } else {
88 return Err(ParserError::InvalidValueForExists());
89 }
90 }
91
92 if self.modifier.value_transformer.is_some() {
93 let mut transformed_values: Vec<FieldValue> = Vec::with_capacity(self.values.len());
94
95 for val in &self.values {
96 let s = val.as_string()?;
97 match self.modifier.value_transformer.as_ref().unwrap() {
98 Base64(utf16) => {
99 transformed_values.push(FieldValue::from(encode_base64(s.as_str(), utf16)))
100 }
101 Base64offset(utf16) => transformed_values.extend(
102 encode_base64_offset(s.as_str(), utf16)
103 .into_iter()
104 .map(FieldValue::from),
105 ),
106 Windash => transformed_values.extend(
107 windash_variations(s.as_str())
108 .into_iter()
109 .map(FieldValue::from),
110 ),
111 }
112 }
113
114 self.values = transformed_values;
115 }
116
117 let mut order_modifier_provided = false;
118 for v in self.values.iter_mut() {
119 match self.modifier.match_modifier {
120 Some(
121 MatchModifier::StartsWith | MatchModifier::EndsWith | MatchModifier::Contains,
122 ) => {
123 if !matches!(v, FieldValue::Base(BaseValue::String(_))) {
124 return Err(ParserError::InvalidValueForStringModifier(
125 self.name.to_string(),
126 ));
127 }
128 }
129 Some(MatchModifier::Cidr) => match IpCidr::from_str(v.as_string()?.as_str()) {
130 Ok(ip) => *v = FieldValue::Cidr(ip),
131 Err(err) => return Err(IPParsing(v.as_string()?, err.to_string())),
132 },
133 Some(MatchModifier::Re) => match Regex::new(v.as_string()?.as_str()) {
134 Ok(re) => *v = FieldValue::Regex(re),
135 Err(err) => return Err(ParserError::RegexParsing(err)),
136 },
137 Some(
138 MatchModifier::Lt | MatchModifier::Lte | MatchModifier::Gt | MatchModifier::Gte,
139 ) => order_modifier_provided = true,
140 None => {}
141 }
142 }
143
144 if !self.modifier.fieldref && !order_modifier_provided {
145 for v in self.values.iter_mut() {
146 if let FieldValue::Base(BaseValue::String(s)) = v {
147 let mut tokens = tokenize(s, !self.modifier.cased);
148 match self.modifier.match_modifier {
149 Some(MatchModifier::StartsWith) => {
150 tokens.push(WildcardToken::Star);
151 }
152 Some(MatchModifier::EndsWith) => {
153 tokens.insert(0, WildcardToken::Star);
154 }
155 Some(MatchModifier::Contains) => {
156 tokens.insert(0, WildcardToken::Star);
157 tokens.push(WildcardToken::Star);
158 }
159 _ => {}
160 }
161
162 *v = FieldValue::WildcardPattern(tokens);
163 }
164 }
165 }
166
167 Ok(())
168 }
169
170 pub(crate) fn evaluate(&self, event: &Event) -> bool {
171 let Some(event_value) = event.get(&self.name) else {
172 return matches!(self.modifier.exists, Some(false));
173 };
174
175 if matches!(self.modifier.exists, Some(true)) {
176 return true;
177 };
178
179 for val in self.values.iter() {
180 let cmp = if self.modifier.fieldref {
181 let event_fieldref_value = if let FieldValue::Base(BaseValue::String(s)) = val {
182 event.get(s)
183 } else if let FieldValue::Base(b) = val {
184 event.get(b.value_to_string().as_str())
185 } else {
186 continue;
188 };
189
190 match event_fieldref_value {
191 Some(EventValue::Value(v)) => &FieldValue::Base(v.clone()),
192 _ => return false,
193 }
194 } else {
195 val
196 };
197
198 let fired = event_value.matches(cmp, &self.modifier);
199 if fired && !self.modifier.match_all {
200 return true;
201 } else if !fired && self.modifier.match_all {
202 return false;
203 }
204 }
205 self.modifier.match_all || self.values.is_empty()
209 }
210}
211
212#[cfg(test)]
213mod tests {
214 use super::*;
215
216 #[test]
217 fn test_parse_name_only() {
218 let field = Field::from_str("a").unwrap();
219 assert_eq!(field.name, "a");
220 assert!(field.modifier.match_modifier.is_none());
221 assert!(field.modifier.value_transformer.is_none());
222 assert!(!field.modifier.match_all);
223 }
224
225 #[test]
226 fn test_parse_contains_modifier() {
227 let field = Field::from_str("hello|contains").unwrap();
228 assert_eq!(field.name, "hello");
229 assert_eq!(
230 field.modifier.match_modifier.unwrap(),
231 MatchModifier::Contains
232 );
233 assert!(field.modifier.value_transformer.is_none());
234 assert!(!field.modifier.match_all);
235 }
236
237 #[test]
238 fn test_parse_value_transformer_modifier() {
239 let field = Field::from_str("hello|windash|contains").unwrap();
240 assert_eq!(field.name, "hello");
241 assert_eq!(field.modifier.match_modifier, Some(MatchModifier::Contains));
242 assert_eq!(field.modifier.value_transformer, Some(Windash));
243 }
244
245 #[test]
246 fn test_parse_base64_modifier() {
247 let field = Field::from_str("hello|base64|endswith").unwrap();
248 assert_eq!(field.name, "hello");
249 assert_eq!(field.modifier.match_modifier, Some(MatchModifier::EndsWith));
250 assert_eq!(field.modifier.value_transformer, Some(Base64(None)));
251 }
252
253 #[test]
254 fn test_parse_utf16_modifier() {
255 let field = Field::from_str("hello|base64offset|utf16le|endswith").unwrap();
256 assert_eq!(field.name, "hello");
257 assert_eq!(field.modifier.match_modifier, Some(MatchModifier::EndsWith));
258 assert_eq!(
259 field.modifier.value_transformer,
260 Some(Base64offset(Some(Utf16Modifier::Utf16le)))
261 );
262 }
263
264 #[test]
265 fn test_parse_error() {
266 let field = Field::new("hello|utf16le", vec![]).unwrap_err();
267 assert!(matches!(field, ParserError::Utf16WithoutBase64));
268 }
269
270 #[test]
271 fn test_evaluate_equals() {
272 let field = Field::new(
273 "test",
274 vec![
275 FieldValue::from("zsh"),
276 FieldValue::from("BASH"),
277 FieldValue::from("pwsh"),
278 ],
279 )
280 .unwrap();
281 let event_no_match = Event::from([("test", "zsh shutdown")]);
282 assert!(!field.evaluate(&event_no_match));
283 let matching_event = Event::from([("test", "bash")]);
284 assert!(field.evaluate(&matching_event));
285 }
286
287 #[test]
288 fn test_evaluate_equals_cased() {
289 let field = Field::new("test|cased", vec![FieldValue::from("bash")]).unwrap();
290 let event_no_match = Event::from([("test", "BASH")]);
291 assert!(!field.evaluate(&event_no_match));
292 let matching_event = Event::from([("test", "bash")]);
293 assert!(field.evaluate(&matching_event));
294 }
295
296 #[test]
297 fn test_evaluate_startswith() {
298 let mut field = Field::new(
299 "test|startswith",
300 vec![
301 FieldValue::from("zsh"),
302 FieldValue::from("bash"),
303 FieldValue::from("pwsh"),
304 ],
305 )
306 .unwrap();
307 let event = Event::from([("test", "zsh shutdown")]);
308 assert!(field.evaluate(&event));
309
310 field.modifier.match_all = true;
311 assert!(!field.evaluate(&event));
312 }
313
314 #[test]
315 fn test_evaluate_startswith_cased() {
316 let field = Field::new("test|startswith|cased", vec![FieldValue::from("zsh")]).unwrap();
317 let event = Event::from([("test", "ZSH shutdown")]);
318 assert!(!field.evaluate(&event));
319
320 let event = Event::from([("test", "zsh shutdown")]);
321 assert!(field.evaluate(&event));
322 }
323
324 #[test]
325 fn test_evaluate_endswith() {
326 let field = Field::new(
327 "test|endswith",
328 vec![FieldValue::from("h"), FieldValue::from("sh")],
329 )
330 .unwrap();
331 let event = Event::from([("test", "zsh")]);
332 assert!(field.evaluate(&event));
333
334 let field = Field::new(
335 "test|endswith|all",
336 vec![FieldValue::from("h"), FieldValue::from("sh")],
337 )
338 .unwrap();
339 assert!(field.evaluate(&event));
340 }
341
342 #[test]
343 fn test_evaluate_endswith_cased() {
344 let field = Field::new("test|endswith|cased", vec![FieldValue::from("down")]).unwrap();
345 let event = Event::from([("test", "ZSH shutdOwn")]);
346 assert!(!field.evaluate(&event));
347
348 let event = Event::from([("test", "zsh shutdown")]);
349 assert!(field.evaluate(&event));
350 }
351
352 #[test]
353 fn test_evaluate_contains() {
354 let field = Field::new(
355 "test|contains",
356 vec![FieldValue::from("zsh"), FieldValue::from("python2")],
357 )
358 .unwrap();
359 let event = Event::from([("test", "zsh python3 -c os.remove('/')")]);
360 assert!(field.evaluate(&event));
361
362 let field = Field::new(
363 "test|contains|all",
364 vec![FieldValue::from("zsh"), FieldValue::from("python2")],
365 )
366 .unwrap();
367 assert!(!field.evaluate(&event));
368 }
369
370 #[test]
371 fn test_evaluate_contains_cased() {
372 let field = Field::new("test|contains|cased", vec![FieldValue::from("shut")]).unwrap();
373 let event = Event::from([("test", "ZSH SHUTDOWN")]);
374 assert!(!field.evaluate(&event));
375
376 let event = Event::from([("test", "zsh shutdown")]);
377 assert!(field.evaluate(&event));
378 }
379
380 #[test]
381 fn test_evaluate_lt() {
382 let mut field =
383 Field::new("test|lt", vec![FieldValue::from(10), FieldValue::from(15)]).unwrap();
384 let event = Event::from([("test", 10)]);
385 assert!(field.evaluate(&event));
386
387 field.modifier.match_all = true;
388 assert!(!field.evaluate(&event));
389 }
390
391 #[test]
392 fn test_evaluate_lt_string() {
393 let field = Field::new("test|lt", vec![FieldValue::from("b")]).unwrap();
394 let event = Event::from([("test", "a")]);
395 assert!(field.evaluate(&event));
396 }
397
398 #[test]
399 fn test_evaluate_gte_null() {
400 let field = Field::new("test|gte", vec![FieldValue::from(None)]).unwrap();
401 let event = Event::from([("test", None)]);
402 assert!(field.evaluate(&event));
403 }
404
405 #[test]
406 fn test_evaluate_lte() {
407 let mut field =
408 Field::new("test|lte", vec![FieldValue::from(15), FieldValue::from(20)]).unwrap();
409 let event = Event::from([("test", 15)]);
410 assert!(field.evaluate(&event));
411
412 field.modifier.match_all = true;
413 assert!(field.evaluate(&event));
414 }
415
416 #[test]
417 fn test_evaluate_gt() {
418 let mut field = Field::new("test|gt", vec![FieldValue::from(10.1)]).unwrap();
419 let event = Event::from([("test", 10.2)]);
420 assert!(field.evaluate(&event));
421
422 field.modifier.match_all = true;
423 assert!(field.evaluate(&event));
424 }
425
426 #[test]
427 fn test_evaluate_gte() {
428 let mut field =
429 Field::new("test|gte", vec![FieldValue::from(15), FieldValue::from(10)]).unwrap();
430 let event = Event::from([("test", 15)]);
431 assert!(field.evaluate(&event));
432
433 field.modifier.match_all = true;
434 assert!(field.evaluate(&event));
435
436 field.modifier.match_all = false;
437
438 let event = Event::from([("test", 14.0)]);
440 assert!(!field.evaluate(&event));
441
442 field.values.push(FieldValue::from(12.34));
444 assert!(field.evaluate(&event));
445
446 field.modifier.match_all = true;
447 assert!(!field.evaluate(&event));
448 }
449
450 #[test]
451 fn test_evaluate_regex() {
452 let mut field = Field::new(
453 "test|re",
454 vec![
455 FieldValue::from(r"hello (.*)d"),
456 FieldValue::from(r"goodbye (.*)"),
457 ],
458 )
459 .unwrap();
460
461 for val in &field.values {
462 assert!(matches!(val, FieldValue::Regex(_)));
463 }
464
465 let event = Event::from([("test", "hello world")]);
466 assert!(field.evaluate(&event));
467
468 field.modifier.match_all = true;
469 assert!(!field.evaluate(&event));
470 }
471
472 #[test]
473 fn test_invalid_regex() {
474 let err = Field::new("test|re", vec![FieldValue::from(r"[")]).unwrap_err();
475 assert!(matches!(err, ParserError::RegexParsing(_)));
476 }
477
478 #[test]
479 fn test_cidr() {
480 let cidrs = ["10.0.0.0/16", "10.0.0.0/24"];
481 let mut field = Field::new(
482 "test|cidr",
483 cidrs.into_iter().map(FieldValue::from).collect(),
484 )
485 .unwrap();
486
487 let event = Event::from([("test", "10.0.1.1")]);
488 assert!(field.evaluate(&event));
489 field.modifier.match_all = true;
490
491 assert!(!field.evaluate(&event));
492
493 let event = Event::from([("test", "10.1.2.3")]);
494 field.modifier.match_all = false;
495 assert!(!field.evaluate(&event));
496 }
497
498 #[test]
499 fn test_cidr_invalid_ip() {
500 let err = Field::new("test|cidr", vec![FieldValue::from("1.2.3.4.5.6/16")]).unwrap_err();
501 assert!(matches!(err, IPParsing(_, _)));
502 }
503
504 #[test]
505 fn test_base64_utf16le() {
506 let patterns = ["Add-MpPreference ", "Set-MpPreference "];
507 let field = Field::new(
508 "test|base64|utf16le|contains",
509 patterns
510 .iter()
511 .map(|x| FieldValue::from(x.to_string()))
512 .collect(),
513 )
514 .unwrap();
515
516 let event = Event::from([(
517 "test",
518 "jkdfgnhjkQQBkAGQALQBNAHAAUAByAGUAZgBlAHIAZQBuAGMAZQAgAioskdfgjk",
519 )]);
520 assert!(field.evaluate(&event));
521
522 let event = Event::from([(
523 "test",
524 "23234345UwBlAHQALQBNAHAAUAByAGUAZgBlAHIAZQBuAGMAZQAgA3535446d",
525 )]);
526 assert!(field.evaluate(&event));
527 }
528
529 #[test]
530 fn test_base64offset_utf16le() {
531 let patterns = [
532 "Add-MpPreference ",
533 "Set-MpPreference ",
534 "add-mppreference ",
535 "set-mppreference ",
536 ];
537 let field = Field::new(
538 "test|base64offset|utf16le|contains",
539 patterns.into_iter().map(FieldValue::from).collect(),
540 )
541 .unwrap();
542
543 let expected = [
544 "QQBkAGQALQBNAHAAUAByAGUAZgBlAHIAZQBuAGMAZQAgA",
545 "EAZABkAC0ATQBwAFAAcgBlAGYAZQByAGUAbgBjAGUAIA",
546 "BAGQAZAAtAE0AcABQAHIAZQBmAGUAcgBlAG4AYwBlACAA",
547 "UwBlAHQALQBNAHAAUAByAGUAZgBlAHIAZQBuAGMAZQAgA",
548 "MAZQB0AC0ATQBwAFAAcgBlAGYAZQByAGUAbgBjAGUAIA",
549 "TAGUAdAAtAE0AcABQAHIAZQBmAGUAcgBlAG4AYwBlACAA",
550 "YQBkAGQALQBtAHAAcAByAGUAZgBlAHIAZQBuAGMAZQAgA",
551 "EAZABkAC0AbQBwAHAAcgBlAGYAZQByAGUAbgBjAGUAIA",
552 "hAGQAZAAtAG0AcABwAHIAZQBmAGUAcgBlAG4AYwBlACAA",
553 "cwBlAHQALQBtAHAAcAByAGUAZgBlAHIAZQBuAGMAZQAgA",
554 "MAZQB0AC0AbQBwAHAAcgBlAGYAZQByAGUAbgBjAGUAIA",
555 "zAGUAdAAtAG0AcABwAHIAZQBmAGUAcgBlAG4AYwBlACAA",
556 ];
557
558 for pattern in expected.into_iter() {
559 let mut scrambled_pattern = pattern.to_string().clone();
560 scrambled_pattern.insert_str(0, "klsenf");
561 scrambled_pattern.insert_str(scrambled_pattern.len(), "scvfv");
562 let event = Event::from([("test", scrambled_pattern.clone())]);
563 assert!(field.evaluate(&event));
564 }
565 }
566
567 #[test]
568 fn test_windash() {
569 let patterns = ["-my-param", "/another-param"];
570 let field = Field::new(
571 "test|windash|contains",
572 patterns.into_iter().map(FieldValue::from).collect(),
573 )
574 .unwrap();
575
576 let event = Event::from([("test", "program.exe /my-param")]);
577 assert!(field.evaluate(&event));
578
579 let event = Event::from([("test", "another.exe -another-param")]);
580 assert!(field.evaluate(&event));
581 }
582
583 #[test]
584 fn test_empty_values() {
585 let values: Vec<FieldValue> = vec![];
586 let err = Field::new("test|contains", values).unwrap_err();
587 assert!(matches!(err, ParserError::EmptyValues(a) if a == "test"));
588 }
589
590 #[test]
591 fn test_invalid_contains() {
592 let values: Vec<FieldValue> = vec![FieldValue::from("ok"), FieldValue::from(5)];
593 let err = Field::new("test|contains", values).unwrap_err();
594 assert!(matches!(err, ParserError::InvalidValueForStringModifier(name) if name == "test"));
595 }
596
597 #[test]
598 fn test_invalid_startswith() {
599 let values: Vec<FieldValue> = vec![FieldValue::from("ok"), FieldValue::from(5)];
600 let err = Field::new("test|startswith", values).unwrap_err();
601 assert!(matches!(err, ParserError::InvalidValueForStringModifier(name) if name == "test"));
602 }
603
604 #[test]
605 fn test_invalid_endswith() {
606 let values: Vec<FieldValue> = vec![FieldValue::from("ok"), FieldValue::from(5)];
607 let err = Field::new("test|endswith", values).unwrap_err();
608 assert!(matches!(err, ParserError::InvalidValueForStringModifier(name) if name == "test"));
609 }
610
611 #[test]
612 fn test_parse_exists_modifier() {
613 let values: Vec<FieldValue> = vec![FieldValue::from(true)];
614 let field = Field::new("test|exists", values).unwrap();
615 assert!(field.modifier.exists.unwrap());
616
617 let values: Vec<FieldValue> = vec![FieldValue::from(false)];
618 let field = Field::new("test|exists", values).unwrap();
619 assert!(!field.modifier.exists.unwrap());
620 }
621
622 #[test]
623 fn test_parse_exists_modifier_invalid_values() {
624 let values_vec: Vec<Vec<FieldValue>> = vec![
625 vec![FieldValue::from("not a boolean")],
626 vec![FieldValue::from("something"), FieldValue::from(5.0)],
627 vec![FieldValue::from(true), FieldValue::from(true)],
628 ];
629
630 for values in values_vec {
631 let err = Field::new("test|exists", values).unwrap_err();
632 assert!(matches!(err, ParserError::InvalidValueForExists()));
633 }
634 }
635
636 #[test]
637 fn test_match_fieldref_startswith_cased() {
638 let event = Event::from([("value", "abcdefg"), ("reference", "aBcd")]);
639 let field = Field::new(
640 "value|fieldref|startswith",
641 vec![FieldValue::from("reference")],
642 )
643 .unwrap();
644
645 assert!(field.evaluate(&event));
646
647 let field = Field::new(
648 "value|cased|fieldref|startswith",
649 vec![FieldValue::from("reference")],
650 )
651 .unwrap();
652
653 assert!(!field.evaluate(&event));
654 let event = Event::from([("value", "abcdefg"), ("reference", "abcd")]);
655 assert!(field.evaluate(&event));
656 }
657
658 #[test]
659 fn test_match_fieldref_endswith_cased() {
660 let event = Event::from([("value", "abcdefg"), ("reference", "eFg")]);
661 let field = Field::new(
662 "value|fieldref|endswith",
663 vec![FieldValue::from("reference")],
664 )
665 .unwrap();
666
667 assert!(field.evaluate(&event));
668
669 let field = Field::new(
670 "value|cased|fieldref|endswith",
671 vec![FieldValue::from("reference")],
672 )
673 .unwrap();
674
675 assert!(!field.evaluate(&event));
676 let event = Event::from([("value", "abcdefg"), ("reference", "efg")]);
677 assert!(field.evaluate(&event));
678 }
679
680 #[test]
681 fn test_match_fieldref_contains_cased() {
682 let event = Event::from([("value", "abcdefg"), ("reference", "cDe")]);
683 let field = Field::new(
684 "value|fieldref|contains",
685 vec![FieldValue::from("reference")],
686 )
687 .unwrap();
688
689 assert!(field.evaluate(&event));
690
691 let field = Field::new(
692 "value|cased|fieldref|contains",
693 vec![FieldValue::from("reference")],
694 )
695 .unwrap();
696
697 assert!(!field.evaluate(&event));
698 let event = Event::from([("value", "abcdefg"), ("reference", "cde")]);
699 assert!(field.evaluate(&event));
700 }
701}