assemblyline_models/datastore/
file.rs1
2use chrono::{DateTime, Utc};
3use md5::Digest;
4use serde::{Serialize, Deserialize};
5use serde_with::{DeserializeFromStr, SerializeDisplay};
6use struct_metadata::Described;
7
8use crate::{ElasticMeta, Readable};
9use crate::types::{ExpandingClassification, SSDeepHash, Sha1, Sha256, Text, MD5};
10
11#[derive(Debug, Serialize, Deserialize, Described, Clone)]
13#[metadata_type(ElasticMeta)]
14#[metadata(index=true, store=true)]
15pub struct File {
16 pub archive_ts: Option<DateTime<Utc>>,
18 #[metadata(index=false, store=false)]
20 pub ascii: String,
21 #[serde(flatten)]
23 pub classification: ExpandingClassification,
24 pub entropy: f32,
26 #[metadata(store=false)]
28 pub expiry_ts: Option<DateTime<Utc>>,
29 #[serde(default)]
31 pub is_section_image: bool,
32 #[serde(default)]
34 pub is_supplementary: bool,
35 #[metadata(index=false, store=false)]
37 pub hex: String,
38 #[serde(default)]
40 #[metadata(copyto="__text__")]
41 pub labels: Vec<String>,
42 #[serde(default)]
44 pub label_categories: LabelCategories,
45 #[metadata(copyto="__text__")]
47 pub md5: MD5,
48 #[metadata(store=false)]
50 pub magic: String,
51 #[metadata(store=false)]
53 pub mime: Option<String>,
54 #[serde(default)]
56 pub seen: Seen,
57 #[metadata(copyto="__text__")]
59 pub sha1: Sha1,
60 #[metadata(copyto="__text__")]
62 pub sha256: Sha256,
63 #[metadata(mapping="long")]
65 pub size: u64,
66 #[metadata(store=false)]
68 pub ssdeep: SSDeepHash,
69 #[serde(rename = "type")]
71 #[metadata(copyto="__text__")]
72 pub file_type: String,
73 #[metadata(copyto="__text__")]
75 pub tlsh: Option<String>,
76 #[serde(default)]
78 #[metadata(index=false, store=false)]
79 pub from_archive: bool,
80 #[serde(default)]
82 pub uri_info: Option<URIInfo>,
83 #[serde(default)]
85 pub comments: Vec<Comment>,
86}
87
88#[cfg(feature = "rand")]
89impl rand::distr::Distribution<File> for rand::distr::StandardUniform {
90 fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> File {
91 let mut data = vec![];
92 for _ in 0..1000 {
93 data.push(rng.random());
94 }
95 File::gen_for_sample(&data, rng)
96 }
97}
98
99impl File {
100 pub fn gen_for_sample<R: rand::Rng + ?Sized>(data: &[u8], rng: &mut R) -> File {
101 let sha256 = hex::encode(sha2::Sha256::new().chain_update(data).finalize());
102 let sha1 = hex::encode(sha1::Sha1::new().chain_update(data).finalize());
103 let md5 = hex::encode(md5::Md5::new().chain_update(data).finalize());
104
105 File {
106 archive_ts: None,
107 ascii: String::from_iter(data.iter().take(64).map(|byte| if byte.is_ascii() { *byte as char } else { '.' })),
108 classification: ExpandingClassification::try_unrestricted().unwrap(),
109 entropy: rng.random_range(0.0..1.0),
110 expiry_ts: None,
111 is_section_image: rng.random(),
112 is_supplementary: rng.random(),
113 hex: String::from_iter(data.iter().take(64).map(|byte| if byte.is_ascii() { *byte as char } else { '.' })),
114 labels: vec![],
115 label_categories: Default::default(),
116 md5: md5.parse().unwrap(),
117 magic: "Binary data".to_string(),
118 mime: Some("application/octet-stream".to_string()),
119 seen: Seen { count: 1, first: chrono::Utc::now(), last: chrono::Utc::now() },
120 sha1: sha1.parse().unwrap(),
121 sha256: sha256.parse().unwrap(),
122 size: data.len() as u64,
123 ssdeep: rng.random(),
124 file_type: "unknown".to_string(),
125 tlsh: None,
126 from_archive: false,
127 uri_info: None,
128 comments: vec![],
129 }
130 }
131}
132
133impl Readable for File {
134 fn set_from_archive(&mut self, from_archive: bool) {
135 self.from_archive = from_archive;
136 }
137}
138
139#[derive(Debug, Serialize, Deserialize, Described, Clone, PartialEq, Eq)]
141#[metadata_type(ElasticMeta)]
142#[metadata(index=true, store=true)]
143pub struct URIInfo {
144 pub uri: String,
146
147 pub scheme: String,
149 pub netloc: String,
150 pub path: Option<String>,
151 pub params: Option<String>,
152 pub query: Option<String>,
153 pub fragment: Option<String>,
154
155 pub username: Option<String>,
157 pub password: Option<String>,
158 pub hostname: String,
159 pub port: Option<u16>,
160}
161
162#[derive(Debug, Serialize, Deserialize, Described, Clone)]
164#[metadata_type(ElasticMeta)]
165#[metadata(index=true, store=true)]
166pub struct Seen {
167 #[serde(default = "default_seen_count")]
169 #[metadata(mapping="integer")]
170 pub count: u64,
171 #[serde(default = "default_now")]
173 pub first: DateTime<Utc>,
174 #[serde(default = "default_now")]
176 pub last: DateTime<Utc>,
177}
178
179fn default_seen_count() -> u64 { 1 }
180fn default_now() -> DateTime<Utc> { Utc::now() }
181
182impl Default for Seen {
183 fn default() -> Self {
184 Self {
185 count: default_seen_count(),
186 first: default_now(),
187 last: default_now()
188 }
189 }
190}
191
192
193#[derive(Debug, Serialize, Deserialize, Described, Clone, Default)]
195#[serde(default)]
196#[metadata_type(ElasticMeta)]
197#[metadata(index=true, store=true)]
198pub struct LabelCategories {
199 pub info: Vec<String>,
201 pub technique: Vec<String>,
203 pub attribution: Vec<String>,
205}
206
207#[derive(Debug, Serialize, Deserialize, Described, Clone)]
209#[metadata_type(ElasticMeta)]
210#[metadata(index=true, store=false)]
211pub struct Comment {
212 pub cid: String,
214 pub uname: String,
216 #[serde(default="Utc::now")]
218 #[metadata(store=true)]
219 pub date: DateTime<Utc>,
220 pub text: Text,
222 #[serde(default)]
224 pub reactions: Vec<Reaction>,
225}
226
227#[derive(Debug, Serialize, Deserialize, Described, Clone)]
229#[metadata_type(ElasticMeta)]
230#[metadata(index=true, store=false)]
231pub struct Reaction {
232 pub icon: ReactionsTypes,
234 pub uname: String,
236}
237
238#[derive(SerializeDisplay, DeserializeFromStr, strum::Display, strum::EnumString, Described, PartialEq, Eq, Debug, Clone, Copy)]
239#[metadata_type(ElasticMeta)]
240#[metadata(mapping="keyword")]
241#[strum(serialize_all = "snake_case")]
242pub enum ReactionsTypes {
243 ThumbsUp,
244 ThumbsDown,
245 Love,
246 Smile,
247 Surprised,
248 Party
249}