misp_client/
models.rs

1//! Data models for MISP entities (events, attributes, galaxies, etc).
2
3use 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}