Skip to main content

stix_rs/sdos/
infrastructure.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3
4use crate::common::{CommonProperties, StixObject};
5use crate::sdos::BuilderError;
6
7/// Infrastructure SDO
8#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
9#[serde(rename_all = "snake_case")]
10pub struct Infrastructure {
11    #[serde(flatten)]
12    pub common: CommonProperties,
13    pub name: String,
14    pub description: Option<String>,
15    pub infrastructure_types: Option<Vec<String>>,
16}
17
18impl Infrastructure {
19    pub fn builder() -> InfrastructureBuilder {
20        InfrastructureBuilder::default()
21    }
22}
23
24#[derive(Debug, Default)]
25pub struct InfrastructureBuilder {
26    name: Option<String>,
27    description: Option<String>,
28    infrastructure_types: Option<Vec<String>>,
29    created_by_ref: Option<String>,
30}
31
32impl InfrastructureBuilder {
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 infrastructure_types(mut self, t: Vec<String>) -> Self {
44        self.infrastructure_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<Infrastructure, BuilderError> {
54        let name = self.name.ok_or(BuilderError::MissingField("name"))?;
55        let common = CommonProperties::new("infrastructure", self.created_by_ref);
56        Ok(Infrastructure {
57            common,
58            name,
59            description: self.description,
60            infrastructure_types: self.infrastructure_types,
61        })
62    }
63}
64
65impl StixObject for Infrastructure {
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<Infrastructure> for crate::StixObjectEnum {
80    fn from(i: Infrastructure) -> Self {
81        crate::StixObjectEnum::Infrastructure(i)
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88
89    #[test]
90    fn infrastructure_builder() {
91        let infra = Infrastructure::builder()
92            .name("C2 Server")
93            .description("Command and control infrastructure")
94            .infrastructure_types(vec!["command-and-control".into()])
95            .build()
96            .unwrap();
97
98        assert_eq!(infra.name, "C2 Server");
99        assert_eq!(infra.common.r#type, "infrastructure");
100    }
101
102    #[test]
103    fn infrastructure_serialize() {
104        let infra = Infrastructure::builder()
105            .name("Malicious Domain")
106            .build()
107            .unwrap();
108
109        let json = serde_json::to_string(&infra).unwrap();
110        assert!(json.contains("\"type\":\"infrastructure\""));
111        assert!(json.contains("\"name\":\"Malicious Domain\""));
112    }
113}