sigma_rust/
selection.rs

1use crate::error::ParserError;
2use crate::error::SelectionError::{
3    InvalidKeywordSelection, InvalidSelectionType, MixedKeywordAndFieldlist,
4    SelectionContainsNoFields,
5};
6use crate::event::Event;
7use crate::field::Field;
8use serde::Deserialize;
9use serde_yml::Value;
10use serde_yml::Value::{Mapping, Sequence};
11
12/// A field group is a collection of fields that are to be combined with AND
13/// In other words a fields group translates to a YAML dictionary
14#[derive(Debug)]
15pub struct FieldGroup {
16    pub fields: Vec<Field>,
17}
18
19impl FieldGroup {
20    fn evaluate(&self, event: &Event) -> bool {
21        self.fields.iter().all(|field| field.evaluate(event))
22    }
23}
24
25impl TryFrom<serde_yml::Mapping> for FieldGroup {
26    type Error = ParserError;
27    fn try_from(mapping: serde_yml::Mapping) -> Result<Self, Self::Error> {
28        let mut fields = vec![];
29        for (name, values) in mapping.into_iter() {
30            match name {
31                Value::String(name) => fields.push(Field::from_yaml(name, values)?),
32                _ => return Err(Self::Error::InvalidFieldName(format!("{:?}", name))),
33            }
34        }
35        Ok(Self { fields })
36    }
37}
38
39#[derive(Deserialize)]
40struct SelectionProxy {
41    #[serde(flatten)]
42    value: Value,
43}
44
45#[derive(Debug, Deserialize)]
46#[serde(try_from = "SelectionProxy")]
47pub enum Selection {
48    Keyword(Vec<String>),
49    Field(Vec<FieldGroup>),
50}
51
52impl TryFrom<SelectionProxy> for Selection {
53    type Error = ParserError;
54
55    fn try_from(other: SelectionProxy) -> Result<Self, Self::Error> {
56        Self::try_from(other.value)
57    }
58}
59
60impl TryFrom<Value> for Selection {
61    type Error = ParserError;
62    fn try_from(other: Value) -> Result<Self, Self::Error> {
63        match other {
64            Sequence(seq) => {
65                if seq.is_empty() {
66                    return Err(Self::Error::SelectionParsingError(
67                        String::new(),
68                        SelectionContainsNoFields(),
69                    ));
70                }
71                let is_keyword_selection = !seq[0].is_mapping();
72                if is_keyword_selection {
73                    let mut keywords = vec![];
74                    for value in seq.iter() {
75                        match value {
76                            Value::String(s) => keywords.push(s.to_string()),
77                            Value::Number(n) => keywords.push(n.to_string()),
78                            Value::Bool(b) => keywords.push(b.to_string()),
79                            _ => {
80                                return Err(Self::Error::SelectionParsingError(
81                                    String::new(),
82                                    InvalidKeywordSelection(format!("{:?}", value)),
83                                ))
84                            }
85                        }
86                    }
87                    return Ok(Self::Keyword(keywords));
88                }
89                // field list selection
90                let mut field_groups = vec![];
91                for value in seq {
92                    match value {
93                        Mapping(map) => {
94                            field_groups.push(FieldGroup::try_from(map)?);
95                        }
96                        _ => {
97                            return Err(Self::Error::SelectionParsingError(
98                                String::new(),
99                                MixedKeywordAndFieldlist(),
100                            ))
101                        }
102                    }
103                }
104                Ok(Self::Field(field_groups))
105            }
106            Mapping(mapping) => {
107                let field_group = FieldGroup::try_from(mapping)?;
108                Ok(Self::Field(vec![field_group]))
109            }
110            _ => Err(Self::Error::SelectionParsingError(
111                String::new(),
112                InvalidSelectionType(),
113            )),
114        }
115    }
116}
117
118impl Selection {
119    pub(crate) fn evaluate(&self, event: &Event) -> bool {
120        match &self {
121            Self::Keyword(keywords) => event
122                .values()
123                .any(|v| keywords.iter().any(|kw| v.contains_keyword(kw))),
124            Self::Field(field_groups) => field_groups.iter().any(|g| g.evaluate(event)),
125        }
126    }
127}
128
129#[cfg(test)]
130mod tests {
131    use super::*;
132    use crate::basevalue::BaseValue;
133    use crate::event::Event;
134    use crate::field::{FieldValue, MatchModifier};
135    use serde_yml::Value;
136
137    #[test]
138    fn test_keyword_selection() {
139        let selection = Selection::Keyword(vec![
140            "test".to_string(),
141            "l?nux".to_string(),
142            "arch *".to_string(),
143        ]);
144
145        let event = Event::from([("key", "test")]);
146        assert!(selection.evaluate(&event));
147
148        let event = Event::from([("nomatch", "zsh".to_string())]);
149        assert!(!selection.evaluate(&event));
150
151        let event = Event::from([("some", "arch linux".to_string())]);
152        assert!(selection.evaluate(&event));
153
154        let event = Event::from([("some", "linux".to_string())]);
155        assert!(selection.evaluate(&event));
156
157        let event = Event::from([("some", "LINUX".to_string())]);
158        assert!(selection.evaluate(&event));
159
160        let event = Event::from([("some", "linus".to_string())]);
161        assert!(!selection.evaluate(&event));
162    }
163
164    #[test]
165    fn test_fields_selection() {
166        let selection = Selection::Field(vec![FieldGroup {
167            fields: vec![
168                Field::new(
169                    "name1|contains",
170                    vec![FieldValue::from("hello"), FieldValue::from("world")],
171                )
172                .unwrap(),
173                Field::new("name2|cidr", vec![FieldValue::from("10.0.0.0/16")]).unwrap(),
174            ],
175        }]);
176
177        let event = Event::from([("name1", "the world is big"), ("name2", "10.0.43.44")]);
178        assert!(selection.evaluate(&event));
179
180        let event = Event::from([("nomatch", "the world is big"), ("name2", "10.42.43.44")]);
181        assert!(!selection.evaluate(&event));
182    }
183
184    #[test]
185    fn test_new_keyword_selection() {
186        let keywords = vec!["test".to_string(), "linux".to_string(), "arch".to_string()];
187        let value = Value::from(keywords.clone());
188
189        let selection = Selection::try_from(value).unwrap();
190        assert!(matches!(selection, Selection::Keyword(kw) if kw.len() == keywords.len()));
191    }
192
193    #[test]
194    fn test_mixed_keyword_selection() {
195        let yaml = r#"
196            - 0
197            - 6
198            - hello
199    "#;
200
201        let value: Value = serde_yml::from_str(yaml).unwrap();
202        let selection = Selection::try_from(value).unwrap();
203        assert!(
204            matches!(selection, Selection::Keyword(kw) if kw.len() == 3 && kw[0] == "0" && kw[1] == "6" && kw[2] == "hello")
205        );
206    }
207
208    #[test]
209    fn test_invalid_keyword_selection() {
210        let yaml = r#"
211            - 0
212            - 6
213            - hello: world
214    "#;
215
216        let value: Value = serde_yml::from_str(yaml).unwrap();
217        let err = Selection::try_from(value).unwrap_err();
218        assert!(matches!(
219            err,
220            ParserError::SelectionParsingError(_, InvalidKeywordSelection(_)),
221        ));
222    }
223
224    #[test]
225    fn test_new_fields_selection() {
226        let yaml = r#"
227    selection:
228        EventID: 6416
229        Float: 42.21
230        ClassName: 'DiskDrive'
231        RandomID|contains:
232            - ab
233            - cd
234            - ed
235"#;
236        let data: serde_yml::Mapping = serde_yml::from_str(yaml).unwrap();
237        assert_eq!(data.len(), 1);
238
239        let value = data.values().next().unwrap().clone();
240        let selection = Selection::try_from(value).unwrap();
241
242        match selection {
243            Selection::Field(field_group) => {
244                assert_eq!(field_group.len(), 1);
245                let fields = &field_group[0].fields;
246                assert_eq!(fields[0].name, "EventID");
247                assert_eq!(fields[0].values.len(), 1);
248                assert!(matches!(
249                    fields[0].values[0],
250                    FieldValue::Base(BaseValue::Int(6416))
251                ));
252
253                assert_eq!(fields[1].name, "Float");
254                assert_eq!(fields[1].values.len(), 1);
255                assert!(matches!(
256                    fields[1].values[0],
257                    FieldValue::Base(BaseValue::Float(42.21))
258                ));
259
260                assert_eq!(fields[2].name, "ClassName");
261                assert_eq!(fields[2].values.len(), 1);
262                assert!(matches!(
263                    fields[2].values[0],
264                    FieldValue::WildcardPattern(_)
265                ));
266
267                assert_eq!(fields[3].name, "RandomID");
268                assert_eq!(fields[3].values.len(), 3);
269                assert!(matches!(
270                    fields[3].values[0],
271                    FieldValue::WildcardPattern(_)
272                ));
273
274                assert!(matches!(
275                    fields[3].modifier.match_modifier,
276                    Some(MatchModifier::Contains)
277                ));
278            }
279            Selection::Keyword(_) => {
280                panic!("wrong mode")
281            }
282        }
283    }
284}