sigma_rust/
detection.rs

1mod ast;
2mod lexer;
3
4use crate::detection::ast::Ast;
5use crate::error::ParserError;
6use crate::event::Event;
7use crate::selection::Selection;
8use crate::wildcard::match_tokenized;
9use serde::Deserialize;
10use serde_yml::Value;
11use std::collections::HashMap;
12
13#[derive(Deserialize, Debug)]
14struct DetectionProxy {
15    #[serde(flatten)]
16    selections: HashMap<String, Value>,
17    condition: String,
18}
19
20#[derive(Debug, Deserialize)]
21#[serde(try_from = "DetectionProxy")]
22pub struct Detection {
23    #[serde(flatten)]
24    selections: HashMap<String, Selection>,
25    condition: String,
26    #[serde(skip)]
27    ast: Ast,
28}
29
30impl TryFrom<DetectionProxy> for Detection {
31    type Error = ParserError;
32
33    fn try_from(other: DetectionProxy) -> Result<Self, Self::Error> {
34        let mut selections = HashMap::with_capacity(other.selections.len());
35        for (name, selection) in other.selections {
36            match Selection::try_from(selection) {
37                Ok(selection) => {
38                    selections.insert(name, selection);
39                }
40                Err(e) => {
41                    return match e {
42                        ParserError::SelectionParsingError(_, se) => {
43                            Err(ParserError::SelectionParsingError(name, se))
44                        }
45                        _ => Err(e),
46                    }
47                }
48            }
49        }
50        let result = Self::new(selections, other.condition)?;
51        Ok(result)
52    }
53}
54
55impl Detection {
56    pub fn get_selections(&self) -> &HashMap<String, Selection> {
57        &self.selections
58    }
59
60    pub fn get_condition(&self) -> &str {
61        &self.condition
62    }
63
64    pub(crate) fn new<S: AsRef<str>>(
65        selections: HashMap<String, Selection>,
66        condition: S,
67    ) -> Result<Self, ParserError> {
68        let mut result = Self {
69            selections,
70            condition: condition.as_ref().into(),
71            ast: Ast::default(),
72        };
73        result.parse_ast()?;
74        Ok(result)
75    }
76
77    pub(crate) fn parse_ast(&mut self) -> Result<(), ParserError> {
78        let ast = Ast::new(self.condition.as_str())?;
79        let identifiers = ast.selections();
80
81        let missing: Vec<String> = identifiers
82            .into_iter()
83            .filter(|i| !self.selections.contains_key(*i))
84            .map(|i| i.to_string())
85            .collect();
86
87        if !missing.is_empty() {
88            return Err(ParserError::UndefinedIdentifiers(missing));
89        }
90
91        self.ast = ast;
92        Ok(())
93    }
94
95    pub(crate) fn evaluate(&self, event: &Event) -> bool {
96        self.eval(event, &self.ast, &mut HashMap::new())
97    }
98
99    fn evaluate_selection(
100        &self,
101        name: &str,
102        lookup: &mut HashMap<String, bool>,
103        event: &Event,
104    ) -> bool {
105        if let Some(e) = lookup.get(name) {
106            *e
107        } else if let Some(selection) = self.selections.get(name) {
108            let eval = selection.evaluate(event);
109            lookup.insert(name.to_string(), eval);
110            eval
111        } else {
112            // should never happen because we check before evaluate
113            // whether all selections in the condition are covered
114            false
115        }
116    }
117
118    fn eval(&self, event: &Event, ast: &Ast, lookup: &mut HashMap<String, bool>) -> bool {
119        match ast {
120            Ast::Selection(s) => self.evaluate_selection(s, lookup, event),
121            Ast::OneOf(s) => self
122                .selections
123                .keys()
124                .filter(|name| match_tokenized(s, name, false))
125                .any(|name| self.evaluate_selection(name, lookup, event)),
126            Ast::OneOfThem => self
127                .selections
128                .keys()
129                .any(|name| self.evaluate_selection(name, lookup, event)),
130            Ast::AllOf(s) => self
131                .selections
132                .keys()
133                .filter(|name| match_tokenized(s, name, false))
134                .all(|name| self.evaluate_selection(name, lookup, event)),
135            Ast::AllOfThem => self
136                .selections
137                .keys()
138                .all(|name| self.evaluate_selection(name, lookup, event)),
139            Ast::Not(ref operand) => !self.eval(event, operand, lookup),
140            Ast::Or(ref left, ref right) => {
141                self.eval(event, left, lookup) || self.eval(event, right, lookup)
142            }
143            Ast::And(ref left, ref right) => {
144                self.eval(event, left, lookup) && self.eval(event, right, lookup)
145            }
146        }
147    }
148}
149
150#[cfg(test)]
151mod tests {
152    use super::*;
153
154    #[test]
155    fn test_missing_identifier() {
156        let err = Detection::new(HashMap::new(), "selection1 and selection2").unwrap_err();
157        assert!(matches!(err, ParserError::UndefinedIdentifiers(_)));
158    }
159
160    #[test]
161    fn test_evaluate() {
162        let detection_yaml = r#"
163    selection_1:
164        EventID: 6416
165        RandomID|contains:
166            - ab
167            - cd
168            - ed
169    selection_2:
170        EventID: 5555
171    condition: selection_1 or selection_2
172"#;
173
174        let mut event = Event::from([("EventID", 6416)]);
175        event.insert("RandomID", "ab");
176
177        let detection: Detection = serde_yml::from_str(detection_yaml).unwrap();
178        assert_eq!(detection.selections.len(), 2);
179        let result = detection.evaluate(&event);
180        assert!(result);
181
182        let detection =
183            Detection::new(detection.selections, "selection_1 and selection_2").unwrap();
184        let result = detection.evaluate(&event);
185        assert!(!result);
186    }
187
188    #[test]
189    fn test_evaluate_one_all_of_them() {
190        let detection_yaml = r#"
191    selection_1:
192        EventID: 6416
193        RandomID|contains:
194            - ab
195            - cd
196            - ed
197    selection_2:
198        EventID: 5555
199    condition: 1 of them
200"#;
201
202        let mut event = Event::from([("EventID", 6416)]);
203        event.insert("RandomID", "ab");
204
205        let detection: Detection = serde_yml::from_str(detection_yaml).unwrap();
206        assert_eq!(detection.selections.len(), 2);
207        let result = detection.evaluate(&event);
208        assert!(result);
209
210        let detection = Detection::new(detection.selections, "all of them").unwrap();
211        let result = detection.evaluate(&event);
212        assert!(!result);
213    }
214
215    #[test]
216    fn test_evaluate_one_of() {
217        let detection_yaml = r#"
218    selection_1:
219        EventID: 6416
220        RandomID|contains:
221            - ab
222            - cd
223            - ed
224    selection_2:
225        EventID: 5555
226    condition: 1 of selection*
227"#;
228
229        let mut event = Event::from([("EventID", 6416)]);
230        event.insert("RandomID", "ab");
231
232        let detection: Detection = serde_yml::from_str(detection_yaml).unwrap();
233        assert_eq!(detection.selections.len(), 2);
234        let result = detection.evaluate(&event);
235        assert!(result);
236
237        let detection = Detection::new(detection.selections, "1 of nothing*").unwrap();
238        let result = detection.evaluate(&event);
239        assert!(!result);
240    }
241
242    #[test]
243    fn test_evaluate_all_of() {
244        let detection_yaml = r#"
245    selection_1x:
246        EventID: 6416
247        RandomID|contains:
248            - ab
249            - cd
250            - ed
251    selection_2x:
252        EventID: 5555
253    condition: all of sel*tion*x
254"#;
255
256        let mut event = Event::from([("EventID", 6416)]);
257        event.insert("RandomID", "ab");
258
259        let detection: Detection = serde_yml::from_str(detection_yaml).unwrap();
260        assert_eq!(detection.selections.len(), 2);
261        let result = detection.evaluate(&event);
262        assert!(!result);
263
264        let detection = Detection::new(detection.selections, "all of selection_1*").unwrap();
265        let result = detection.evaluate(&event);
266        assert!(result);
267
268        let detection = Detection::new(detection.selections, "all of nothing*").unwrap();
269        let result = detection.evaluate(&event);
270        assert!(result);
271    }
272}