assemblyline_models/datastore/
retrohunt.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use serde_with::{SerializeDisplay, DeserializeFromStr};
4use struct_metadata::Described;
5
6use crate::{ElasticMeta, types::{Sha256, ClassificationString, Text, ExpandingClassification}};
7
8#[derive(SerializeDisplay, DeserializeFromStr, Debug, PartialEq, Eq, strum::Display, strum::EnumString, Described, Clone, Copy)]
9#[metadata_type(ElasticMeta)]
10#[strum(serialize_all = "snake_case")]
11pub enum IndexCatagory {
12    Hot = 1,
13    Archive = 2,
14    HotAndArchive = 3,
15}
16
17/// A search run on stored files.
18#[derive(Serialize, Deserialize, Debug, Described, Clone)]
19#[metadata_type(ElasticMeta)]
20#[metadata(index=true, store=true)]
21pub struct Retrohunt {
22    /// Which archive catagories do we run on
23    pub indices: IndexCatagory,
24    /// Classification for the retrohunt job
25    #[serde(flatten)]
26    pub classification: ExpandingClassification,
27    /// Maximum classification of results in the search
28    #[metadata(mapping="keyword")]
29    pub search_classification: ClassificationString,
30    /// User who created this retrohunt job
31    #[metadata(copyto="__text__")]
32    pub creator: String,
33    /// Human readable description of this retrohunt job
34    #[metadata(copyto="__text__")]
35    pub description: Text,
36    /// Tags describing this retrohunt job"
37    // pub tags = odm.Optional(odm.mapping(odm.sequence(odm.keyword(copyto="__text__")),)
38    /// Expiry timestamp of this retrohunt job
39    #[metadata(store=false)]
40    pub expiry_ts: Option<DateTime<Utc>>,
41
42    /// Earliest expiry group this search will include
43    pub start_group: u32,
44    /// Latest expiry group this search will include
45    pub end_group: u32,
46
47    /// Start time for the search.
48    pub created_time: DateTime<Utc>,
49    /// Start time for the search.
50    pub started_time: DateTime<Utc>,
51    /// Time that the search finished
52    #[metadata(store=false)]
53    pub completed_time: Option<DateTime<Utc>>,
54
55    /// Unique id identifying this retrohunt job
56    pub key: String,
57    /// Text of filter query derived from yara signature
58    #[metadata(store=false)]
59    pub raw_query: String,
60    /// Text of original yara signature run
61    #[metadata(store=false, copyto="__text__")]
62    pub yara_signature: String,
63
64    /// List of error messages that occured during the search
65    #[metadata(store=false)]
66    pub errors: Vec<String>,
67    /// List of warning messages that occured during the search
68    #[metadata(store=false)]
69    pub warnings: Vec<String>,
70    /// Boolean that indicates if this retrohunt job is finished
71    pub finished: bool,
72    /// Indicates if the list of hits been truncated at some limit
73    pub truncated: bool,
74}
75
76/// A hit encountered during a retrohunt search.
77#[derive(Serialize, Deserialize, Debug, Described, Clone, PartialEq, Eq)]
78#[metadata_type(ElasticMeta)]
79#[metadata(index=true, store=true)]
80pub struct RetrohuntHit {
81    /// Unique id identifying this retrohunt result
82    pub key: String,
83    /// Classification string for the retrohunt job and results list
84    #[serde(flatten)]
85    pub classification: ExpandingClassification,
86    pub sha256: Sha256,
87    /// Expiry for this entry.
88    #[metadata(store=false)]
89    pub expiry_ts: Option<DateTime<Utc>>,
90    pub search: String,
91}
92
93#[cfg(test)]
94mod test {
95    use chrono::Utc;
96
97    use super::RetrohuntHit;
98    use crate::{serialize::test::setup_classification, types::ExpandingClassification};
99
100    #[test]
101    fn hit_roundtrip(){
102        let parser = setup_classification();
103        let data = RetrohuntHit {
104            key: "abc123".to_owned(),
105            classification: ExpandingClassification::new("L0".to_owned(), &parser).unwrap(),
106            sha256: "cb3f7b194d220004ffa6eef1305849bcef38033c49cb1b16c5ab3c3d60bd9d20".parse().unwrap(),
107            expiry_ts: Utc::now().into(),
108            search: "search".to_owned(),
109        };
110
111        let json = serde_json::to_string_pretty(&data).unwrap();
112        println!("{json}");
113        let data_copy = serde_json::from_str(&json).unwrap();
114        assert_eq!(data, data_copy);
115    }
116}