rsigma_convert/
condition.rs1use std::collections::HashMap;
2
3use rsigma_parser::*;
4
5use crate::backend::Backend;
6use crate::error::{ConvertError, Result};
7use crate::state::ConversionState;
8
9pub fn convert_condition_expr(
11 backend: &dyn Backend,
12 expr: &ConditionExpr,
13 detections: &HashMap<String, Detection>,
14 state: &mut ConversionState,
15) -> Result<String> {
16 match expr {
17 ConditionExpr::Identifier(name) => {
18 let det = detections.get(name).ok_or_else(|| {
19 ConvertError::RuleConversion(format!("detection '{name}' not found"))
20 })?;
21 backend.convert_detection(det, state)
22 }
23
24 ConditionExpr::And(exprs) => {
25 let parts: Vec<String> = exprs
26 .iter()
27 .map(|e| convert_condition_expr(backend, e, detections, state))
28 .collect::<Result<Vec<_>>>()?;
29 backend.convert_condition_and(&parts)
30 }
31
32 ConditionExpr::Or(exprs) => {
33 let parts: Vec<String> = exprs
34 .iter()
35 .map(|e| convert_condition_expr(backend, e, detections, state))
36 .collect::<Result<Vec<_>>>()?;
37 backend.convert_condition_or(&parts)
38 }
39
40 ConditionExpr::Not(inner) => {
41 let part = convert_condition_expr(backend, inner, detections, state)?;
42 backend.convert_condition_not(&part)
43 }
44
45 ConditionExpr::Selector {
46 quantifier,
47 pattern,
48 } => {
49 let names: Vec<&String> = match pattern {
50 SelectorPattern::Them => {
51 detections.keys().filter(|n| !n.starts_with('_')).collect()
52 }
53 SelectorPattern::Pattern(pat) => detections
54 .keys()
55 .filter(|n| pattern_matches(pat, n))
56 .collect(),
57 };
58
59 if names.is_empty() {
60 return Err(ConvertError::RuleConversion(
61 "selector matched no detections".into(),
62 ));
63 }
64
65 let parts: Vec<String> = names
66 .iter()
67 .map(|name| {
68 let det = detections.get(*name).unwrap();
69 backend.convert_detection(det, state)
70 })
71 .collect::<Result<Vec<_>>>()?;
72
73 match quantifier {
74 Quantifier::Any | Quantifier::Count(1) => backend.convert_condition_or(&parts),
75 Quantifier::All => backend.convert_condition_and(&parts),
76 Quantifier::Count(n) => Err(ConvertError::RuleConversion(format!(
77 "'{n} of' quantifier not supported in conversion"
78 ))),
79 }
80 }
81 }
82}
83
84fn pattern_matches(pattern: &str, name: &str) -> bool {
86 if pattern == "*" {
87 return true;
88 }
89 if let Some(prefix) = pattern.strip_suffix('*') {
90 return name.starts_with(prefix);
91 }
92 if let Some(suffix) = pattern.strip_prefix('*') {
93 return name.ends_with(suffix);
94 }
95 if let Some((prefix, suffix)) = pattern.split_once('*') {
96 return name.starts_with(prefix) && name.ends_with(suffix);
97 }
98 pattern == name
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104
105 #[test]
106 fn test_pattern_matches_star_suffix() {
107 assert!(pattern_matches("selection_*", "selection_main"));
108 assert!(pattern_matches("selection_*", "selection_"));
109 assert!(!pattern_matches("selection_*", "filter_main"));
110 }
111
112 #[test]
113 fn test_pattern_matches_star_prefix() {
114 assert!(pattern_matches("*_main", "selection_main"));
115 assert!(!pattern_matches("*_main", "selection_alt"));
116 }
117
118 #[test]
119 fn test_pattern_matches_star_middle() {
120 assert!(pattern_matches("sel*main", "selection_main"));
121 assert!(!pattern_matches("sel*main", "filter_main"));
122 }
123
124 #[test]
125 fn test_pattern_matches_exact() {
126 assert!(pattern_matches("selection", "selection"));
127 assert!(!pattern_matches("selection", "filter"));
128 }
129
130 #[test]
131 fn test_pattern_matches_star_only() {
132 assert!(pattern_matches("*", "anything"));
133 }
134}