1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3
4use crate::common::{CommonProperties, StixObject};
5use crate::sdos::BuilderError;
6
7#[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}