Skip to main content

stix_rs/sdos/
threat_actor.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3
4use crate::common::{CommonProperties, StixObject};
5use crate::sdos::BuilderError;
6
7/// Threat Actor SDO
8#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
9#[serde(rename_all = "snake_case")]
10pub struct ThreatActor {
11    #[serde(flatten)]
12    pub common: CommonProperties,
13    pub name: String,
14    pub description: Option<String>,
15    pub threat_actor_types: Option<Vec<String>>,
16}
17
18impl ThreatActor {
19    pub fn builder() -> ThreatActorBuilder {
20        ThreatActorBuilder::default()
21    }
22}
23
24#[derive(Debug, Default)]
25pub struct ThreatActorBuilder {
26    name: Option<String>,
27    description: Option<String>,
28    threat_actor_types: Option<Vec<String>>,
29    created_by_ref: Option<String>,
30}
31
32impl ThreatActorBuilder {
33    pub fn name(mut self, name: impl Into<String>) -> Self {
34        self.name = Some(name.into());
35        self
36    }
37
38    pub fn description(mut self, d: impl Into<String>) -> Self {
39        self.description = Some(d.into());
40        self
41    }
42
43    pub fn threat_actor_types(mut self, t: Vec<String>) -> Self {
44        self.threat_actor_types = Some(t);
45        self
46    }
47
48    pub fn created_by_ref(mut self, r: impl Into<String>) -> Self {
49        self.created_by_ref = Some(r.into());
50        self
51    }
52
53    pub fn build(self) -> Result<ThreatActor, BuilderError> {
54        let name = self.name.ok_or(BuilderError::MissingField("name"))?;
55        let common = CommonProperties::new("threat-actor", self.created_by_ref);
56        Ok(ThreatActor {
57            common,
58            name,
59            description: self.description,
60            threat_actor_types: self.threat_actor_types,
61        })
62    }
63}
64
65impl StixObject for ThreatActor {
66    fn id(&self) -> &str {
67        &self.common.id
68    }
69
70    fn type_(&self) -> &str {
71        &self.common.r#type
72    }
73
74    fn created(&self) -> DateTime<Utc> {
75        self.common.created
76    }
77}
78
79impl From<ThreatActor> for crate::StixObjectEnum {
80    fn from(t: ThreatActor) -> Self {
81        crate::StixObjectEnum::ThreatActor(t)
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88
89    #[test]
90    fn threat_actor_builder() {
91        let ta = ThreatActor::builder()
92            .name("APT28")
93            .description("Advanced persistent threat group")
94            .threat_actor_types(vec!["nation-state".into()])
95            .build()
96            .unwrap();
97
98        assert_eq!(ta.name, "APT28");
99        assert_eq!(ta.common.r#type, "threat-actor");
100    }
101
102    #[test]
103    fn threat_actor_serialize() {
104        let ta = ThreatActor::builder().name("APT28").build().unwrap();
105
106        let json = serde_json::to_string(&ta).unwrap();
107        assert!(json.contains("\"type\":\"threat-actor\""));
108        assert!(json.contains("\"name\":\"APT28\""));
109    }
110}