rsigma_eval/event/
json.rs1use std::borrow::Cow;
2
3use serde_json::Value;
4
5use super::{Event, EventValue};
6
7const MAX_NESTING_DEPTH: usize = 64;
9
10#[derive(Debug)]
18pub struct JsonEvent<'a> {
19 inner: Cow<'a, Value>,
20}
21
22impl<'a> JsonEvent<'a> {
23 pub fn borrow(v: &'a Value) -> Self {
25 Self {
26 inner: Cow::Borrowed(v),
27 }
28 }
29
30 pub fn owned(v: Value) -> Self {
32 Self {
33 inner: Cow::Owned(v),
34 }
35 }
36}
37
38impl<'a> From<&'a Value> for JsonEvent<'a> {
39 fn from(v: &'a Value) -> Self {
40 Self::borrow(v)
41 }
42}
43
44impl From<Value> for JsonEvent<'static> {
45 fn from(v: Value) -> Self {
46 Self::owned(v)
47 }
48}
49
50impl<'a> Event for JsonEvent<'a> {
51 fn get_field(&self, path: &str) -> Option<EventValue<'_>> {
57 let value: &Value = &self.inner;
58
59 if let Some(obj) = value.as_object()
60 && let Some(v) = obj.get(path)
61 {
62 return Some(EventValue::from(v));
63 }
64
65 if path.contains('.') {
66 let parts: Vec<&str> = path.split('.').collect();
67 return traverse_json(value, &parts).map(EventValue::from);
68 }
69
70 None
71 }
72
73 fn any_string_value(&self, pred: &dyn Fn(&str) -> bool) -> bool {
78 any_string_value_json(&self.inner, pred, MAX_NESTING_DEPTH)
79 }
80
81 fn all_string_values(&self) -> Vec<Cow<'_, str>> {
87 let mut values = Vec::new();
88 collect_string_values_json(&self.inner, &mut values, MAX_NESTING_DEPTH);
89 values
90 }
91
92 fn to_json(&self) -> Value {
93 self.inner.as_ref().clone()
94 }
95}
96
97fn traverse_json<'a>(current: &'a Value, parts: &[&str]) -> Option<&'a Value> {
102 if parts.is_empty() {
103 return Some(current);
104 }
105
106 let (head, rest) = (parts[0], &parts[1..]);
107
108 match current {
109 Value::Object(map) => {
110 let next = map.get(head)?;
111 traverse_json(next, rest)
112 }
113 Value::Array(arr) => {
114 for item in arr {
115 if let Some(v) = traverse_json(item, parts) {
116 return Some(v);
117 }
118 }
119 None
120 }
121 _ => None,
122 }
123}
124
125fn any_string_value_json(v: &Value, pred: &dyn Fn(&str) -> bool, depth: usize) -> bool {
126 if depth == 0 {
127 return false;
128 }
129 match v {
130 Value::String(s) => pred(s.as_str()),
131 Value::Object(map) => map
132 .values()
133 .any(|val| any_string_value_json(val, pred, depth - 1)),
134 Value::Array(arr) => arr
135 .iter()
136 .any(|val| any_string_value_json(val, pred, depth - 1)),
137 _ => false,
138 }
139}
140
141fn collect_string_values_json<'a>(v: &'a Value, out: &mut Vec<Cow<'a, str>>, depth: usize) {
142 if depth == 0 {
143 return;
144 }
145 match v {
146 Value::String(s) => out.push(Cow::Borrowed(s.as_str())),
147 Value::Object(map) => {
148 for val in map.values() {
149 collect_string_values_json(val, out, depth - 1);
150 }
151 }
152 Value::Array(arr) => {
153 for val in arr {
154 collect_string_values_json(val, out, depth - 1);
155 }
156 }
157 _ => {}
158 }
159}
160
161#[cfg(test)]
162mod tests {
163 use super::*;
164 use serde_json::json;
165
166 #[test]
167 fn json_flat_field() {
168 let v = json!({"CommandLine": "whoami", "User": "admin"});
169 let event = JsonEvent::borrow(&v);
170 assert_eq!(
171 event.get_field("CommandLine"),
172 Some(EventValue::Str(Cow::Borrowed("whoami")))
173 );
174 }
175
176 #[test]
177 fn json_nested_field() {
178 let v = json!({"actor": {"id": "user123", "type": "User"}});
179 let event = JsonEvent::borrow(&v);
180 assert_eq!(
181 event.get_field("actor.id"),
182 Some(EventValue::Str(Cow::Borrowed("user123")))
183 );
184 }
185
186 #[test]
187 fn json_flat_key_precedence() {
188 let v = json!({"actor.id": "flat_value", "actor": {"id": "nested_value"}});
189 let event = JsonEvent::borrow(&v);
190 assert_eq!(
191 event.get_field("actor.id"),
192 Some(EventValue::Str(Cow::Borrowed("flat_value")))
193 );
194 }
195
196 #[test]
197 fn json_missing_field() {
198 let v = json!({"foo": "bar"});
199 let event = JsonEvent::borrow(&v);
200 assert_eq!(event.get_field("missing"), None);
201 }
202
203 #[test]
204 fn json_null_field() {
205 let v = json!({"foo": null});
206 let event = JsonEvent::borrow(&v);
207 assert_eq!(event.get_field("foo"), Some(EventValue::Null));
208 }
209
210 #[test]
211 fn json_array_traversal() {
212 let v = json!({"a": {"b": [{"c": "found"}, {"c": "other"}]}});
213 let event = JsonEvent::borrow(&v);
214 assert_eq!(
215 event.get_field("a.b.c"),
216 Some(EventValue::Str(Cow::Borrowed("found")))
217 );
218 }
219
220 #[test]
221 fn json_array_traversal_no_match() {
222 let v = json!({"a": {"b": [{"x": 1}, {"y": 2}]}});
223 let event = JsonEvent::borrow(&v);
224 assert_eq!(event.get_field("a.b.c"), None);
225 }
226
227 #[test]
228 fn json_array_traversal_deep() {
229 let v = json!({
230 "events": [
231 {"actors": [{"name": "alice"}, {"name": "bob"}]},
232 {"actors": [{"name": "charlie"}]}
233 ]
234 });
235 let event = JsonEvent::borrow(&v);
236 assert_eq!(
237 event.get_field("events.actors.name"),
238 Some(EventValue::Str(Cow::Borrowed("alice")))
239 );
240 }
241
242 #[test]
243 fn json_array_at_root_level() {
244 let v = json!({"process": [{"command_line": "whoami"}, {"command_line": "id"}]});
245 let event = JsonEvent::borrow(&v);
246 assert_eq!(
247 event.get_field("process.command_line"),
248 Some(EventValue::Str(Cow::Borrowed("whoami")))
249 );
250 }
251
252 #[test]
253 fn json_array_returns_array_value() {
254 let v = json!({"a": {"tags": ["t1", "t2"]}});
255 let event = JsonEvent::borrow(&v);
256 let result = event.get_field("a.tags");
257 assert!(matches!(result, Some(EventValue::Array(_))));
258 }
259
260 #[test]
261 fn json_flat_key_wins_over_array_traversal() {
262 let v = json!({"a.b.c": "flat", "a": {"b": [{"c": "nested"}]}});
263 let event = JsonEvent::borrow(&v);
264 assert_eq!(
265 event.get_field("a.b.c"),
266 Some(EventValue::Str(Cow::Borrowed("flat")))
267 );
268 }
269
270 #[test]
271 fn json_all_string_values() {
272 let v = json!({
273 "a": "hello",
274 "b": 42,
275 "c": {"d": "world", "e": true},
276 "f": ["one", "two"]
277 });
278 let event = JsonEvent::borrow(&v);
279 let values = event.all_string_values();
280 let strs: Vec<&str> = values.iter().map(|c| c.as_ref()).collect();
281 assert!(strs.contains(&"hello"));
282 assert!(strs.contains(&"world"));
283 assert!(strs.contains(&"one"));
284 assert!(strs.contains(&"two"));
285 assert_eq!(values.len(), 4);
286 }
287
288 #[test]
289 fn json_to_json_roundtrip() {
290 let v = json!({"a": 1, "b": "hello", "c": [1, 2]});
291 let event = JsonEvent::borrow(&v);
292 assert_eq!(event.to_json(), v);
293 }
294
295 #[test]
296 fn json_owned_works() {
297 let v = json!({"key": "value"});
298 let event = JsonEvent::owned(v.clone());
299 assert_eq!(
300 event.get_field("key"),
301 Some(EventValue::Str(Cow::Borrowed("value")))
302 );
303 assert_eq!(event.to_json(), v);
304 }
305}