Skip to main content

attack/domain/
campaign.rs

1use serde::{Deserialize, Serialize};
2use stix_rs::{CommonProperties, StixObject};
3use crate::domain::AttackObject;
4use chrono::{DateTime, Utc};
5
6/// Represents a MITRE ATT&CK Campaign.
7///
8/// Campaigns represent groupings of adversarial behaviors that describe a set of
9/// malicious activities or attacks that occur over a period of time against a
10/// specific set of targets.
11#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
12pub struct Campaign {
13    #[serde(flatten)]
14    pub common: CommonProperties,
15
16    pub name: String,
17    pub description: Option<String>,
18
19    #[serde(default)]
20    pub aliases: Vec<String>,
21
22    pub first_seen: Option<DateTime<Utc>>,
23    pub last_seen: Option<DateTime<Utc>>,
24
25    #[serde(default)]
26    pub objective: Option<String>,
27
28    // Extended ATT&CK fields
29    #[serde(default, rename = "x_mitre_version")]
30    pub version: Option<String>,
31
32    #[serde(default, rename = "x_mitre_contributors")]
33    pub contributors: Vec<String>,
34
35    #[serde(default, rename = "x_mitre_first_seen_citation")]
36    pub first_seen_citation: Option<String>,
37
38    #[serde(default, rename = "x_mitre_last_seen_citation")]
39    pub last_seen_citation: Option<String>,
40
41    #[serde(default, rename = "x_mitre_domains")]
42    pub domains: Vec<String>,
43}
44
45impl StixObject for Campaign {
46    fn id(&self) -> &str {
47        &self.common.id
48    }
49
50    fn type_(&self) -> &str {
51        &self.common.r#type
52    }
53
54    fn created(&self) -> DateTime<Utc> {
55        self.common.created
56    }
57}
58
59impl AttackObject for Campaign {
60    fn name(&self) -> &str {
61        &self.name
62    }
63
64    fn description(&self) -> Option<&str> {
65        self.description.as_deref()
66    }
67
68    fn revoked(&self) -> bool {
69        self.common.revoked.unwrap_or(false)
70    }
71
72    fn deprecated(&self) -> bool {
73        self.common.custom_properties.get("x_mitre_deprecated").and_then(|v| v.as_bool()).unwrap_or(false)
74    }
75}