Skip to main content

stix_rs/sdos/
tool.rs

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