1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3
4use crate::common::{CommonProperties, StixObject};
5use crate::vocab::{IdentityClass, IndicatorPatternType};
6fn default_pattern_type() -> IndicatorPatternType { IndicatorPatternType::Stix }
7fn default_valid_from() -> DateTime<Utc> { Utc::now() }
8use crate::pattern::validate_pattern;
9
10pub use crate::sdos::BuilderError;
12
13#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, )]
15#[serde(rename_all = "snake_case")]
16pub struct KillChainPhase {
17 #[serde(rename = "kill_chain_name")]
18 pub name: String,
19
20 #[serde(rename = "phase_name")]
21 pub phase_name: String,
22}
23
24#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, )]
26#[serde(rename_all = "snake_case")]
27pub struct Identity {
28 #[serde(flatten)]
29 pub common: CommonProperties,
30
31 pub name: String,
32
33 pub identity_class: Option<IdentityClass>,
34
35 pub sectors: Option<Vec<String>>,
36}
37
38impl Identity {
39 pub fn builder() -> IdentityBuilder {
40 IdentityBuilder::default()
41 }
42}
43
44#[derive(Debug, Default)]
45pub struct IdentityBuilder {
46 name: Option<String>,
47 identity_class: Option<IdentityClass>,
48 sectors: Option<Vec<String>>,
49 created_by_ref: Option<String>,
50 custom_properties: std::collections::HashMap<String, serde_json::Value>,
51}
52
53impl IdentityBuilder {
54 pub fn name(mut self, name: impl Into<String>) -> Self {
55 self.name = Some(name.into());
56 self
57 }
58
59 pub fn identity_class(mut self, identity_class: IdentityClass) -> Self {
61 self.identity_class = Some(identity_class);
62 self
63 }
64
65 pub fn class(self, identity_class: IdentityClass) -> Self {
67 self.identity_class(identity_class)
68 }
69
70 pub fn sectors(mut self, sectors: Vec<String>) -> Self {
71 self.sectors = Some(sectors);
72 self
73 }
74
75 pub fn property(mut self, key: impl Into<String>, value: impl Into<serde_json::Value>) -> Self {
77 self.custom_properties.insert(key.into(), value.into());
78 self
79 }
80
81 pub fn created_by_ref(mut self, r: impl Into<String>) -> Self {
82 self.created_by_ref = Some(r.into());
83 self
84 }
85
86 pub fn build(mut self) -> Result<Identity, BuilderError> {
87 let name = self.name.ok_or(BuilderError::MissingField("name"))?;
88 let identity_class = self.identity_class;
89
90 let mut common = CommonProperties::new("identity", self.created_by_ref);
91 if !self.custom_properties.is_empty() {
93 common.custom_properties.extend(self.custom_properties.drain());
94 }
95
96 Ok(Identity {
97 common,
98 name,
99 identity_class,
100 sectors: self.sectors,
101 })
102 }
103}
104
105impl StixObject for Identity {
106 fn id(&self) -> &str {
107 &self.common.id
108 }
109
110 fn type_(&self) -> &str {
111 &self.common.r#type
112 }
113
114 fn created(&self) -> DateTime<Utc> {
115 self.common.created
116 }
117}
118
119impl From<Identity> for crate::StixObjectEnum {
121 fn from(i: Identity) -> Self {
122 crate::StixObjectEnum::Identity(i)
123 }
124}
125#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, )]
127#[serde(rename_all = "snake_case")]
128pub struct Malware {
129 #[serde(flatten)]
130 pub common: CommonProperties,
131
132 pub name: String,
133
134 pub description: Option<String>,
135
136 #[serde(default)]
137 pub malware_types: Vec<String>,
138
139 #[serde(default)]
140 pub is_family: bool,
141
142 pub aliases: Option<Vec<String>>,
143
144 pub kill_chain_phases: Option<Vec<KillChainPhase>>,
145
146 pub first_seen: Option<DateTime<Utc>>,
147
148 pub last_seen: Option<DateTime<Utc>>,
149
150 pub operating_system_refs: Option<Vec<String>>,
151
152 pub architecture_execution_envs: Option<Vec<String>>,
153
154 pub implementation_languages: Option<Vec<String>>,
155
156 pub capabilities: Option<Vec<String>>,
157
158 pub sample_refs: Option<Vec<String>>,
159}
160
161impl Malware {
162 pub fn builder() -> MalwareBuilder {
163 MalwareBuilder::default()
164 }
165}
166
167#[derive(Debug, Default)]
168pub struct MalwareBuilder {
169 name: Option<String>,
170 description: Option<String>,
171 is_family: Option<bool>,
172 malware_types: Option<Vec<String>>,
173 aliases: Option<Vec<String>>,
174 kill_chain_phases: Option<Vec<KillChainPhase>>,
175 first_seen: Option<DateTime<Utc>>,
176 last_seen: Option<DateTime<Utc>>,
177 operating_system_refs: Option<Vec<String>>,
178 architecture_execution_envs: Option<Vec<String>>,
179 implementation_languages: Option<Vec<String>>,
180 capabilities: Option<Vec<String>>,
181 sample_refs: Option<Vec<String>>,
182 created_by_ref: Option<String>,
183}
184
185impl MalwareBuilder {
186 pub fn name(mut self, name: impl Into<String>) -> Self {
187 self.name = Some(name.into());
188 self
189 }
190
191 pub fn description(mut self, desc: impl Into<String>) -> Self {
192 self.description = Some(desc.into());
193 self
194 }
195
196 pub fn is_family(mut self, is_family: bool) -> Self {
197 self.is_family = Some(is_family);
198 self
199 }
200
201 pub fn malware_types(mut self, types: Vec<String>) -> Self {
202 self.malware_types = Some(types);
203 self
204 }
205
206 pub fn aliases(mut self, aliases: Vec<String>) -> Self {
207 self.aliases = Some(aliases);
208 self
209 }
210
211 pub fn kill_chain_phases(mut self, phases: Vec<KillChainPhase>) -> Self {
212 self.kill_chain_phases = Some(phases);
213 self
214 }
215
216 pub fn first_seen(mut self, t: DateTime<Utc>) -> Self {
217 self.first_seen = Some(t);
218 self
219 }
220
221 pub fn last_seen(mut self, t: DateTime<Utc>) -> Self {
222 self.last_seen = Some(t);
223 self
224 }
225
226 pub fn operating_system_refs(mut self, refs: Vec<String>) -> Self {
227 self.operating_system_refs = Some(refs);
228 self
229 }
230
231 pub fn architecture_execution_envs(mut self, envs: Vec<String>) -> Self {
232 self.architecture_execution_envs = Some(envs);
233 self
234 }
235
236 pub fn implementation_languages(mut self, langs: Vec<String>) -> Self {
237 self.implementation_languages = Some(langs);
238 self
239 }
240
241 pub fn capabilities(mut self, caps: Vec<String>) -> Self {
242 self.capabilities = Some(caps);
243 self
244 }
245
246 pub fn sample_refs(mut self, refs: Vec<String>) -> Self {
247 self.sample_refs = Some(refs);
248 self
249 }
250
251 pub fn created_by_ref(mut self, r: impl Into<String>) -> Self {
252 self.created_by_ref = Some(r.into());
253 self
254 }
255
256 pub fn build(self) -> Result<Malware, BuilderError> {
257 let name = self.name.ok_or(BuilderError::MissingField("name"))?;
258 let is_family = self.is_family.unwrap_or(false);
259 let malware_types = self.malware_types.unwrap_or_default();
260
261 let common = CommonProperties::new("malware", self.created_by_ref);
262
263 Ok(Malware {
264 common,
265 name,
266 description: self.description,
267 malware_types,
268 is_family,
269 aliases: self.aliases,
270 kill_chain_phases: self.kill_chain_phases,
271 first_seen: self.first_seen,
272 last_seen: self.last_seen,
273 operating_system_refs: self.operating_system_refs,
274 architecture_execution_envs: self.architecture_execution_envs,
275 implementation_languages: self.implementation_languages,
276 capabilities: self.capabilities,
277 sample_refs: self.sample_refs,
278 })
279 }
280}
281
282impl StixObject for Malware {
283 fn id(&self) -> &str {
284 &self.common.id
285 }
286
287 fn type_(&self) -> &str {
288 &self.common.r#type
289 }
290
291 fn created(&self) -> DateTime<Utc> {
292 self.common.created
293 }
294}
295
296#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, )]
298#[serde(rename_all = "snake_case")]
299pub struct Indicator {
300 #[serde(flatten)]
301 pub common: CommonProperties,
302
303 pub name: Option<String>,
304
305 pub description: Option<String>,
306
307 pub indicator_types: Option<Vec<String>>,
308
309 pub pattern: String,
310
311 #[serde(default = "default_pattern_type")]
312 pub pattern_type: IndicatorPatternType,
313
314 pub pattern_version: Option<String>,
315
316 #[serde(default = "default_valid_from")]
317 pub valid_from: DateTime<Utc>,
318
319 pub valid_until: Option<DateTime<Utc>>,
320
321 pub kill_chain_phases: Option<Vec<KillChainPhase>>,
322}
323
324impl Indicator {
325 pub fn builder() -> IndicatorBuilder {
326 IndicatorBuilder::default()
327 }
328
329 pub fn validate_pattern(&self) -> Result<(), crate::pattern::PatternError> {
331 if self.pattern_type == IndicatorPatternType::Stix {
332 validate_pattern(&self.pattern)
333 } else {
334 Ok(())
336 }
337 }
338}
339
340#[derive(Debug, Default)]
341pub struct IndicatorBuilder {
342 name: Option<String>,
343 description: Option<String>,
344 indicator_types: Option<Vec<String>>,
345 pattern: Option<String>,
346 pattern_type: Option<IndicatorPatternType>,
347 pattern_version: Option<String>,
348 valid_from: Option<DateTime<Utc>>,
349 valid_until: Option<DateTime<Utc>>,
350 kill_chain_phases: Option<Vec<KillChainPhase>>,
351 created_by_ref: Option<String>,
352 validate_pattern: bool,
353}
354
355impl IndicatorBuilder {
356 pub fn name(mut self, name: impl Into<String>) -> Self {
357 self.name = Some(name.into());
358 self
359 }
360
361 pub fn description(mut self, desc: impl Into<String>) -> Self {
362 self.description = Some(desc.into());
363 self
364 }
365
366 pub fn indicator_types(mut self, types: Vec<String>) -> Self {
367 self.indicator_types = Some(types);
368 self
369 }
370
371 pub fn pattern(mut self, pattern: impl Into<String>) -> Self {
372 self.pattern = Some(pattern.into());
373 self
374 }
375
376 pub fn pattern_type(mut self, pt: IndicatorPatternType) -> Self {
377 self.pattern_type = Some(pt);
378 self
379 }
380
381 pub fn pattern_version(mut self, version: impl Into<String>) -> Self {
382 self.pattern_version = Some(version.into());
383 self
384 }
385
386 pub fn valid_from(mut self, t: DateTime<Utc>) -> Self {
387 self.valid_from = Some(t);
388 self
389 }
390
391 pub fn valid_until(mut self, t: DateTime<Utc>) -> Self {
392 self.valid_until = Some(t);
393 self
394 }
395
396 pub fn kill_chain_phases(mut self, phases: Vec<KillChainPhase>) -> Self {
397 self.kill_chain_phases = Some(phases);
398 self
399 }
400
401 pub fn created_by_ref(mut self, r: impl Into<String>) -> Self {
402 self.created_by_ref = Some(r.into());
403 self
404 }
405
406 pub fn validate_pattern(mut self, validate: bool) -> Self {
408 self.validate_pattern = validate;
409 self
410 }
411
412 pub fn build(self) -> Result<Indicator, BuilderError> {
413 let pattern = self.pattern.ok_or(BuilderError::MissingField("pattern"))?;
414 let pattern_type = self.pattern_type.ok_or(BuilderError::MissingField("pattern_type"))?;
415 let valid_from = self.valid_from.ok_or(BuilderError::MissingField("valid_from"))?;
416
417 if self.validate_pattern && pattern_type == IndicatorPatternType::Stix {
419 validate_pattern(&pattern)
420 .map_err(|_| BuilderError::MissingField("invalid pattern"))?;
421 }
422
423 let common = CommonProperties::new("indicator", self.created_by_ref);
424
425 Ok(Indicator {
426 common,
427 name: self.name,
428 description: self.description,
429 indicator_types: self.indicator_types,
430 pattern,
431 pattern_type,
432 pattern_version: self.pattern_version,
433 valid_from,
434 valid_until: self.valid_until,
435 kill_chain_phases: self.kill_chain_phases,
436 })
437 }
438}
439
440impl StixObject for Indicator {
441 fn id(&self) -> &str {
442 &self.common.id
443 }
444
445 fn type_(&self) -> &str {
446 &self.common.r#type
447 }
448
449 fn created(&self) -> DateTime<Utc> {
450 self.common.created
451 }
452}
453
454impl From<Indicator> for crate::StixObjectEnum {
455 fn from(i: Indicator) -> Self {
456 crate::StixObjectEnum::Indicator(i)
457 }
458}
459
460impl From<Malware> for crate::StixObjectEnum {
461 fn from(m: Malware) -> Self {
462 crate::StixObjectEnum::Malware(m)
463 }
464}
465
466#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, )]
468#[serde(rename_all = "snake_case")]
469pub struct Sighting {
470 #[serde(flatten)]
471 pub common: CommonProperties,
472
473 pub count: u32,
474
475 pub sighting_of_ref: String,
476
477 pub where_sighted_refs: Vec<String>,
478}
479
480impl Sighting {
481 pub fn builder() -> SightingBuilder {
482 SightingBuilder::default()
483 }
484}
485
486#[derive(Debug, Default)]
487pub struct SightingBuilder {
488 count: Option<u32>,
489 sighting_of_ref: Option<String>,
490 where_sighted_refs: Option<Vec<String>>,
491 created_by_ref: Option<String>,
492}
493
494impl SightingBuilder {
495 pub fn count(mut self, count: u32) -> Self {
496 self.count = Some(count);
497 self
498 }
499
500 pub fn sighting_of_ref(mut self, r: impl Into<String>) -> Self {
501 self.sighting_of_ref = Some(r.into());
502 self
503 }
504
505 pub fn where_sighted_refs(mut self, refs: Vec<String>) -> Self {
506 self.where_sighted_refs = Some(refs);
507 self
508 }
509
510 pub fn created_by_ref(mut self, r: impl Into<String>) -> Self {
511 self.created_by_ref = Some(r.into());
512 self
513 }
514
515 pub fn build(self) -> Result<Sighting, BuilderError> {
516 let count = self.count.ok_or(BuilderError::MissingField("count"))?;
517 let sighting_of_ref = self.sighting_of_ref.ok_or(BuilderError::MissingField("sighting_of_ref"))?;
518 let where_sighted_refs = self.where_sighted_refs.ok_or(BuilderError::MissingField("where_sighted_refs"))?;
519
520 let common = CommonProperties::new("sighting", self.created_by_ref);
521
522 Ok(Sighting {
523 common,
524 count,
525 sighting_of_ref,
526 where_sighted_refs,
527 })
528 }
529}
530
531impl StixObject for Sighting {
532 fn id(&self) -> &str {
533 &self.common.id
534 }
535
536 fn type_(&self) -> &str {
537 &self.common.r#type
538 }
539
540 fn created(&self) -> DateTime<Utc> {
541 self.common.created
542 }
543}
544
545impl From<Sighting> for crate::StixObjectEnum {
546 fn from(s: Sighting) -> Self {
547 crate::StixObjectEnum::Sighting(s)
548 }
549}
550
551#[cfg(test)]
552mod tests {
553 use super::*;
554 use crate::vocab::{IdentityClass, IndicatorPatternType};
555 use serde_json::Value;
556
557 #[test]
558 fn identity_builder_and_serialize() {
559 let idty = Identity::builder()
560 .name("ACME")
561 .class(IdentityClass::Organization)
562 .sectors(vec!["technology".into()])
563 .build()
564 .unwrap();
565
566 let s = serde_json::to_string(&idty).unwrap();
567 let v: Value = serde_json::from_str(&s).unwrap();
568 assert_eq!(v.get("type").and_then(Value::as_str).unwrap(), "identity");
569 assert_eq!(v.get("identity_class").and_then(Value::as_str).unwrap(), "organization");
570 let id_field = v.get("id").and_then(Value::as_str).unwrap();
571 assert!(id_field.starts_with("identity--"));
572 }
573
574 #[test]
575 fn malware_builder_and_serialize() {
576 let mw = Malware::builder()
577 .name("BadWare")
578 .is_family(true)
579 .malware_types(vec!["ransomware".into()])
580 .build()
581 .unwrap();
582
583 let s = serde_json::to_string(&mw).unwrap();
584 let v: Value = serde_json::from_str(&s).unwrap();
585 assert_eq!(v.get("type").and_then(Value::as_str).unwrap(), "malware");
586 assert_eq!(v.get("is_family").and_then(Value::as_bool).unwrap(), true);
587 }
588
589 #[test]
590 fn indicator_builder_and_serialize() {
591 let ind = Indicator::builder()
592 .name("Test")
593 .pattern("[file:hashes.'SHA-256' = '...']")
594 .pattern_type(IndicatorPatternType::Stix)
595 .valid_from(Utc::now())
596 .build()
597 .unwrap();
598
599 let s = serde_json::to_string(&ind).unwrap();
600 let v: Value = serde_json::from_str(&s).unwrap();
601 assert_eq!(v.get("type").and_then(Value::as_str).unwrap(), "indicator");
602 assert_eq!(v.get("pattern_type").and_then(Value::as_str).unwrap(), "stix");
603 }
604
605 #[test]
606 fn sighting_builder_and_serialize() {
607 let s = Sighting::builder()
608 .count(2)
609 .sighting_of_ref("malware--1111")
610 .where_sighted_refs(vec!["sensor--1".into()])
611 .build()
612 .unwrap();
613
614 let j = serde_json::to_string(&s).unwrap();
615 let v: Value = serde_json::from_str(&j).unwrap();
616 assert_eq!(v.get("type").and_then(Value::as_str).unwrap(), "sighting");
617 assert_eq!(v.get("sighting_of_ref").and_then(Value::as_str).unwrap(), "malware--1111");
618 }
619
620 #[test]
621 fn missing_required_field_errors() {
622 let r = Identity::builder().name("No Class").build();
624 assert!(r.is_ok());
625
626 let r2 = Malware::builder().is_family(false).build();
628 assert!(r2.is_err());
629 }
630}