Skip to main content

stix_matcher/
observation.rs

1//! An observation: a set of cyber-observable objects plus temporal metadata.
2
3use stix_model::StixObject;
4
5/// A set of objects observed together. Each STIX `observed-data` SDO maps to one
6/// `Observation`; `match_scos` treats a flat list as a single observation.
7#[derive(Debug, Clone)]
8pub struct Observation {
9    /// The objects observed together.
10    pub objects: Vec<StixObject>,
11    /// The start of the observation window (RFC3339 timestamp), if known.
12    pub first_observed: Option<String>,
13    /// The end of the observation window (RFC3339 timestamp), if known.
14    pub last_observed: Option<String>,
15    /// How many times the contents were observed.
16    pub number_observed: u64,
17}
18
19impl Observation {
20    /// A single observation of the given objects (`number_observed` = 1, no times).
21    pub fn new(objects: Vec<StixObject>) -> Self {
22        Observation {
23            objects,
24            first_observed: None,
25            last_observed: None,
26            number_observed: 1,
27        }
28    }
29}
30
31#[cfg(test)]
32mod tests {
33    use super::*;
34    use stix_model::StixObject;
35
36    fn sco(json: serde_json::Value) -> StixObject {
37        StixObject::from_json(json).unwrap()
38    }
39
40    #[test]
41    fn new_defaults_number_observed_to_one() {
42        let o = Observation::new(vec![sco(serde_json::json!({
43            "type": "ipv4-addr", "id": "ipv4-addr--1", "value": "1.2.3.4"
44        }))]);
45        assert_eq!(o.objects.len(), 1);
46        assert_eq!(o.number_observed, 1);
47        assert!(o.first_observed.is_none());
48    }
49}