use chrono::{DateTime, Utc};
use serde::{Serialize, Deserialize};
use serde_with::{SerializeDisplay, DeserializeFromStr};
use struct_metadata::Described;
use crate::{Sha256, ElasticMeta, ClassificationString, Text, ExpandingClassification};
#[derive(SerializeDisplay, DeserializeFromStr, Debug, PartialEq, Eq, strum::Display, strum::EnumString, Described, Clone, Copy)]
#[metadata_type(ElasticMeta)]
#[strum(serialize_all = "snake_case")]
pub enum IndexCatagory {
Hot = 1,
Archive = 2,
HotAndArchive = 3,
}
#[derive(Serialize, Deserialize, Debug, Described, Clone)]
#[metadata_type(ElasticMeta)]
#[metadata(index=true, store=true)]
pub struct Retrohunt {
pub indices: IndexCatagory,
#[serde(flatten)]
pub classification: ExpandingClassification,
pub search_classification: ClassificationString,
#[metadata(copyto="__text__")]
pub creator: String,
#[metadata(copyto="__text__")]
pub description: Text,
#[metadata(store=false)]
pub expiry_ts: Option<DateTime<Utc>>,
pub start_group: u32,
pub end_group: u32,
pub created_time: DateTime<Utc>,
pub started_time: DateTime<Utc>,
pub completed_time: Option<DateTime<Utc>>,
pub code: String,
#[metadata(store=false)]
pub raw_query: String,
#[metadata(store=false, copyto="__text__")]
pub yara_signature: String,
#[metadata(store=false)]
pub errors: Vec<String>,
#[metadata(store=false)]
pub warnings: Vec<String>,
pub finished: bool,
pub truncated: bool,
}
#[derive(Serialize, Deserialize, Debug, Described, Clone)]
#[metadata_type(ElasticMeta)]
#[metadata(index=true, store=true)]
struct RetrohuntHit {
#[serde(flatten)]
pub classification: ExpandingClassification,
pub sha256: Sha256,
#[metadata(store=false)]
pub expiry_ts: Option<DateTime<Utc>>,
pub search: String,
}
#[cfg(test)]
mod python {
use serde_json::json;
use crate::datastore::retrohunt::RetrohuntHit;
use crate::{meta::Mappings, datastore::retrohunt::Retrohunt};
use crate::meta::build_mapping;
use pretty_assertions::assert_eq;
fn load_mapping(module: &str, name: &str) -> Mappings {
let output = std::process::Command::new("python").arg("-c").arg("from assemblyline.datastore.support.build import build_mapping; from assemblyline.odm.models. ".to_owned() + module + " import " + name + "; import json; print(json.dumps(build_mapping(" + name + ".fields().values())))").output().unwrap();
let stderr = String::from_utf8(output.stderr).unwrap();
let stdout = String::from_utf8(output.stdout).unwrap();
if !output.status.success() || !stderr.is_empty() {
println!("{stderr}");
panic!();
}
let (properties, dynamic_templates): (serde_json::Value, serde_json::Value) = serde_json::from_str(&stdout).unwrap();
let py_mapping = json!({
"properties": properties,
"dynamic_templates": dynamic_templates,
});
serde_json::from_value(py_mapping).unwrap()
}
#[test]
fn retrohunt_schema() {
let py_mappings = load_mapping("retrohunt", "Retrohunt");
let mapping = build_mapping::<Retrohunt>().unwrap();
assert_eq!(mapping, py_mappings);
}
#[test]
fn retrohunt_hits_schema() {
let py_mappings = load_mapping("retrohunt", "RetrohuntHit");
let mapping = build_mapping::<RetrohuntHit>().unwrap();
assert_eq!(mapping, py_mappings);
}
}