mod ast;
mod lexer;
use crate::detection::ast::Ast;
use crate::error::ParserError;
use crate::event::Event;
use crate::selection::Selection;
use glob_match::glob_match;
use serde::Deserialize;
use serde_yml::Value;
use std::collections::HashMap;
#[derive(Deserialize, Debug)]
struct DetectionProxy {
#[serde(flatten)]
selections: HashMap<String, Value>,
condition: String,
}
#[derive(Debug, Deserialize)]
#[serde(try_from = "DetectionProxy")]
pub struct Detection {
#[serde(flatten)]
selections: HashMap<String, Selection>,
condition: String,
#[serde(skip)]
ast: Ast,
}
impl TryFrom<DetectionProxy> for Detection {
type Error = ParserError;
fn try_from(other: DetectionProxy) -> Result<Self, Self::Error> {
let mut selections = HashMap::with_capacity(other.selections.len());
for (name, selection) in other.selections {
match Selection::try_from(selection) {
Ok(selection) => {
selections.insert(name, selection);
}
Err(e) => {
return match e {
ParserError::SelectionParsingError(_, se) => {
Err(ParserError::SelectionParsingError(name, se))
}
_ => Err(e),
}
}
}
}
let result = Self::new(selections, other.condition)?;
Ok(result)
}
}
impl Detection {
pub fn get_selections(&self) -> &HashMap<String, Selection> {
&self.selections
}
pub fn get_condition(&self) -> &str {
&self.condition
}
pub(crate) fn new<S: AsRef<str>>(
selections: HashMap<String, Selection>,
condition: S,
) -> Result<Self, ParserError> {
let mut result = Self {
selections,
condition: condition.as_ref().into(),
ast: Ast::default(),
};
result.parse_ast()?;
Ok(result)
}
pub(crate) fn parse_ast(&mut self) -> Result<(), ParserError> {
let ast = Ast::new(self.condition.as_str())?;
let identifiers = ast.selections();
let missing: Vec<String> = identifiers
.into_iter()
.filter(|i| !self.selections.contains_key(*i))
.map(|i| i.to_string())
.collect();
if !missing.is_empty() {
return Err(ParserError::UndefinedIdentifiers(missing));
}
self.ast = ast;
Ok(())
}
pub(crate) fn evaluate(&self, event: &Event) -> bool {
self.eval(event, &self.ast, &mut HashMap::new())
}
fn evaluate_selection(
&self,
name: &str,
lookup: &mut HashMap<String, bool>,
event: &Event,
) -> bool {
if let Some(e) = lookup.get(name) {
*e
} else if let Some(selection) = self.selections.get(name) {
let eval = selection.evaluate(event);
lookup.insert(name.to_string(), eval);
eval
} else {
false
}
}
fn eval(&self, event: &Event, ast: &Ast, lookup: &mut HashMap<String, bool>) -> bool {
match ast {
Ast::Selection(s) => self.evaluate_selection(s, lookup, event),
Ast::OneOf(s) => self
.selections
.keys()
.filter(|name| glob_match(s, name))
.any(|name| self.evaluate_selection(name, lookup, event)),
Ast::OneOfThem => self
.selections
.keys()
.any(|name| self.evaluate_selection(name, lookup, event)),
Ast::AllOf(s) => self
.selections
.keys()
.filter(|name| glob_match(s, name))
.all(|name| self.evaluate_selection(name, lookup, event)),
Ast::AllOfThem => self
.selections
.keys()
.all(|name| self.evaluate_selection(name, lookup, event)),
Ast::Not(ref operand) => !self.eval(event, operand, lookup),
Ast::Or(ref left, ref right) => {
self.eval(event, left, lookup) || self.eval(event, right, lookup)
}
Ast::And(ref left, ref right) => {
self.eval(event, left, lookup) && self.eval(event, right, lookup)
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_missing_identifier() {
let err = Detection::new(HashMap::new(), "selection1 and selection2").unwrap_err();
assert!(matches!(err, ParserError::UndefinedIdentifiers(_)));
}
#[test]
fn test_evaluate() {
let detection_yaml = r#"
selection_1:
EventID: 6416
RandomID|contains:
- ab
- cd
- ed
selection_2:
EventID: 5555
condition: selection_1 or selection_2
"#;
let mut event = Event::from([("EventID", 6416)]);
event.insert("RandomID", "ab");
let detection: Detection = serde_yml::from_str(detection_yaml).unwrap();
assert_eq!(detection.selections.len(), 2);
let result = detection.evaluate(&event);
assert!(result);
let detection =
Detection::new(detection.selections, "selection_1 and selection_2").unwrap();
let result = detection.evaluate(&event);
assert!(!result);
}
#[test]
fn test_evaluate_one_all_of_them() {
let detection_yaml = r#"
selection_1:
EventID: 6416
RandomID|contains:
- ab
- cd
- ed
selection_2:
EventID: 5555
condition: 1 of them
"#;
let mut event = Event::from([("EventID", 6416)]);
event.insert("RandomID", "ab");
let detection: Detection = serde_yml::from_str(detection_yaml).unwrap();
assert_eq!(detection.selections.len(), 2);
let result = detection.evaluate(&event);
assert!(result);
let detection = Detection::new(detection.selections, "all of them").unwrap();
let result = detection.evaluate(&event);
assert!(!result);
}
#[test]
fn test_evaluate_one_of() {
let detection_yaml = r#"
selection_1:
EventID: 6416
RandomID|contains:
- ab
- cd
- ed
selection_2:
EventID: 5555
condition: 1 of selection*
"#;
let mut event = Event::from([("EventID", 6416)]);
event.insert("RandomID", "ab");
let detection: Detection = serde_yml::from_str(detection_yaml).unwrap();
assert_eq!(detection.selections.len(), 2);
let result = detection.evaluate(&event);
assert!(result);
let detection = Detection::new(detection.selections, "1 of nothing*").unwrap();
let result = detection.evaluate(&event);
assert!(!result);
}
#[test]
fn test_evaluate_all_of() {
let detection_yaml = r#"
selection_1:
EventID: 6416
RandomID|contains:
- ab
- cd
- ed
selection_2:
EventID: 5555
condition: all of selection*
"#;
let mut event = Event::from([("EventID", 6416)]);
event.insert("RandomID", "ab");
let detection: Detection = serde_yml::from_str(detection_yaml).unwrap();
assert_eq!(detection.selections.len(), 2);
let result = detection.evaluate(&event);
assert!(!result);
let detection = Detection::new(detection.selections, "all of selection_1*").unwrap();
let result = detection.evaluate(&event);
assert!(result);
let detection = Detection::new(detection.selections, "all of nothing*").unwrap();
let result = detection.evaluate(&event);
assert!(result);
}
}