sigma_rust/
field.rs

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// https://sigmahq.io/docs/basics/modifiers.html
21#[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                    // Should never happen as we do not compile values if fieldref modifier is given
187                    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        // After the loop, there are two options:
206        // 1. match_all = false: no condition fired  => return false
207        // 2. match_all = true: all conditions fired => return true
208        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        // We enforce strict type checking, so 15.0 will fail to compare against the int values
439        let event = Event::from([("test", 14.0)]);
440        assert!(!field.evaluate(&event));
441
442        // If we add a float it will work though
443        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}