1use chrono::NaiveDate;
4use serde::{Deserialize, Deserializer, Serialize};
5
6fn flexible_bool_required<'de, D>(deserializer: D) -> Result<bool, D::Error>
7where
8 D: Deserializer<'de>,
9{
10 #[derive(Deserialize)]
11 #[serde(untagged)]
12 enum FlexBool {
13 Bool(bool),
14 String(String),
15 Int(i64),
16 }
17
18 match FlexBool::deserialize(deserializer)? {
19 FlexBool::Bool(b) => Ok(b),
20 FlexBool::String(s) => Ok(s == "1" || s.eq_ignore_ascii_case("true")),
21 FlexBool::Int(i) => Ok(i != 0),
22 }
23}
24
25fn string_to_option_bool<'de, D>(deserializer: D) -> Result<Option<bool>, D::Error>
26where
27 D: Deserializer<'de>,
28{
29 let opt: Option<String> = Deserialize::deserialize(deserializer)?;
30 Ok(opt.map(|s| s == "1" || s.eq_ignore_ascii_case("true")))
31}
32
33fn flexible_bool<'de, D>(deserializer: D) -> Result<Option<bool>, D::Error>
34where
35 D: Deserializer<'de>,
36{
37 #[derive(Deserialize)]
38 #[serde(untagged)]
39 enum FlexBool {
40 Bool(bool),
41 String(String),
42 Int(i64),
43 }
44
45 let opt: Option<FlexBool> = Deserialize::deserialize(deserializer)?;
46 Ok(opt.map(|v| match v {
47 FlexBool::Bool(b) => b,
48 FlexBool::String(s) => s == "1" || s.eq_ignore_ascii_case("true"),
49 FlexBool::Int(i) => i != 0,
50 }))
51}
52
53fn string_or_int<'de, D>(deserializer: D) -> Result<String, D::Error>
54where
55 D: Deserializer<'de>,
56{
57 #[derive(Deserialize)]
58 #[serde(untagged)]
59 enum StringOrInt {
60 String(String),
61 Int(i64),
62 }
63
64 match StringOrInt::deserialize(deserializer)? {
65 StringOrInt::String(s) => Ok(s),
66 StringOrInt::Int(i) => Ok(i.to_string()),
67 }
68}
69
70#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
71#[serde(from = "StringOrU8")]
72pub enum ThreatLevel {
73 High,
74 Medium,
75 Low,
76 Undefined,
77}
78
79#[derive(Deserialize)]
80#[serde(untagged)]
81enum StringOrU8 {
82 String(String),
83 Int(u8),
84}
85
86impl From<StringOrU8> for ThreatLevel {
87 fn from(v: StringOrU8) -> Self {
88 let n = match v {
89 StringOrU8::String(s) => s.parse().unwrap_or(4),
90 StringOrU8::Int(n) => n,
91 };
92 match n {
93 1 => ThreatLevel::High,
94 2 => ThreatLevel::Medium,
95 3 => ThreatLevel::Low,
96 _ => ThreatLevel::Undefined,
97 }
98 }
99}
100
101#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
102#[serde(from = "StringOrU8")]
103pub enum AnalysisLevel {
104 Initial,
105 Ongoing,
106 Complete,
107}
108
109impl From<StringOrU8> for AnalysisLevel {
110 fn from(v: StringOrU8) -> Self {
111 let n = match v {
112 StringOrU8::String(s) => s.parse().unwrap_or(0),
113 StringOrU8::Int(n) => n,
114 };
115 match n {
116 0 => AnalysisLevel::Initial,
117 1 => AnalysisLevel::Ongoing,
118 _ => AnalysisLevel::Complete,
119 }
120 }
121}
122
123#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
124#[serde(from = "StringOrU8")]
125pub enum Distribution {
126 Organisation,
127 Community,
128 Connected,
129 All,
130 SharingGroup,
131 Inherit,
132}
133
134impl From<StringOrU8> for Distribution {
135 fn from(v: StringOrU8) -> Self {
136 let n = match v {
137 StringOrU8::String(s) => s.parse().unwrap_or(0),
138 StringOrU8::Int(n) => n,
139 };
140 match n {
141 0 => Distribution::Organisation,
142 1 => Distribution::Community,
143 2 => Distribution::Connected,
144 3 => Distribution::All,
145 4 => Distribution::SharingGroup,
146 _ => Distribution::Inherit,
147 }
148 }
149}
150
151#[derive(Debug, Clone, Serialize, Deserialize)]
152pub struct Event {
153 #[serde(deserialize_with = "string_or_int")]
154 pub id: String,
155 pub uuid: String,
156 pub info: String,
157 #[serde(default, deserialize_with = "string_or_int")]
158 pub org_id: String,
159 #[serde(default, deserialize_with = "string_or_int")]
160 pub orgc_id: String,
161 #[serde(default)]
162 pub date: Option<NaiveDate>,
163 pub threat_level_id: ThreatLevel,
164 pub analysis: AnalysisLevel,
165 pub distribution: Distribution,
166 #[serde(deserialize_with = "flexible_bool_required")]
167 pub published: bool,
168 #[serde(default)]
169 pub timestamp: Option<String>,
170 #[serde(default)]
171 pub publish_timestamp: Option<String>,
172 #[serde(default, rename = "Attribute")]
173 pub attributes: Vec<Attribute>,
174 #[serde(default, rename = "Object")]
175 pub objects: Vec<MispObject>,
176 #[serde(default, rename = "Tag")]
177 pub tags: Vec<Tag>,
178 #[serde(default, rename = "Galaxy")]
179 pub galaxies: Vec<Galaxy>,
180 #[serde(default, rename = "Org")]
181 pub org: Option<Organisation>,
182 #[serde(default, rename = "Orgc")]
183 pub orgc: Option<Organisation>,
184 #[serde(default)]
185 pub attribute_count: Option<String>,
186}
187
188#[derive(Debug, Clone, Serialize, Deserialize)]
189pub struct Attribute {
190 #[serde(deserialize_with = "string_or_int")]
191 pub id: String,
192 pub uuid: String,
193 #[serde(deserialize_with = "string_or_int")]
194 pub event_id: String,
195 pub category: String,
196 #[serde(rename = "type")]
197 pub attr_type: String,
198 pub value: String,
199 #[serde(deserialize_with = "flexible_bool_required")]
200 pub to_ids: bool,
201 pub distribution: Distribution,
202 #[serde(default)]
203 pub comment: Option<String>,
204 #[serde(default)]
205 pub timestamp: Option<String>,
206 #[serde(default, rename = "Tag")]
207 pub tags: Vec<Tag>,
208 #[serde(default, rename = "Galaxy")]
209 pub galaxies: Vec<Galaxy>,
210 #[serde(default, rename = "Sighting")]
211 pub sightings: Vec<Sighting>,
212 #[serde(default)]
213 pub first_seen: Option<String>,
214 #[serde(default)]
215 pub last_seen: Option<String>,
216 #[serde(default, deserialize_with = "flexible_bool")]
217 pub deleted: Option<bool>,
218}
219
220#[derive(Debug, Clone, Serialize, Deserialize)]
221pub struct MispObject {
222 #[serde(deserialize_with = "string_or_int")]
223 pub id: String,
224 pub uuid: String,
225 pub name: String,
226 #[serde(default)]
227 pub description: Option<String>,
228 #[serde(default)]
229 pub template_uuid: Option<String>,
230 #[serde(default)]
231 pub template_version: Option<String>,
232 #[serde(deserialize_with = "string_or_int")]
233 pub event_id: String,
234 pub distribution: Distribution,
235 #[serde(default, rename = "Attribute")]
236 pub attributes: Vec<Attribute>,
237 #[serde(default, rename = "ObjectReference")]
238 pub references: Vec<ObjectReference>,
239}
240
241#[derive(Debug, Clone, Serialize, Deserialize)]
242pub struct ObjectReference {
243 #[serde(deserialize_with = "string_or_int")]
244 pub id: String,
245 pub uuid: String,
246 #[serde(deserialize_with = "string_or_int")]
247 pub object_id: String,
248 #[serde(deserialize_with = "string_or_int")]
249 pub referenced_id: String,
250 #[serde(default)]
251 pub referenced_uuid: Option<String>,
252 #[serde(default)]
253 pub referenced_type: Option<String>,
254 #[serde(default)]
255 pub relationship_type: Option<String>,
256 #[serde(default)]
257 pub comment: Option<String>,
258}
259
260#[derive(Debug, Clone, Serialize, Deserialize)]
261pub struct Tag {
262 #[serde(deserialize_with = "string_or_int")]
263 pub id: String,
264 pub name: String,
265 #[serde(default)]
266 pub colour: Option<String>,
267 #[serde(default, deserialize_with = "string_to_option_bool")]
268 pub exportable: Option<bool>,
269 #[serde(default)]
270 pub numerical_value: Option<String>,
271}
272
273#[derive(Debug, Clone, Serialize, Deserialize)]
274pub struct Galaxy {
275 #[serde(deserialize_with = "string_or_int")]
276 pub id: String,
277 pub uuid: String,
278 pub name: String,
279 #[serde(rename = "type")]
280 pub galaxy_type: String,
281 #[serde(default)]
282 pub description: Option<String>,
283 #[serde(default)]
284 pub version: Option<String>,
285 #[serde(default, rename = "GalaxyCluster")]
286 pub clusters: Vec<GalaxyCluster>,
287}
288
289#[derive(Debug, Clone, Serialize, Deserialize)]
290pub struct GalaxyCluster {
291 #[serde(deserialize_with = "string_or_int")]
292 pub id: String,
293 pub uuid: String,
294 pub value: String,
295 #[serde(default)]
296 pub description: Option<String>,
297 #[serde(default)]
298 pub source: Option<String>,
299 #[serde(default)]
300 pub authors: Vec<String>,
301 #[serde(default)]
302 pub version: Option<String>,
303 #[serde(default, rename = "GalaxyElement")]
304 pub elements: Vec<GalaxyElement>,
305 #[serde(default)]
306 pub meta: Option<serde_json::Value>,
307 #[serde(default)]
308 pub tag_name: Option<String>,
309}
310
311#[derive(Debug, Clone, Serialize, Deserialize)]
312pub struct GalaxyElement {
313 #[serde(deserialize_with = "string_or_int")]
314 pub id: String,
315 pub key: String,
316 pub value: String,
317}
318
319#[derive(Debug, Clone, Serialize, Deserialize)]
320pub struct Sighting {
321 #[serde(deserialize_with = "string_or_int")]
322 pub id: String,
323 #[serde(deserialize_with = "string_or_int")]
324 pub attribute_id: String,
325 #[serde(deserialize_with = "string_or_int")]
326 pub event_id: String,
327 #[serde(default)]
328 pub org_id: Option<String>,
329 #[serde(default)]
330 pub date_sighting: Option<String>,
331 #[serde(default)]
332 pub source: Option<String>,
333 #[serde(default, rename = "type")]
334 pub sighting_type: Option<String>,
335 #[serde(default)]
336 pub uuid: Option<String>,
337 #[serde(default, rename = "Organisation")]
338 pub organisation: Option<Organisation>,
339}
340
341#[derive(Debug, Clone, Serialize, Deserialize)]
342pub struct Organisation {
343 #[serde(deserialize_with = "string_or_int")]
344 pub id: String,
345 pub name: String,
346 #[serde(default)]
347 pub uuid: Option<String>,
348}
349
350#[derive(Debug, Clone, Serialize, Deserialize)]
351pub struct Warninglist {
352 #[serde(deserialize_with = "string_or_int")]
353 pub id: String,
354 pub name: String,
355 #[serde(rename = "type")]
356 pub list_type: String,
357 #[serde(default)]
358 pub description: Option<String>,
359 #[serde(default)]
360 pub version: Option<String>,
361 #[serde(default, deserialize_with = "flexible_bool")]
362 pub enabled: Option<bool>,
363 #[serde(default)]
364 pub category: Option<String>,
365}
366
367#[derive(Debug, Clone, Serialize, Deserialize)]
368pub struct WarninglistCheckResult {
369 #[serde(default)]
370 pub value: String,
371 #[serde(default)]
372 pub matched: bool,
373 #[serde(default)]
374 pub warninglists: Vec<WarninglistMatch>,
375}
376
377#[derive(Debug, Clone, Serialize, Deserialize)]
378pub struct WarninglistMatch {
379 #[serde(deserialize_with = "string_or_int")]
380 pub id: String,
381 pub name: String,
382 #[serde(default)]
383 pub matched: Option<String>,
384}