1use crate::prelude::{types::LogString, SiemField, SiemIp};
2use serde::{Deserialize, Serialize};
3use std::collections::{BTreeMap, BTreeSet};
4
5use super::ifield::InternalField;
6
7#[derive(Serialize, Deserialize, Debug, Clone)]
14pub struct SiemLog {
15 #[serde(skip, default)]
16 tags: BTreeSet<LogString>,
18 #[serde(flatten)]
20 pub(crate) fields: BTreeMap<LogString, InternalField>,
21 #[serde(skip, default)]
22 ip_fields: BTreeSet<LogString>,
23}
24
25impl<'a> SiemLog {
26 pub fn new<S, M>(message: M, received: i64, origin: S) -> SiemLog
27 where
28 S: Into<LogString>,
29 M: Into<String>,
30 {
31 let cw = origin.into();
32 let ms = message.into();
33 let mut fields = BTreeMap::new();
34 fields.insert(
35 LogString::Borrowed("message"),
36 SiemField::Text(LogString::Owned(ms)).into(),
37 );
38 fields.insert(LogString::Borrowed("origin"), SiemField::Text(cw).into());
39 fields.insert(
40 LogString::Borrowed("event.created"),
41 SiemField::Date(received).into(),
42 );
43 fields.insert(
44 LogString::Borrowed("event.received"),
45 SiemField::Date(received).into(),
46 );
47 SiemLog {
48 tags: BTreeSet::default(),
49 fields,
50 ip_fields: BTreeSet::new(),
51 }
52 }
53
54 pub fn message(&'a self) -> &'a str {
55 match self.field("message") {
56 Some(SiemField::Text(v)) => v,
57 _ => "",
58 }
59 }
60 pub fn set_message(&mut self, msg: String) {
61 self.fields.insert(
62 LogString::Borrowed("message"),
63 SiemField::Text(LogString::Owned(msg)).into(),
64 );
65 }
66 pub fn origin(&'a self) -> &'a str {
67 match self.field("origin") {
68 Some(SiemField::Text(v)) => v,
69 _ => "",
70 }
71 }
72 pub fn set_origin(&mut self, msg: LogString) {
73 self.fields
74 .insert(LogString::Borrowed("origin"), SiemField::Text(msg).into());
75 }
76 pub fn tenant(&'a self) -> &'a str {
77 match self.field("tenant") {
78 Some(SiemField::Text(v)) => v,
79 _ => "",
80 }
81 }
82 pub fn set_tenant<S>(&mut self, tenant: S)
83 where
84 S: Into<LogString>,
85 {
86 self.fields.insert(
87 LogString::Borrowed("tenant"),
88 SiemField::Text(tenant.into()).into(),
89 );
90 }
91 pub fn product(&'a self) -> &'a str {
93 match self.field("product") {
94 Some(SiemField::Text(v)) => v,
95 _ => "",
96 }
97 }
98 pub fn set_product<S>(&mut self, product: S)
99 where
100 S: Into<LogString>,
101 {
102 let product = product.into();
103 self.fields.insert(
104 LogString::Borrowed("product"),
105 SiemField::Text(product.clone()).into(),
106 );
107 }
108 pub fn service(&'a self) -> &'a str {
110 match self.field("service") {
111 Some(SiemField::Text(v)) => v,
112 _ => "",
113 }
114 }
115
116 pub fn set_service<S>(&mut self, service: S)
117 where
118 S: Into<LogString>,
119 {
120 let service = service.into();
121 self.fields.insert(
122 LogString::Borrowed("service"),
123 SiemField::Text(service.clone()).into(),
124 );
125 }
126 pub fn category(&'a self) -> &'a str {
128 match self.field("category") {
129 Some(SiemField::Text(v)) => v,
130 _ => "",
131 }
132 }
133 pub fn set_category<S>(&mut self, category: S)
134 where
135 S: Into<LogString>,
136 {
137 let category = category.into();
138 self.fields.insert(
139 LogString::Borrowed("category"),
140 SiemField::Text(category.clone()).into(),
141 );
142 }
143 pub fn vendor(&'a self) -> &'a str {
145 self.field("vendor")
146 .map(|v| match v {
147 SiemField::Text(v) => v,
148 _ => "",
149 })
150 .unwrap_or("")
151 }
152 pub fn set_vendor<S>(&mut self, vendor: S)
153 where
154 S: Into<LogString>,
155 {
156 let vendor = vendor.into();
157 self.fields.insert(
158 LogString::Borrowed("vendor"),
159 SiemField::Text(vendor.clone()).into(),
160 );
161 }
162 pub fn event_received(&'a self) -> i64 {
164 match self.field("event.received") {
165 Some(SiemField::Date(v)) => *v,
166 _ => 0,
167 }
168 }
169 pub fn event_created(&'a self) -> i64 {
171 match self.field("event.created") {
172 Some(SiemField::Date(v)) => *v,
173 _ => 0,
174 }
175 }
176 pub fn set_event_created(&mut self, date: i64) {
177 self.fields.insert(
178 LogString::Borrowed("event.created"),
179 SiemField::I64(date).into(),
180 );
181 }
182 pub fn has_tag(&self, tag: &str) -> bool {
183 self.tags.contains(tag)
184 }
185 pub fn add_tag(&mut self, tag: &str) {
186 self.tags.insert(LogString::Owned(tag.to_lowercase()));
187 self.fields.insert(
188 LogString::Borrowed("tags"),
189 SiemField::Array(
190 self.tags
191 .iter()
192 .map(|x| LogString::Owned(x.to_lowercase()))
193 .collect::<Vec<LogString>>(),
194 )
195 .into(),
196 );
197 }
198 pub fn tags(&'a self) -> &'a BTreeSet<LogString> {
199 &self.tags
200 }
201 pub fn field(&'a self, field_name: &str) -> Option<&SiemField> {
202 Some(&self.fields.get(field_name)?.original)
203 }
204 pub fn field_mut(&'a mut self, field_name: &str) -> Option<&mut SiemField> {
205 Some(&mut self.fields.get_mut(field_name)?.original)
206 }
207 pub fn add_field(&mut self, field_name: &str, field_value: SiemField) {
208 let field_name = LogString::Owned(field_name.to_owned());
209 self.insert(field_name, field_value);
210 }
211 pub fn insert(&mut self, field_name: LogString, field_value: SiemField) {
212 if let SiemField::IP(_) = &field_value {
213 self.ip_fields.insert(field_name.clone());
214 }
215 self.fields.insert(field_name, field_value.into());
216 }
217 pub fn has_field(&self, field_name: &str) -> bool {
218 self.fields.contains_key(field_name)
219 }
220 pub fn fields(&self) -> EventIter<'_> {
221 EventIter {
222 children: self.fields.iter(),
223 }
224 }
225 pub fn iter(&self) -> EventIter<'_> {
226 EventIter {
227 children: self.fields.iter(),
228 }
229 }
230 pub fn iter_mut(&mut self) -> EventIterMut<'_> {
231 EventIterMut {
232 children: self.fields.iter_mut(),
233 }
234 }
235 pub fn ip_fields(&self) -> EventFieldIter<'_> {
236 EventFieldIter {
237 names: self.ip_fields.iter(),
238 fields: &self.fields,
239 }
240 }
241 pub fn i64_field(&'a mut self, field_name: &str) -> Option<i64> {
243 let field = self.fields.get_mut(field_name)?;
244 match field.ni64.as_ref() {
245 super::ifield::PreStoredField::Invalid => return None,
246 super::ifield::PreStoredField::None => {}
247 super::ifield::PreStoredField::Some(v) => return Some(*v),
248 };
249 let i64field: Option<i64> = (&field.original).try_into().ok();
250 let pfield = match i64field {
251 Some(v) => super::ifield::PreStoredField::Some(v),
252 None => super::ifield::PreStoredField::Invalid,
253 };
254 field.ni64 = Box::new(pfield);
255 match field.ni64.as_ref() {
256 super::ifield::PreStoredField::Some(v) => Some(*v),
257 _ => None,
258 }
259 }
260 pub fn f64_field(&'a mut self, field_name: &str) -> Option<f64> {
262 let field = self.fields.get_mut(field_name)?;
263 match field.nf64.as_ref() {
264 super::ifield::PreStoredField::Invalid => return None,
265 super::ifield::PreStoredField::None => {}
266 super::ifield::PreStoredField::Some(v) => return Some(*v),
267 };
268 let i64field: Option<f64> = (&field.original).try_into().ok();
269 let pfield = match i64field {
270 Some(v) => super::ifield::PreStoredField::Some(v),
271 None => super::ifield::PreStoredField::Invalid,
272 };
273 field.nf64 = Box::new(pfield);
274 match field.nf64.as_ref() {
275 super::ifield::PreStoredField::Some(v) => Some(*v),
276 _ => None,
277 }
278 }
279 pub fn u64_field(&'a mut self, field_name: &str) -> Option<u64> {
281 let field = self.fields.get_mut(field_name)?;
282 match field.nu64.as_ref() {
283 super::ifield::PreStoredField::Invalid => return None,
284 super::ifield::PreStoredField::None => {}
285 super::ifield::PreStoredField::Some(v) => return Some(*v),
286 };
287 let i64field: Option<u64> = (&field.original).try_into().ok();
288 let pfield = match i64field {
289 Some(v) => super::ifield::PreStoredField::Some(v),
290 None => super::ifield::PreStoredField::Invalid,
291 };
292 field.nu64 = Box::new(pfield);
293 match field.nu64.as_ref() {
294 super::ifield::PreStoredField::Some(v) => Some(*v),
295 _ => None,
296 }
297 }
298 pub fn ip_field(&'a mut self, field_name: &str) -> Option<SiemIp> {
300 let field = self.fields.get_mut(field_name)?;
301 match field.ip.as_ref() {
302 super::ifield::PreStoredField::Invalid => return None,
303 super::ifield::PreStoredField::None => {}
304 super::ifield::PreStoredField::Some(v) => return Some(*v),
305 };
306 let i64field: Option<SiemIp> = (&field.original).try_into().ok();
307 let pfield = match i64field {
308 Some(v) => super::ifield::PreStoredField::Some(v),
309 None => super::ifield::PreStoredField::Invalid,
310 };
311 field.ip = Box::new(pfield);
312 match field.ip.as_ref() {
313 super::ifield::PreStoredField::Some(v) => Some(*v),
314 _ => None,
315 }
316 }
317 pub fn txt_field(&'a mut self, field_name: &str) -> Option<&LogString> {
319 let mut has_value = false;
320
321 let field = self.fields.get_mut(field_name)?;
322 match field.text.as_ref() {
323 super::ifield::PreStoredField::Invalid => return None,
324 super::ifield::PreStoredField::None => {}
325 super::ifield::PreStoredField::Some(_) => {
326 has_value = true;
327 }
328 };
329 if has_value {
330 match field.text.as_ref() {
331 super::ifield::PreStoredField::Some(v) => return Some(v),
332 _ => return None,
333 }
334 }
335 let txtfield: Option<LogString> = (&field.original).try_into().ok();
336 let pfield = match txtfield {
337 Some(v) => super::ifield::PreStoredField::Some(v),
338 None => super::ifield::PreStoredField::Invalid,
339 };
340 field.text = Box::new(pfield);
341 match field.text.as_ref() {
342 super::ifield::PreStoredField::Some(v) => Some(v),
343 _ => None,
344 }
345 }
346 pub fn array_field(&'a mut self, field_name: &str) -> Option<&Vec<LogString>> {
348 let mut has_value = false;
349
350 let field = self.fields.get_mut(field_name)?;
351 match field.array.as_ref() {
352 super::ifield::PreStoredField::Invalid => return None,
353 super::ifield::PreStoredField::None => {}
354 super::ifield::PreStoredField::Some(_) => {
355 has_value = true;
356 }
357 };
358 if has_value {
359 match field.array.as_ref() {
360 super::ifield::PreStoredField::Some(v) => return Some(v),
361 _ => return None,
362 }
363 }
364 let txtfield: Option<Vec<LogString>> = (&field.original).try_into().ok();
365 let pfield = match txtfield {
366 Some(v) => super::ifield::PreStoredField::Some(v),
367 None => super::ifield::PreStoredField::Invalid,
368 };
369 field.array = Box::new(pfield);
370 match field.array.as_ref() {
371 super::ifield::PreStoredField::Some(v) => Some(v),
372 _ => None,
373 }
374 }
375}
376
377pub struct EventIter<'a> {
378 children: std::collections::btree_map::Iter<'a, LogString, InternalField>,
379}
380pub struct EventFieldIter<'a> {
381 names: std::collections::btree_set::Iter<'a, LogString>,
382 fields: &'a BTreeMap<LogString, InternalField>,
383}
384
385pub struct EventIterMut<'a> {
386 children: std::collections::btree_map::IterMut<'a, LogString, InternalField>,
387}
388
389impl<'a> Iterator for EventIter<'a> {
390 type Item = (&'a LogString, &'a SiemField);
391
392 fn next(&mut self) -> Option<Self::Item> {
393 let evt = self.children.next()?;
394 Some((evt.0, &evt.1.original))
395 }
396}
397impl<'a> Iterator for EventIterMut<'a> {
398 type Item = (&'a LogString, &'a mut SiemField);
399
400 fn next(&mut self) -> Option<Self::Item> {
401 let evt = self.children.next()?;
402 Some((evt.0, &mut evt.1.original))
403 }
404}
405impl<'a> Iterator for EventFieldIter<'a> {
406 type Item = (&'a LogString, &'a SiemField);
407
408 fn next(&mut self) -> Option<Self::Item> {
409 let field = self.names.next()?;
410 let value = self.fields.get(field)?;
411 Some((field, &value.original))
412 }
413}
414
415#[cfg(test)]
416mod tests {
417 use super::*;
418 use crate::prelude::event::SiemEvent;
419 use crate::prelude::{FirewallEvent, FirewallOutcome, NetworkProtocol, SiemIp};
420
421 #[test]
422 fn check_log() {
423 let event = SiemEvent::Firewall(FirewallEvent {
424 source_ip: SiemIp::V4(0),
425 destination_ip: SiemIp::V4(10000),
426 source_port: 10000,
427 destination_port: 443,
428 outcome: FirewallOutcome::ALLOW,
429 in_bytes: 0,
430 out_bytes: 0,
431 in_interface: LogString::Borrowed("in123"),
432 out_interface: LogString::Borrowed("out123"),
433 network_protocol: NetworkProtocol::TCP,
434 });
435 let mut log: SiemLog = event.into();
436 log.set_message("<134>Aug 23 20:30:25 OPNsense.localdomain filterlog[21853]: 82,,,0,igb0,match,pass,out,4,0x0,,62,25678,0,DF,17,udp,60,192.168.1.8,8.8.8.8,5074,53,40".to_string());
437 log.set_origin("localhost".into());
438 log.add_field(
439 "event.dataset",
440 SiemField::Text(LogString::Borrowed("filterlog")),
441 );
442 let val: &str = log.field("event.dataset").unwrap().try_into().unwrap();
443 assert_eq!("filterlog", val);
444 }
445
446 #[test]
447 fn casting_between_fields() {
448 let mut log = SiemLog::new("", 0, "");
449 let (name, value) = ("field_1", "value_1");
450 log.add_field(name, value.into());
451 assert_eq!(value, log.txt_field(name).unwrap());
452
453 let (name, value) = ("field_1", 100u64);
454 log.add_field(name, value.into());
455 assert_eq!(value as u64, log.u64_field(name).unwrap());
456 assert_eq!(value as i64, log.i64_field(name).unwrap());
457 assert_eq!(value as f64, log.f64_field(name).unwrap());
458
459 let (name, value) = ("field_1", -200i64);
460 log.add_field(name, value.into());
461 assert_eq!(value as u64, log.u64_field(name).unwrap());
462 assert_eq!(value as i64, log.i64_field(name).unwrap());
463 assert_eq!(value as f64, log.f64_field(name).unwrap());
464
465 let (name, value) = ("field_1", 300.512f64);
466 log.add_field(name, value.clone().into());
467 assert_eq!(value as u64, log.u64_field(name).unwrap());
468 assert_eq!(value as i64, log.i64_field(name).unwrap());
469 assert_eq!(value as f64, log.f64_field(name).unwrap());
470
471 let (name, value) = ("field_1", SiemIp::V4(1234));
472 log.add_field(name, value.clone().into());
473 assert_eq!(value, log.ip_field(name).unwrap());
474
475 let (name, value): (&'static str, Vec<LogString>) =
476 ("field_1", vec!["value_001".into(), "value_002".into()]);
477 log.add_field(name, value.clone().into());
478 assert_eq!(&value, log.array_field(name).unwrap());
479 }
480}