Skip to main content

rsigma_eval/correlation/
keys.rs

1use serde::Serialize;
2
3use crate::event::{Event, EventValue};
4
5use super::types::GroupByField;
6
7// =============================================================================
8// Group Key
9// =============================================================================
10
11/// Composite key for group-by partitioning.
12///
13/// Each element corresponds to a `GroupByField` value extracted from an event.
14#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, serde::Deserialize)]
15pub struct GroupKey(pub Vec<Option<String>>);
16
17impl GroupKey {
18    /// Extract a group key from an event given the group-by fields and the
19    /// rule reference identifiers (ID, name, etc.) that produced the detection match.
20    ///
21    /// Returns `None` when any group-by field is absent, null, or cannot be
22    /// represented as a scalar string.
23    pub fn extract(
24        event: &impl Event,
25        group_by: &[GroupByField],
26        rule_refs: &[&str],
27    ) -> Option<Self> {
28        let values: Option<Vec<_>> = group_by
29            .iter()
30            .map(|field| {
31                let field_name = field.resolve(rule_refs);
32                event
33                    .get_field(field_name)
34                    .and_then(|v| value_to_string(&v))
35                    .map(Some)
36            })
37            .collect();
38        values.map(GroupKey)
39    }
40
41    /// Build a group key from explicit field-value pairs (for chaining).
42    ///
43    /// Returns `None` when the child correlation did not provide every field
44    /// required by the parent.
45    pub fn from_pairs(pairs: &[(String, String)], group_by: &[GroupByField]) -> Option<Self> {
46        let values: Option<Vec<_>> = group_by
47            .iter()
48            .map(|field| {
49                let name = field.name();
50                pairs
51                    .iter()
52                    .find(|(k, _)| k == name)
53                    .map(|(_, v)| v.clone())
54                    .map(Some)
55            })
56            .collect();
57        values.map(GroupKey)
58    }
59
60    /// Convert to field-name/value pairs for output.
61    pub fn to_pairs(&self, group_by: &[GroupByField]) -> Vec<(String, String)> {
62        group_by
63            .iter()
64            .zip(self.0.iter())
65            .filter_map(|(field, value)| {
66                value
67                    .as_ref()
68                    .map(|v| (field.name().to_string(), v.clone()))
69            })
70            .collect()
71    }
72}
73
74/// Convert an [`EventValue`] to a string for group-key purposes.
75fn value_to_string(v: &EventValue) -> Option<String> {
76    match v {
77        EventValue::Str(s) => Some(s.to_string()),
78        EventValue::Int(n) => Some(n.to_string()),
79        EventValue::Float(f) => Some(f.to_string()),
80        EventValue::Bool(b) => Some(b.to_string()),
81        _ => None,
82    }
83}