1use serde::{Deserialize, Serialize};
7
8use crate::common::{ExternalReference, MaecObject};
9use crate::error::{MaecError, Result};
10use crate::objects::types::{FieldData, Name};
11use crate::Capability;
12use chrono::{DateTime, Utc};
13
14#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
34#[serde(rename_all = "snake_case")]
35pub struct MalwareFamily {
36 #[serde(flatten)]
38 pub common: crate::common::CommonProperties,
39
40 pub name: Name,
42
43 #[serde(default, skip_serializing_if = "Vec::is_empty")]
45 pub aliases: Vec<Name>,
46
47 #[serde(default, skip_serializing_if = "Vec::is_empty")]
49 pub labels: Vec<String>,
50
51 #[serde(skip_serializing_if = "Option::is_none")]
53 pub description: Option<String>,
54
55 #[serde(skip_serializing_if = "Option::is_none")]
57 pub field_data: Option<FieldData>,
58
59 #[serde(default, skip_serializing_if = "Vec::is_empty")]
61 pub common_strings: Vec<String>,
62
63 #[serde(default, skip_serializing_if = "Vec::is_empty")]
65 pub common_capabilities: Vec<Capability>,
66
67 #[serde(default, skip_serializing_if = "Vec::is_empty")]
69 pub common_code_refs: Vec<String>,
70
71 #[serde(default, skip_serializing_if = "Vec::is_empty")]
73 pub common_behavior_refs: Vec<String>,
74
75 #[serde(default, skip_serializing_if = "Vec::is_empty")]
77 pub references: Vec<ExternalReference>,
78}
79
80impl MalwareFamily {
81 pub fn builder() -> MalwareFamilyBuilder {
83 MalwareFamilyBuilder::default()
84 }
85
86 pub fn new(name: impl Into<Name>) -> Self {
88 Self {
89 common: crate::common::CommonProperties::new("malware-family", None),
90 name: name.into(),
91 aliases: vec![],
92 labels: vec![],
93 description: None,
94 field_data: None,
95 common_strings: vec![],
96 common_capabilities: vec![],
97 common_code_refs: vec![],
98 common_behavior_refs: vec![],
99 references: vec![],
100 }
101 }
102
103 pub fn validate(&self) -> Result<()> {
105 if self.common.r#type != "malware-family" {
106 return Err(MaecError::ValidationError(format!(
107 "type must be 'malware-family', got '{}'",
108 self.common.r#type
109 )));
110 }
111
112 if !crate::common::is_valid_maec_id(&self.common.id) {
113 return Err(MaecError::InvalidId(self.common.id.clone()));
114 }
115
116 Ok(())
117 }
118}
119
120impl MaecObject for MalwareFamily {
121 fn id(&self) -> &str {
122 &self.common.id
123 }
124
125 fn type_(&self) -> &str {
126 &self.common.r#type
127 }
128
129 fn created(&self) -> DateTime<Utc> {
130 self.common.created
131 }
132}
133
134#[derive(Debug, Default)]
136pub struct MalwareFamilyBuilder {
137 id: Option<String>,
138 name: Option<Name>,
139 aliases: Vec<Name>,
140 labels: Vec<String>,
141 description: Option<String>,
142 field_data: Option<FieldData>,
143 common_strings: Vec<String>,
144 common_capabilities: Vec<Capability>,
145 common_code_refs: Vec<String>,
146 common_behavior_refs: Vec<String>,
147 references: Vec<ExternalReference>,
148}
149
150impl MalwareFamilyBuilder {
151 pub fn id(mut self, id: impl Into<String>) -> Self {
153 self.id = Some(id.into());
154 self
155 }
156
157 pub fn name(mut self, name: impl Into<Name>) -> Self {
159 self.name = Some(name.into());
160 self
161 }
162
163 pub fn add_alias(mut self, alias: impl Into<Name>) -> Self {
165 self.aliases.push(alias.into());
166 self
167 }
168
169 pub fn aliases(mut self, aliases: Vec<Name>) -> Self {
171 self.aliases = aliases;
172 self
173 }
174
175 pub fn add_label(mut self, label: impl Into<String>) -> Self {
177 self.labels.push(label.into());
178 self
179 }
180
181 pub fn labels(mut self, labels: Vec<String>) -> Self {
183 self.labels = labels;
184 self
185 }
186
187 pub fn description(mut self, desc: impl Into<String>) -> Self {
189 self.description = Some(desc.into());
190 self
191 }
192
193 pub fn field_data(mut self, field_data: FieldData) -> Self {
195 self.field_data = Some(field_data);
196 self
197 }
198
199 pub fn add_common_string(mut self, s: impl Into<String>) -> Self {
201 self.common_strings.push(s.into());
202 self
203 }
204
205 pub fn common_strings(mut self, strings: Vec<String>) -> Self {
207 self.common_strings = strings;
208 self
209 }
210
211 pub fn add_capability(mut self, capability: Capability) -> Self {
213 self.common_capabilities.push(capability);
214 self
215 }
216
217 pub fn common_capabilities(mut self, capabilities: Vec<Capability>) -> Self {
219 self.common_capabilities = capabilities;
220 self
221 }
222
223 pub fn add_common_code_ref(mut self, ref_id: impl Into<String>) -> Self {
225 self.common_code_refs.push(ref_id.into());
226 self
227 }
228
229 pub fn add_common_behavior_ref(mut self, ref_id: impl Into<String>) -> Self {
231 self.common_behavior_refs.push(ref_id.into());
232 self
233 }
234
235 pub fn add_reference(mut self, reference: ExternalReference) -> Self {
237 self.references.push(reference);
238 self
239 }
240
241 pub fn references(mut self, references: Vec<ExternalReference>) -> Self {
243 self.references = references;
244 self
245 }
246
247 pub fn build(self) -> Result<MalwareFamily> {
249 let name = self.name.ok_or(MaecError::MissingField("name"))?;
250
251 let mut common = crate::common::CommonProperties::new("malware-family", None);
252 if let Some(id) = self.id {
253 common.id = id;
254 }
255
256 let family = MalwareFamily {
257 common,
258 name,
259 aliases: self.aliases,
260 labels: self.labels,
261 description: self.description,
262 field_data: self.field_data,
263 common_strings: self.common_strings,
264 common_capabilities: self.common_capabilities,
265 common_code_refs: self.common_code_refs,
266 common_behavior_refs: self.common_behavior_refs,
267 references: self.references,
268 };
269
270 family.validate()?;
271 Ok(family)
272 }
273}
274
275#[cfg(test)]
276mod tests {
277 use super::*;
278
279 #[test]
280 fn test_malware_family_new() {
281 let family = MalwareFamily::new("WannaCry");
282 assert_eq!(family.name.value, "WannaCry");
283 assert_eq!(family.common.r#type, "malware-family");
284 assert!(family.common.id.starts_with("malware-family--"));
285 }
286
287 #[test]
288 fn test_malware_family_builder() {
289 let family = MalwareFamily::builder()
290 .name(Name::new("Emotet"))
291 .description("Banking trojan and malware loader")
292 .add_label("trojan")
293 .add_label("banking")
294 .add_alias(Name::new("Geodo"))
295 .build()
296 .unwrap();
297
298 assert_eq!(family.name.value, "Emotet");
299 assert_eq!(family.labels.len(), 2);
300 assert_eq!(family.aliases.len(), 1);
301 assert!(family.description.is_some());
302 }
303
304 #[test]
305 fn test_malware_family_validation() {
306 let family = MalwareFamily::new("Test");
307 assert!(family.validate().is_ok());
308 }
309
310 #[test]
311 fn test_malware_family_serialize() {
312 let family = MalwareFamily::builder()
313 .name(Name::new("TestMalware"))
314 .build()
315 .unwrap();
316
317 let json = serde_json::to_string(&family).unwrap();
318 assert!(json.contains("\"type\":\"malware-family\""));
319 assert!(json.contains("TestMalware"));
320
321 let deserialized: MalwareFamily = serde_json::from_str(&json).unwrap();
322 assert_eq!(family, deserialized);
323 }
324}