use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use serde_with::{DeserializeFromStr, SerializeDisplay};
use struct_metadata::Described;
use crate::types::classification::unrestricted_classification_string;
use crate::types::{ClassificationString, ExpandingClassification, SSDeepHash, Sha1, Sha256, UpperString, MD5};
use crate::{ElasticMeta, Readable};
#[derive(SerializeDisplay, DeserializeFromStr, Clone, Copy, strum::Display, strum::EnumString, Described, Debug, PartialEq, Eq)]
#[metadata_type(ElasticMeta)]
#[strum(serialize_all = "lowercase")]
pub enum BadhashTypes {
File,
Tag,
}
#[derive(SerializeDisplay, DeserializeFromStr, Clone, Copy, strum::Display, strum::EnumString, Described, Debug, PartialEq, Eq)]
#[metadata_type(ElasticMeta)]
#[strum(serialize_all = "lowercase")]
pub enum SourceTypes {
User,
External,
}
#[derive(Debug, Serialize, Deserialize, Clone, Described, Default, PartialEq, Eq)]
#[serde(default)]
#[metadata_type(ElasticMeta)]
#[metadata(index=true, store=false)]
pub struct Attribution {
#[metadata(copyto="__text__")]
pub actor: Option<Vec<UpperString>>,
#[metadata(copyto="__text__")]
pub campaign: Option<Vec<UpperString>>,
#[metadata(copyto="__text__")]
pub category: Option<Vec<UpperString>>,
#[metadata(copyto="__text__")]
pub exploit: Option<Vec<UpperString>>,
#[metadata(copyto="__text__")]
pub implant: Option<Vec<UpperString>>,
#[metadata(copyto="__text__")]
pub family: Option<Vec<UpperString>>,
#[metadata(copyto="__text__")]
pub network: Option<Vec<UpperString>>,
}
impl Attribution {
pub fn update(&mut self, mut other: Attribution) {
macro_rules! update_field {($self: ident, $other: ident, $key: ident) => {
match &mut $self.$key {
Some(data) => {
data.append(&mut $other.$key.take().unwrap_or_default());
data.sort_unstable();
data.dedup();
},
None => {
$self.$key = $other.$key;
}
};
};}
update_field!(self, other, actor);
update_field!(self, other, campaign);
update_field!(self, other, category);
update_field!(self, other, exploit);
update_field!(self, other, implant);
update_field!(self, other, family);
update_field!(self, other, network);
}
}
#[derive(Debug, Serialize, Deserialize, Clone, Described, Default, PartialEq, Eq)]
#[serde(default)]
#[metadata_type(ElasticMeta)]
#[metadata(index=true, store=true)]
pub struct Hashes {
#[metadata(copyto="__text__")]
pub md5: Option<MD5>,
#[metadata(copyto="__text__")]
pub sha1: Option<Sha1>,
#[metadata(copyto="__text__")]
pub sha256: Option<Sha256>,
#[metadata(copyto="__text__")]
pub ssdeep: Option<SSDeepHash>,
#[metadata(copyto="__text__")]
pub tlsh: Option<String>,
}
impl Hashes {
pub fn update(&mut self, other: Hashes) {
macro_rules! update_field {($self: ident, $other: ident, $key: ident) => {
$self.$key = $other.$key.or($self.$key.take())
};}
update_field!(self, other, md5);
update_field!(self, other, sha1);
update_field!(self, other, sha256);
update_field!(self, other, ssdeep);
update_field!(self, other, tlsh);
}
pub fn label_hash(&self) -> Option<String> {
macro_rules! read_field {($self: ident, $key: ident) => {
if let Some(val) = &($self.$key) {
return Some(val.to_string());
}
};}
read_field!(self, sha256);
read_field!(self, sha1);
read_field!(self, md5);
read_field!(self, tlsh);
read_field!(self, ssdeep);
None
}
}
#[derive(Debug, Serialize, Deserialize, Clone, Described, Default, PartialEq, Eq)]
#[serde(default)]
#[metadata_type(ElasticMeta)]
#[metadata(index=true, store=false)]
pub struct File {
#[metadata(store=true, copyto="__text__")]
pub name: Vec<String>,
pub size: Option<i64>,
#[serde(rename="type")]
pub file_type: Option<String>,
}
#[derive(Debug, Serialize, Deserialize, Clone, Described, PartialEq, Eq)]
#[metadata_type(ElasticMeta)]
#[metadata(index=true, store=false)]
pub struct Source {
#[serde(default="unrestricted_classification_string")]
pub classification: ClassificationString,
#[metadata(store=true)]
pub name: String,
pub reason: Vec<String>,
#[serde(rename="type")]
pub source_type: SourceTypes,
}
#[derive(Debug, Serialize, Deserialize, Clone, Described, PartialEq, Eq)]
#[metadata_type(ElasticMeta)]
#[metadata(index=true, store=true)]
pub struct Tag {
#[serde(rename="type")]
pub tag_type: String,
#[metadata(copyto="__text__")]
pub value: String,
}
#[derive(Debug, Serialize, Deserialize, Clone, Described, PartialEq, Eq)]
#[metadata_type(ElasticMeta)]
#[metadata(index=true, store=true)]
pub struct Badlist {
#[serde(default="chrono::Utc::now")]
pub added: DateTime<Utc>,
#[serde(default)]
pub attribution: Option<Attribution>,
#[serde(flatten)]
pub classification: ExpandingClassification,
#[serde(default="default_true")]
pub enabled: bool,
#[serde(default)]
pub expiry_ts: Option<DateTime<Utc>>,
#[serde(default)]
pub hashes: Hashes,
#[serde(default)]
pub file: Option<File>,
pub sources: Vec<Source>,
#[serde(default)]
pub tag: Option<Tag>,
#[serde(rename="type")]
pub hash_type: BadhashTypes,
#[serde(default="chrono::Utc::now")]
pub updated: DateTime<Utc>,
}
fn default_true() -> bool { true }
impl Readable for Badlist {
fn set_from_archive(&mut self, _from_archive: bool) {}
}