misp_client/
search.rs

1//! High-level search builders for common threat hunting patterns.
2
3use crate::attributes::{AttributeSearchQuery, AttributesClient};
4use crate::error::MispError;
5use crate::events::{EventSearchQuery, EventsClient};
6use crate::models::{Attribute, Event};
7
8pub struct SearchBuilder<'a> {
9    events_client: &'a EventsClient,
10    attributes_client: &'a AttributesClient,
11}
12
13impl<'a> SearchBuilder<'a> {
14    pub fn new(events: &'a EventsClient, attributes: &'a AttributesClient) -> Self {
15        Self {
16            events_client: events,
17            attributes_client: attributes,
18        }
19    }
20
21    pub fn ioc(&self, value: &str) -> IocSearch<'a> {
22        IocSearch {
23            attributes_client: self.attributes_client,
24            value: value.to_string(),
25            attr_types: None,
26            tags: None,
27            not_tags: None,
28            from: None,
29            to: None,
30            to_ids_only: false,
31            include_sightings: false,
32            include_galaxies: false,
33            limit: None,
34        }
35    }
36
37    pub fn by_tag(&self, tag: &str) -> TagSearch<'a> {
38        TagSearch {
39            events_client: self.events_client,
40            attributes_client: self.attributes_client,
41            tags: vec![tag.to_string()],
42            not_tags: Vec::new(),
43            from: None,
44            to: None,
45            published_only: false,
46            limit: None,
47        }
48    }
49
50    pub fn recent(&self, duration: &str) -> RecentSearch<'a> {
51        RecentSearch {
52            events_client: self.events_client,
53            attributes_client: self.attributes_client,
54            duration: duration.to_string(),
55            tags: None,
56            attr_types: None,
57            to_ids_only: false,
58            limit: None,
59        }
60    }
61
62    pub fn threat_actor(&self, name: &str) -> ThreatActorSearch<'a> {
63        ThreatActorSearch {
64            events_client: self.events_client,
65            actor_name: name.to_string(),
66            include_attributes: true,
67            limit: None,
68        }
69    }
70}
71
72pub struct IocSearch<'a> {
73    attributes_client: &'a AttributesClient,
74    value: String,
75    attr_types: Option<Vec<String>>,
76    tags: Option<Vec<String>>,
77    not_tags: Option<Vec<String>>,
78    from: Option<String>,
79    to: Option<String>,
80    to_ids_only: bool,
81    include_sightings: bool,
82    include_galaxies: bool,
83    limit: Option<u32>,
84}
85
86impl<'a> IocSearch<'a> {
87    pub fn types(mut self, types: Vec<&str>) -> Self {
88        self.attr_types = Some(types.into_iter().map(String::from).collect());
89        self
90    }
91
92    pub fn tags(mut self, tags: Vec<&str>) -> Self {
93        self.tags = Some(tags.into_iter().map(String::from).collect());
94        self
95    }
96
97    pub fn exclude_tags(mut self, tags: Vec<&str>) -> Self {
98        self.not_tags = Some(tags.into_iter().map(String::from).collect());
99        self
100    }
101
102    pub fn from_date(mut self, from: &str) -> Self {
103        self.from = Some(from.to_string());
104        self
105    }
106
107    pub fn to_date(mut self, to: &str) -> Self {
108        self.to = Some(to.to_string());
109        self
110    }
111
112    pub fn to_ids_only(mut self) -> Self {
113        self.to_ids_only = true;
114        self
115    }
116
117    pub fn with_sightings(mut self) -> Self {
118        self.include_sightings = true;
119        self
120    }
121
122    pub fn with_galaxies(mut self) -> Self {
123        self.include_galaxies = true;
124        self
125    }
126
127    pub fn limit(mut self, limit: u32) -> Self {
128        self.limit = Some(limit);
129        self
130    }
131
132    pub async fn execute(self) -> Result<Vec<Attribute>, MispError> {
133        let mut query = AttributeSearchQuery::new().value(&self.value);
134
135        if let Some(tags) = self.tags {
136            query = query.tags(tags);
137        }
138        if let Some(not_tags) = self.not_tags {
139            query = query.exclude_tags(not_tags);
140        }
141        if let Some(from) = self.from {
142            query = query.from_date(from);
143        }
144        if let Some(to) = self.to {
145            query = query.to_date(to);
146        }
147        if self.to_ids_only {
148            query = query.to_ids(true);
149        }
150        if self.include_sightings {
151            query = query.include_sightings();
152        }
153        if self.include_galaxies {
154            query = query.include_galaxy();
155        }
156        if let Some(limit) = self.limit {
157            query = query.limit(limit);
158        }
159
160        let mut results = self.attributes_client.search(query).await?;
161
162        if let Some(ref types) = self.attr_types {
163            results.retain(|a| types.contains(&a.attr_type));
164        }
165
166        Ok(results)
167    }
168}
169
170pub struct TagSearch<'a> {
171    events_client: &'a EventsClient,
172    attributes_client: &'a AttributesClient,
173    tags: Vec<String>,
174    not_tags: Vec<String>,
175    from: Option<String>,
176    to: Option<String>,
177    published_only: bool,
178    limit: Option<u32>,
179}
180
181impl<'a> TagSearch<'a> {
182    pub fn and_tag(mut self, tag: &str) -> Self {
183        self.tags.push(tag.to_string());
184        self
185    }
186
187    pub fn exclude_tag(mut self, tag: &str) -> Self {
188        self.not_tags.push(tag.to_string());
189        self
190    }
191
192    pub fn from_date(mut self, from: &str) -> Self {
193        self.from = Some(from.to_string());
194        self
195    }
196
197    pub fn to_date(mut self, to: &str) -> Self {
198        self.to = Some(to.to_string());
199        self
200    }
201
202    pub fn published_only(mut self) -> Self {
203        self.published_only = true;
204        self
205    }
206
207    pub fn limit(mut self, limit: u32) -> Self {
208        self.limit = Some(limit);
209        self
210    }
211
212    pub async fn events(self) -> Result<Vec<Event>, MispError> {
213        let mut query = EventSearchQuery::new().tags(self.tags.clone());
214
215        if !self.not_tags.is_empty() {
216            query = query.exclude_tags(self.not_tags.clone());
217        }
218        if let Some(from) = self.from.clone() {
219            query = query.from_date(from);
220        }
221        if let Some(to) = self.to.clone() {
222            query = query.to_date(to);
223        }
224        if self.published_only {
225            query = query.published(true);
226        }
227        if let Some(limit) = self.limit {
228            query = query.limit(limit);
229        }
230
231        self.events_client.search(query).await
232    }
233
234    pub async fn attributes(self) -> Result<Vec<Attribute>, MispError> {
235        let mut query = AttributeSearchQuery::new().tags(self.tags.clone());
236
237        if !self.not_tags.is_empty() {
238            query = query.exclude_tags(self.not_tags.clone());
239        }
240        if let Some(from) = self.from.clone() {
241            query = query.from_date(from);
242        }
243        if let Some(to) = self.to.clone() {
244            query = query.to_date(to);
245        }
246        if self.published_only {
247            query = query.published(true);
248        }
249        if let Some(limit) = self.limit {
250            query = query.limit(limit);
251        }
252
253        self.attributes_client.search(query).await
254    }
255}
256
257pub struct RecentSearch<'a> {
258    events_client: &'a EventsClient,
259    attributes_client: &'a AttributesClient,
260    duration: String,
261    tags: Option<Vec<String>>,
262    attr_types: Option<Vec<String>>,
263    to_ids_only: bool,
264    limit: Option<u32>,
265}
266
267impl<'a> RecentSearch<'a> {
268    pub fn tags(mut self, tags: Vec<&str>) -> Self {
269        self.tags = Some(tags.into_iter().map(String::from).collect());
270        self
271    }
272
273    pub fn types(mut self, types: Vec<&str>) -> Self {
274        self.attr_types = Some(types.into_iter().map(String::from).collect());
275        self
276    }
277
278    pub fn to_ids_only(mut self) -> Self {
279        self.to_ids_only = true;
280        self
281    }
282
283    pub fn limit(mut self, limit: u32) -> Self {
284        self.limit = Some(limit);
285        self
286    }
287
288    pub async fn events(self) -> Result<Vec<Event>, MispError> {
289        let mut query = EventSearchQuery::new().last(&self.duration);
290
291        if let Some(tags) = self.tags.clone() {
292            query = query.tags(tags);
293        }
294        if let Some(limit) = self.limit {
295            query = query.limit(limit);
296        }
297
298        self.events_client.search(query).await
299    }
300
301    pub async fn attributes(self) -> Result<Vec<Attribute>, MispError> {
302        let mut query = AttributeSearchQuery::new().last(&self.duration);
303
304        if let Some(tags) = self.tags.clone() {
305            query = query.tags(tags);
306        }
307        if self.to_ids_only {
308            query = query.to_ids(true);
309        }
310        if let Some(limit) = self.limit {
311            query = query.limit(limit);
312        }
313
314        let mut results = self.attributes_client.search(query).await?;
315
316        if let Some(ref types) = self.attr_types {
317            results.retain(|a| types.contains(&a.attr_type));
318        }
319
320        Ok(results)
321    }
322}
323
324pub struct ThreatActorSearch<'a> {
325    events_client: &'a EventsClient,
326    actor_name: String,
327    include_attributes: bool,
328    limit: Option<u32>,
329}
330
331impl<'a> ThreatActorSearch<'a> {
332    pub fn without_attributes(mut self) -> Self {
333        self.include_attributes = false;
334        self
335    }
336
337    pub fn limit(mut self, limit: u32) -> Self {
338        self.limit = Some(limit);
339        self
340    }
341
342    pub async fn execute(self) -> Result<Vec<Event>, MispError> {
343        let tag = format!("misp-galaxy:threat-actor=\"{}\"", self.actor_name);
344        let mut query = EventSearchQuery::new().tags(vec![tag]);
345
346        if self.include_attributes {
347            query = query.include_attributes();
348        }
349        if let Some(limit) = self.limit {
350            query = query.limit(limit);
351        }
352
353        self.events_client.search(query).await
354    }
355}