1use std::borrow::Cow;
2
3use rsigma_parser::fieldpath::{first_unescaped, unescape_brackets};
4use serde_json::Value;
5
6use super::{Event, EventValue};
7
8const MAX_NESTING_DEPTH: usize = 64;
10
11#[derive(Debug)]
19pub struct JsonEvent<'a> {
20 inner: Cow<'a, Value>,
21}
22
23impl<'a> JsonEvent<'a> {
24 pub fn borrow(v: &'a Value) -> Self {
26 Self {
27 inner: Cow::Borrowed(v),
28 }
29 }
30
31 pub fn owned(v: Value) -> Self {
33 Self {
34 inner: Cow::Owned(v),
35 }
36 }
37}
38
39impl<'a> From<&'a Value> for JsonEvent<'a> {
40 fn from(v: &'a Value) -> Self {
41 Self::borrow(v)
42 }
43}
44
45impl From<Value> for JsonEvent<'static> {
46 fn from(v: Value) -> Self {
47 Self::owned(v)
48 }
49}
50
51impl<'a> Event for JsonEvent<'a> {
52 fn get_field(&self, path: &str) -> Option<EventValue<'_>> {
61 let value: &Value = &self.inner;
62
63 if let Some(obj) = value.as_object() {
64 if let Some(v) = obj.get(path) {
65 return Some(EventValue::from(v));
66 }
67
68 if path.contains('.') || path.contains('[') || path.contains('\\') {
69 if let Some(root) = path_root_key(path)
72 && !obj.contains_key(root.as_ref())
73 {
74 return None;
75 }
76 let ops = parse_path_ops(path);
77 let mut collected: Vec<EventValue<'_>> = Vec::new();
78 collect_by_ops(value, &ops, &mut collected);
79 return match collected.len() {
80 0 => None,
81 1 => collected.pop(),
82 _ => Some(EventValue::Array(collected)),
83 };
84 }
85
86 return None;
87 }
88
89 if path.contains('.') || path.contains('[') || path.contains('\\') {
90 let ops = parse_path_ops(path);
91 let mut collected: Vec<EventValue<'_>> = Vec::new();
92 collect_by_ops(value, &ops, &mut collected);
93 return match collected.len() {
94 0 => None,
95 1 => collected.pop(),
96 _ => Some(EventValue::Array(collected)),
97 };
98 }
99
100 None
101 }
102
103 fn top_level_keys(&self) -> Option<Vec<Cow<'_, str>>> {
104 match self.inner.as_ref() {
105 Value::Object(map) => Some(map.keys().map(|k| Cow::Borrowed(k.as_str())).collect()),
106 _ => None,
109 }
110 }
111
112 fn visit_top_level_keys(&self, visit: &mut dyn FnMut(&str)) -> bool {
113 match self.inner.as_ref() {
114 Value::Object(map) => {
115 for key in map.keys() {
116 visit(key.as_str());
117 }
118 true
119 }
120 _ => false,
121 }
122 }
123
124 fn any_string_value(&self, pred: &dyn Fn(&str) -> bool) -> bool {
129 any_string_value_json(&self.inner, pred, MAX_NESTING_DEPTH)
130 }
131
132 fn all_string_values(&self) -> Vec<Cow<'_, str>> {
138 let mut values = Vec::new();
139 collect_string_values_json(&self.inner, &mut values, MAX_NESTING_DEPTH);
140 values
141 }
142
143 fn visit_string_values(&self, visit: &mut dyn FnMut(&str)) {
144 visit_string_values_json(&self.inner, visit, MAX_NESTING_DEPTH);
145 }
146
147 fn to_json(&self) -> Value {
148 self.inner.as_ref().clone()
149 }
150
151 fn field_keys(&self) -> Vec<Cow<'_, str>> {
162 let mut out = Vec::new();
163 collect_field_keys(&self.inner, "", &mut out, MAX_NESTING_DEPTH);
164 out
165 }
166}
167
168fn path_root_key(path: &str) -> Option<Cow<'_, str>> {
173 let segment = match first_unescaped(path, b'.') {
174 Some(0) => return None,
175 Some(pos) => &path[..pos],
176 None => path,
177 };
178 if segment.is_empty() {
179 return None;
180 }
181 let name = match first_unescaped(segment, b'[') {
182 Some(0) => return None,
183 Some(pos) => &segment[..pos],
184 None => segment,
185 };
186 if name.is_empty() {
187 return None;
188 }
189 Some(unescape_brackets(name))
190}
191
192enum PathOp<'a> {
194 Key(Cow<'a, str>),
197 Index(i64),
200}
201
202pub(crate) fn resolve_array_index(index: i64, len: usize) -> Option<usize> {
205 if index >= 0 {
206 usize::try_from(index).ok().filter(|&i| i < len)
207 } else {
208 usize::try_from(index.unsigned_abs())
209 .ok()
210 .and_then(|abs| len.checked_sub(abs))
211 }
212}
213
214fn parse_path_ops(path: &str) -> Vec<PathOp<'_>> {
219 let mut ops = Vec::new();
220 for part in path.split('.') {
221 match first_unescaped(part, b'[') {
222 Some(bpos) if parse_index_groups(&part[bpos..]).is_some() => {
223 let name = &part[..bpos];
224 if !name.is_empty() {
225 ops.push(PathOp::Key(unescape_brackets(name)));
226 }
227 for idx in parse_index_groups(&part[bpos..]).expect("checked") {
228 ops.push(PathOp::Index(idx));
229 }
230 }
231 _ => ops.push(PathOp::Key(unescape_brackets(part))),
234 }
235 }
236 ops
237}
238
239fn parse_index_groups(s: &str) -> Option<Vec<i64>> {
242 let mut out = Vec::new();
243 let mut rem = s;
244 while !rem.is_empty() {
245 let rest = rem.strip_prefix('[')?;
246 let close = rest.find(']')?;
247 let idx: i64 = rest[..close].parse().ok()?;
248 out.push(idx);
249 rem = &rest[close + 1..];
250 }
251 Some(out)
252}
253
254fn collect_by_ops<'a>(current: &'a Value, ops: &[PathOp<'_>], out: &mut Vec<EventValue<'a>>) {
261 let Some((op, rest)) = ops.split_first() else {
262 out.push(EventValue::from(current));
263 return;
264 };
265
266 match op {
267 PathOp::Key(key) => match current {
268 Value::Object(map) => {
269 if let Some(next) = map.get(key.as_ref()) {
270 collect_by_ops(next, rest, out);
271 }
272 }
273 Value::Array(arr) => {
274 for item in arr {
275 collect_by_ops(item, ops, out);
276 }
277 }
278 _ => {}
279 },
280 PathOp::Index(i) => {
281 if let Value::Array(arr) = current
282 && let Some(idx) = resolve_array_index(*i, arr.len())
283 && let Some(next) = arr.get(idx)
284 {
285 collect_by_ops(next, rest, out);
286 }
287 }
288 }
289}
290
291fn any_string_value_json(v: &Value, pred: &dyn Fn(&str) -> bool, depth: usize) -> bool {
292 if depth == 0 {
293 return false;
294 }
295 match v {
296 Value::String(s) => pred(s.as_str()),
297 Value::Object(map) => map
298 .values()
299 .any(|val| any_string_value_json(val, pred, depth - 1)),
300 Value::Array(arr) => arr
301 .iter()
302 .any(|val| any_string_value_json(val, pred, depth - 1)),
303 _ => false,
304 }
305}
306
307fn collect_field_keys<'a>(v: &'a Value, prefix: &str, out: &mut Vec<Cow<'a, str>>, depth: usize) {
308 if depth == 0 {
309 return;
310 }
311 if let Value::Object(map) = v {
312 for (k, child) in map {
313 let path = if prefix.is_empty() {
314 k.clone()
315 } else {
316 format!("{prefix}.{k}")
317 };
318 match child {
319 Value::Object(_) => collect_field_keys(child, &path, out, depth - 1),
326 _ => out.push(Cow::Owned(path)),
327 }
328 }
329 }
330}
331
332fn collect_string_values_json<'a>(v: &'a Value, out: &mut Vec<Cow<'a, str>>, depth: usize) {
333 if depth == 0 {
334 return;
335 }
336 match v {
337 Value::String(s) => out.push(Cow::Borrowed(s.as_str())),
338 Value::Object(map) => {
339 for val in map.values() {
340 collect_string_values_json(val, out, depth - 1);
341 }
342 }
343 Value::Array(arr) => {
344 for val in arr {
345 collect_string_values_json(val, out, depth - 1);
346 }
347 }
348 _ => {}
349 }
350}
351
352fn visit_string_values_json(v: &Value, visit: &mut dyn FnMut(&str), depth: usize) {
353 if depth == 0 {
354 return;
355 }
356 match v {
357 Value::String(s) => visit(s.as_str()),
358 Value::Object(map) => {
359 for val in map.values() {
360 visit_string_values_json(val, visit, depth - 1);
361 }
362 }
363 Value::Array(arr) => {
364 for val in arr {
365 visit_string_values_json(val, visit, depth - 1);
366 }
367 }
368 _ => {}
369 }
370}
371
372#[cfg(test)]
373mod tests {
374 use super::*;
375 use serde_json::json;
376
377 #[test]
378 fn json_flat_field() {
379 let v = json!({"CommandLine": "whoami", "User": "admin"});
380 let event = JsonEvent::borrow(&v);
381 assert_eq!(
382 event.get_field("CommandLine"),
383 Some(EventValue::Str(Cow::Borrowed("whoami")))
384 );
385 }
386
387 #[test]
388 fn json_nested_field() {
389 let v = json!({"actor": {"id": "user123", "type": "User"}});
390 let event = JsonEvent::borrow(&v);
391 assert_eq!(
392 event.get_field("actor.id"),
393 Some(EventValue::Str(Cow::Borrowed("user123")))
394 );
395 }
396
397 #[test]
398 fn json_flat_key_precedence() {
399 let v = json!({"actor.id": "flat_value", "actor": {"id": "nested_value"}});
400 let event = JsonEvent::borrow(&v);
401 assert_eq!(
402 event.get_field("actor.id"),
403 Some(EventValue::Str(Cow::Borrowed("flat_value")))
404 );
405 }
406
407 #[test]
408 fn json_missing_field() {
409 let v = json!({"foo": "bar"});
410 let event = JsonEvent::borrow(&v);
411 assert_eq!(event.get_field("missing"), None);
412 }
413
414 #[test]
415 fn json_dotted_miss_skips_absent_root() {
416 let v = json!({"message": "x", "product": "windows"});
417 let event = JsonEvent::borrow(&v);
418 assert_eq!(event.get_field("Event.EventData.CommandLine"), None);
419 assert_eq!(event.get_field("actor.id"), None);
420 assert_eq!(event.get_field("name[0].x"), None);
421 }
422
423 #[test]
424 fn json_top_level_keys_object() {
425 let v = json!({"message": "x", "product": "windows"});
426 let event = JsonEvent::borrow(&v);
427 let mut keys: Vec<String> = event
428 .top_level_keys()
429 .unwrap()
430 .into_iter()
431 .map(|k| k.into_owned())
432 .collect();
433 keys.sort();
434 assert_eq!(keys, vec!["message", "product"]);
435 }
436
437 #[test]
438 fn json_top_level_keys_non_object_unknown() {
439 let v = json!([{"a": 1}]);
440 let event = JsonEvent::borrow(&v);
441 assert_eq!(event.top_level_keys(), None);
442 }
443
444 #[test]
445 fn path_root_key_segments() {
446 assert_eq!(path_root_key("CommandLine").as_deref(), Some("CommandLine"));
447 assert_eq!(path_root_key("actor.id").as_deref(), Some("actor"));
448 assert_eq!(path_root_key("name[0].x").as_deref(), Some("name"));
449 assert_eq!(path_root_key(".id"), None);
450 assert_eq!(path_root_key("[0].x"), None);
451 }
452
453 #[test]
454 fn json_null_field() {
455 let v = json!({"foo": null});
456 let event = JsonEvent::borrow(&v);
457 assert_eq!(event.get_field("foo"), Some(EventValue::Null));
458 }
459
460 #[test]
461 fn json_array_traversal() {
462 let v = json!({"a": {"b": [{"c": "found"}, {"c": "other"}]}});
465 let event = JsonEvent::borrow(&v);
466 assert_eq!(
467 event.get_field("a.b.c"),
468 Some(EventValue::Array(vec![
469 EventValue::Str(Cow::Borrowed("found")),
470 EventValue::Str(Cow::Borrowed("other")),
471 ]))
472 );
473 }
474
475 #[test]
476 fn json_array_traversal_no_match() {
477 let v = json!({"a": {"b": [{"x": 1}, {"y": 2}]}});
478 let event = JsonEvent::borrow(&v);
479 assert_eq!(event.get_field("a.b.c"), None);
480 }
481
482 #[test]
483 fn json_array_traversal_deep() {
484 let v = json!({
485 "events": [
486 {"actors": [{"name": "alice"}, {"name": "bob"}]},
487 {"actors": [{"name": "charlie"}]}
488 ]
489 });
490 let event = JsonEvent::borrow(&v);
491 assert_eq!(
493 event.get_field("events.actors.name"),
494 Some(EventValue::Array(vec![
495 EventValue::Str(Cow::Borrowed("alice")),
496 EventValue::Str(Cow::Borrowed("bob")),
497 EventValue::Str(Cow::Borrowed("charlie")),
498 ]))
499 );
500 }
501
502 #[test]
503 fn json_array_at_root_level() {
504 let v = json!({"process": [{"command_line": "whoami"}, {"command_line": "id"}]});
505 let event = JsonEvent::borrow(&v);
506 assert_eq!(
507 event.get_field("process.command_line"),
508 Some(EventValue::Array(vec![
509 EventValue::Str(Cow::Borrowed("whoami")),
510 EventValue::Str(Cow::Borrowed("id")),
511 ]))
512 );
513 }
514
515 #[test]
516 fn json_array_returns_array_value() {
517 let v = json!({"a": {"tags": ["t1", "t2"]}});
518 let event = JsonEvent::borrow(&v);
519 let result = event.get_field("a.tags");
520 assert!(matches!(result, Some(EventValue::Array(_))));
521 }
522
523 #[test]
524 fn json_flat_key_wins_over_array_traversal() {
525 let v = json!({"a.b.c": "flat", "a": {"b": [{"c": "nested"}]}});
526 let event = JsonEvent::borrow(&v);
527 assert_eq!(
528 event.get_field("a.b.c"),
529 Some(EventValue::Str(Cow::Borrowed("flat")))
530 );
531 }
532
533 #[test]
534 fn json_all_string_values() {
535 let v = json!({
536 "a": "hello",
537 "b": 42,
538 "c": {"d": "world", "e": true},
539 "f": ["one", "two"]
540 });
541 let event = JsonEvent::borrow(&v);
542 let values = event.all_string_values();
543 let strs: Vec<&str> = values.iter().map(|c| c.as_ref()).collect();
544 assert!(strs.contains(&"hello"));
545 assert!(strs.contains(&"world"));
546 assert!(strs.contains(&"one"));
547 assert!(strs.contains(&"two"));
548 assert_eq!(values.len(), 4);
549 }
550
551 #[test]
552 fn json_to_json_roundtrip() {
553 let v = json!({"a": 1, "b": "hello", "c": [1, 2]});
554 let event = JsonEvent::borrow(&v);
555 assert_eq!(event.to_json(), v);
556 }
557
558 #[test]
559 fn json_owned_works() {
560 let v = json!({"key": "value"});
561 let event = JsonEvent::owned(v.clone());
562 assert_eq!(
563 event.get_field("key"),
564 Some(EventValue::Str(Cow::Borrowed("value")))
565 );
566 assert_eq!(event.to_json(), v);
567 }
568
569 #[test]
570 fn json_field_keys_flat() {
571 let v = json!({"CommandLine": "x", "User": "y"});
572 let event = JsonEvent::borrow(&v);
573 let mut keys: Vec<String> = event.field_keys().iter().map(|c| c.to_string()).collect();
574 keys.sort();
575 assert_eq!(keys, vec!["CommandLine", "User"]);
576 }
577
578 #[test]
579 fn json_field_keys_nested_leaves_only() {
580 let v = json!({"actor": {"id": "u1", "type": "User"}, "verb": "login"});
584 let event = JsonEvent::borrow(&v);
585 let mut keys: Vec<String> = event.field_keys().iter().map(|c| c.to_string()).collect();
586 keys.sort();
587 assert_eq!(keys, vec!["actor.id", "actor.type", "verb"]);
588 }
589
590 #[test]
591 fn json_field_keys_deeply_nested_leaves_only() {
592 let v = json!({"a": {"b": {"c": 1}}, "flat": "x"});
593 let event = JsonEvent::borrow(&v);
594 let mut keys: Vec<String> = event.field_keys().iter().map(|c| c.to_string()).collect();
595 keys.sort();
596 assert_eq!(keys, vec!["a.b.c", "flat"]);
597 }
598
599 #[test]
600 fn json_field_keys_array_parent_only() {
601 let v = json!({"events": [{"id": 1}, {"id": 2}]});
602 let event = JsonEvent::borrow(&v);
603 let keys: Vec<String> = event.field_keys().iter().map(|c| c.to_string()).collect();
604 assert_eq!(keys, vec!["events"]);
606 }
607
608 #[test]
609 fn json_field_keys_top_level_non_object_empty() {
610 let v = json!("just a string");
611 let event = JsonEvent::owned(v);
612 assert!(event.field_keys().is_empty());
613 }
614
615 #[test]
616 fn json_traversal_with_consecutive_dots_does_not_panic() {
617 let v = json!({"a": {"b": "x"}});
624 let event = JsonEvent::borrow(&v);
625 assert_eq!(event.get_field("a..b"), None);
626 }
627
628 #[test]
629 fn json_traversal_with_trailing_dot_does_not_panic() {
630 let v = json!({"a": {"b": "x"}});
635 let event = JsonEvent::borrow(&v);
636 assert_eq!(event.get_field("a.b."), None);
637 }
638}