1pub const MEDIA_TYPE_STIX: &str = "application/stix+json;version=2.1";
6
7pub const MEDIA_TYPE_TAXII: &str = "application/taxii+json;version=2.1";
9
10pub const MEDIA_TYPE_STIX_GENERIC: &str = "application/stix+json";
12
13pub const MEDIA_TYPE_TAXII_GENERIC: &str = "application/taxii+json";
15
16pub mod common;
17pub mod sdos;
18pub mod sros;
19pub mod observables;
20pub mod vocab;
21pub mod bundle;
22pub mod objects;
23pub mod pattern;
24
25pub use common::{
26 CommonProperties, ExtensionDefinition, ExternalReference, GranularMarking, LanguageContent,
27 MarkingDefinition, StixObject, extract_type_from_id, generate_stix_id, is_valid_ref_for_type,
28 is_valid_stix_id,
29};
30pub use objects::*;
31pub use observables::*;
32pub use sdos::*;
33pub use sros::Relationship;
34pub use vocab::*;
35pub use bundle::*;
36pub use pattern::{validate_pattern, PatternBuilder, PatternError};
37
38use uuid::Uuid;
39const SCO_NAMESPACE: Uuid = Uuid::from_u128(0x00abedb4_aa42_466c_9c01_def7442f5a74);
40
41fn generate_sco_id(object_type: &str, data: &str) -> String {
42 let id_part = Uuid::new_v5(&SCO_NAMESPACE, data.as_bytes());
43 format!("{}--{}", object_type, id_part)
44}
45use serde::{Deserialize, Serialize};
46use serde::de::Deserializer;
47use serde_json::Value;
48
49#[derive(Debug, Clone, PartialEq, Serialize)]
51#[serde(tag = "type", rename_all = "kebab-case")]
52pub enum StixObjectEnum {
53 Identity(Identity),
54 Malware(Malware),
55 Indicator(Indicator),
56 ObservedData(ObservedData),
57 MalwareAnalysis(MalwareAnalysis),
58 Sighting(Sighting),
59 Relationship(Relationship),
60 File(File),
61 Incident(Incident),
62 Location(Location),
63 NetworkTraffic(NetworkTraffic),
64 DomainName(DomainName),
65 #[serde(rename = "ipv4-addr")]
66 IPv4Addr(IPv4Addr),
67 Url(Url),
68 Process(Process),
69 Artifact(Artifact),
70 #[serde(rename = "ipv6-addr")]
71 IPv6Addr(IPv6Addr),
72 MacAddr(MacAddr),
73 Software(Software),
74 UserAccount(UserAccount),
75 EmailAddr(EmailAddr),
76 EmailMessage(EmailMessage),
77 SocketAddr(SocketAddr),
78 AutonomousSystem(AutonomousSystem),
79 SoftwarePackage(SoftwarePackage),
80 Directory(Directory),
81 Mutex(Mutex),
82 WindowsRegistryKey(WindowsRegistryKey),
83 X509Certificate(X509Certificate),
84 AttackPattern(AttackPattern),
85 Campaign(Campaign),
86 ThreatActor(ThreatActor),
87 Tool(Tool),
88 Vulnerability(Vulnerability),
89 CourseOfAction(CourseOfAction),
90 IntrusionSet(IntrusionSet),
91 Infrastructure(Infrastructure),
92 Report(Report),
93 Note(Note),
94 Opinion(Opinion),
95 Grouping(Grouping),
96 MarkingDefinition(MarkingDefinition),
97 LanguageContent(LanguageContent),
98 ExtensionDefinition(ExtensionDefinition),
99 Custom(serde_json::Value),
100}
101
102impl StixObjectEnum {
103 pub fn name(&self) -> Option<&str> {
105 match self {
106 StixObjectEnum::Identity(o) => Some(&o.name),
107 StixObjectEnum::Malware(o) => Some(&o.name),
108 StixObjectEnum::ThreatActor(o) => Some(&o.name),
109 StixObjectEnum::AttackPattern(o) => Some(&o.name),
110 StixObjectEnum::Campaign(o) => Some(&o.name),
111 StixObjectEnum::Tool(o) => Some(&o.name),
112 StixObjectEnum::Vulnerability(o) => Some(&o.name),
113 StixObjectEnum::CourseOfAction(o) => Some(&o.name),
114 StixObjectEnum::Infrastructure(o) => Some(&o.name),
115 StixObjectEnum::Report(o) => Some(&o.name),
116 _ => None,
117 }
118 }
119
120 pub fn created(&self) -> chrono::DateTime<chrono::Utc> {
121 match self {
122 StixObjectEnum::Indicator(o) => o.common.created,
123 StixObjectEnum::Malware(o) => o.common.created,
124 StixObjectEnum::ThreatActor(o) => o.common.created,
125 StixObjectEnum::Identity(o) => o.common.created,
126 StixObjectEnum::IntrusionSet(o) => o.common.created,
127 StixObjectEnum::Campaign(o) => o.common.created,
128 StixObjectEnum::Relationship(o) => o.common.created,
129 StixObjectEnum::Custom(v) => v.get("created").and_then(|c| c.as_str()).and_then(|s| chrono::DateTime::parse_from_rfc3339(s).ok()).map(|dt| dt.with_timezone(&chrono::Utc)).unwrap_or_else(chrono::Utc::now),
130 _ => chrono::Utc::now(),
131 }
132 }
133
134 pub fn labels(&self) -> Option<&Vec<String>> {
135 match self {
136 StixObjectEnum::Indicator(o) => o.common.labels.as_ref(),
137 StixObjectEnum::Malware(o) => o.common.labels.as_ref(),
138 StixObjectEnum::ThreatActor(o) => o.common.labels.as_ref(),
139 StixObjectEnum::Identity(o) => o.common.labels.as_ref(),
140 StixObjectEnum::IntrusionSet(o) => o.common.labels.as_ref(),
141 StixObjectEnum::Campaign(o) => o.common.labels.as_ref(),
142 _ => None,
143 }
144 }
145
146 pub fn id(&self) -> String {
147 match self {
148 StixObjectEnum::Identity(o) => o.id().to_string(),
149 StixObjectEnum::Malware(o) => o.id().to_string(),
150 StixObjectEnum::Indicator(o) => o.id().to_string(),
151 StixObjectEnum::ObservedData(o) => o.id().to_string(),
152 StixObjectEnum::MalwareAnalysis(o) => o.id().to_string(),
153 StixObjectEnum::Sighting(o) => o.id().to_string(),
154 StixObjectEnum::Relationship(o) => o.id().to_string(),
155 StixObjectEnum::File(o) => {
156 if let Some(hashes) = &o.hashes {
157 if let Some(h) = hashes.get("SHA-256").or(hashes.get("MD5")) {
158 return generate_sco_id("file", h);
159 }
160 }
161 generate_sco_id("file", o.name.as_deref().unwrap_or("unknown"))
162 },
163 StixObjectEnum::Incident(o) => o.id().to_string(),
164 StixObjectEnum::Location(o) => o.id().to_string(),
165 StixObjectEnum::NetworkTraffic(_) => generate_sco_id("network-traffic", "unknown"),
166 StixObjectEnum::DomainName(o) => generate_sco_id("domain-name", &o.value),
167 StixObjectEnum::IPv4Addr(o) => generate_sco_id("ipv4-addr", &o.value),
168 StixObjectEnum::Url(o) => generate_sco_id("url", &o.value),
169 StixObjectEnum::Process(_) => generate_sco_id("process", "unknown"),
170 StixObjectEnum::Artifact(_) => generate_sco_id("artifact", "unknown"),
171 StixObjectEnum::IPv6Addr(o) => generate_sco_id("ipv6-addr", &o.value),
172 StixObjectEnum::MacAddr(o) => generate_sco_id("mac-addr", &o.value),
173 StixObjectEnum::Software(o) => generate_sco_id("software", o.name.as_deref().unwrap_or("unknown")),
174 StixObjectEnum::UserAccount(o) => generate_sco_id("user-account", o.user_id.as_deref().unwrap_or("unknown")),
175 StixObjectEnum::EmailAddr(o) => generate_sco_id("email-addr", &o.value),
176 StixObjectEnum::EmailMessage(_) => generate_sco_id("email-message", "unknown"),
177 StixObjectEnum::SocketAddr(_) => generate_sco_id("socket-addr", "unknown"),
178 StixObjectEnum::AutonomousSystem(o) => generate_sco_id("autonomous-system", &o.number.map(|n| n.to_string()).unwrap_or_else(|| "unknown".to_string())),
179 StixObjectEnum::SoftwarePackage(_) => generate_sco_id("software-package", "unknown"),
180 StixObjectEnum::Directory(o) => generate_sco_id("directory", o.path.as_deref().unwrap_or("unknown")),
181 StixObjectEnum::Mutex(o) => generate_sco_id("mutex", o.name.as_deref().unwrap_or("unknown")),
182 StixObjectEnum::WindowsRegistryKey(o) => generate_sco_id("windows-registry-key", o.key.as_deref().unwrap_or("unknown")),
183 StixObjectEnum::X509Certificate(_) => generate_sco_id("x509-certificate", "unknown"),
184 StixObjectEnum::AttackPattern(o) => o.id().to_string(),
185 StixObjectEnum::Campaign(o) => o.id().to_string(),
186 StixObjectEnum::ThreatActor(o) => o.id().to_string(),
187 StixObjectEnum::Tool(o) => o.id().to_string(),
188 StixObjectEnum::Vulnerability(o) => o.id().to_string(),
189 StixObjectEnum::CourseOfAction(o) => o.id().to_string(),
190 StixObjectEnum::IntrusionSet(o) => o.id().to_string(),
191 StixObjectEnum::Infrastructure(o) => o.id().to_string(),
192 StixObjectEnum::Report(o) => o.id().to_string(),
193 StixObjectEnum::Note(o) => o.id().to_string(),
194 StixObjectEnum::Opinion(o) => o.id().to_string(),
195 StixObjectEnum::Grouping(o) => o.id().to_string(),
196 StixObjectEnum::MarkingDefinition(o) => o.id().to_string(),
197 StixObjectEnum::LanguageContent(o) => o.id().to_string(),
198 StixObjectEnum::ExtensionDefinition(o) => o.id().to_string(),
199 StixObjectEnum::Custom(v) => v.get("id").and_then(|i| i.as_str()).map(|s| s.to_string()).unwrap_or_else(|| "unknown".to_string()),
200 }
201 }
202
203 pub fn type_(&self) -> &str {
205 match self {
206 StixObjectEnum::Identity(o) => o.type_(),
207 StixObjectEnum::Malware(o) => o.type_(),
208 StixObjectEnum::Indicator(o) => o.type_(),
209 StixObjectEnum::ObservedData(o) => o.type_(),
210 StixObjectEnum::MalwareAnalysis(o) => o.type_(),
211 StixObjectEnum::Sighting(o) => o.type_(),
212 StixObjectEnum::Relationship(o) => o.type_(),
213 StixObjectEnum::File(_) => "file",
214 StixObjectEnum::Incident(o) => o.type_(),
215 StixObjectEnum::Location(o) => o.type_(),
216 StixObjectEnum::NetworkTraffic(_) => "network-traffic",
217 StixObjectEnum::DomainName(_) => "domain-name",
218 StixObjectEnum::IPv4Addr(_) => "ipv4-addr",
219 StixObjectEnum::Url(_) => "url",
220 StixObjectEnum::Process(_) => "process",
221 StixObjectEnum::Artifact(_) => "artifact",
222 StixObjectEnum::IPv6Addr(_) => "ipv6-addr",
223 StixObjectEnum::MacAddr(_) => "mac-addr",
224 StixObjectEnum::Software(_) => "software",
225 StixObjectEnum::UserAccount(_) => "user-account",
226 StixObjectEnum::EmailAddr(_) => "email-addr",
227 StixObjectEnum::EmailMessage(_) => "email-message",
228 StixObjectEnum::SocketAddr(_) => "socket-addr",
229 StixObjectEnum::AutonomousSystem(_) => "autonomous-system",
230 StixObjectEnum::SoftwarePackage(_) => "software-package",
231 StixObjectEnum::Directory(_) => "directory",
232 StixObjectEnum::Mutex(_) => "mutex",
233 StixObjectEnum::WindowsRegistryKey(_) => "windows-registry-key",
234 StixObjectEnum::X509Certificate(_) => "x509-certificate",
235 StixObjectEnum::AttackPattern(o) => o.type_(),
236 StixObjectEnum::Campaign(o) => o.type_(),
237 StixObjectEnum::ThreatActor(o) => o.type_(),
238 StixObjectEnum::Tool(o) => o.type_(),
239 StixObjectEnum::Vulnerability(o) => o.type_(),
240 StixObjectEnum::CourseOfAction(o) => o.type_(),
241 StixObjectEnum::IntrusionSet(o) => o.type_(),
242 StixObjectEnum::Infrastructure(o) => o.type_(),
243 StixObjectEnum::Report(o) => o.type_(),
244 StixObjectEnum::Note(o) => o.type_(),
245 StixObjectEnum::Opinion(o) => o.type_(),
246 StixObjectEnum::Grouping(o) => o.type_(),
247 StixObjectEnum::MarkingDefinition(o) => o.type_(),
248 StixObjectEnum::LanguageContent(o) => o.type_(),
249 StixObjectEnum::ExtensionDefinition(o) => o.type_(),
250 StixObjectEnum::Custom(v) => v.get("type").and_then(|t| t.as_str()).unwrap_or("unknown"),
251 }
252 }
253}
254
255impl<'de> Deserialize<'de> for StixObjectEnum {
260 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
261 where
262 D: Deserializer<'de>,
263 {
264 let v = Value::deserialize(deserializer).map_err(serde::de::Error::custom)?;
265 let t = v
266 .get("type")
267 .and_then(Value::as_str)
268 .ok_or_else(|| serde::de::Error::custom("missing or invalid `type` field"))?;
269 match t {
270 "identity" => Ok(StixObjectEnum::Identity(serde_json::from_value(v).map_err(serde::de::Error::custom)?)),
271 "malware" => Ok(StixObjectEnum::Malware(serde_json::from_value(v).map_err(serde::de::Error::custom)?)),
272 "indicator" => Ok(StixObjectEnum::Indicator(serde_json::from_value(v).map_err(serde::de::Error::custom)?)),
273 "observed-data" => Ok(StixObjectEnum::ObservedData(serde_json::from_value(v).map_err(serde::de::Error::custom)?)),
274 "file" => Ok(StixObjectEnum::File(serde_json::from_value(v.clone()).map_err(serde::de::Error::custom)?)),
275 "network-traffic" => Ok(StixObjectEnum::NetworkTraffic(serde_json::from_value(v.clone()).map_err(serde::de::Error::custom)?)),
276 "domain-name" => Ok(StixObjectEnum::DomainName(serde_json::from_value(v.clone()).map_err(serde::de::Error::custom)?)),
277 "ipv4-addr" => Ok(StixObjectEnum::IPv4Addr(serde_json::from_value(v.clone()).map_err(serde::de::Error::custom)?)),
278 "ipv6-addr" => Ok(StixObjectEnum::IPv6Addr(serde_json::from_value(v.clone()).map_err(serde::de::Error::custom)?)),
279 "url" => Ok(StixObjectEnum::Url(serde_json::from_value(v.clone()).map_err(serde::de::Error::custom)?)),
280 "process" => Ok(StixObjectEnum::Process(serde_json::from_value(v.clone()).map_err(serde::de::Error::custom)?)),
281 "artifact" => Ok(StixObjectEnum::Artifact(serde_json::from_value(v.clone()).map_err(serde::de::Error::custom)?)),
282 "mac-addr" => Ok(StixObjectEnum::MacAddr(serde_json::from_value(v.clone()).map_err(serde::de::Error::custom)?)),
283 "software" => Ok(StixObjectEnum::Software(serde_json::from_value(v.clone()).map_err(serde::de::Error::custom)?)),
284 "user-account" => Ok(StixObjectEnum::UserAccount(serde_json::from_value(v.clone()).map_err(serde::de::Error::custom)?)),
285 "email-addr" => Ok(StixObjectEnum::EmailAddr(serde_json::from_value(v.clone()).map_err(serde::de::Error::custom)?)),
286 "email-message" => Ok(StixObjectEnum::EmailMessage(serde_json::from_value(v.clone()).map_err(serde::de::Error::custom)?)),
287 "socket-addr" => Ok(StixObjectEnum::SocketAddr(serde_json::from_value(v.clone()).map_err(serde::de::Error::custom)?)),
288 "autonomous-system" => Ok(StixObjectEnum::AutonomousSystem(serde_json::from_value(v.clone()).map_err(serde::de::Error::custom)?)),
289 "software-package" => Ok(StixObjectEnum::SoftwarePackage(serde_json::from_value(v.clone()).map_err(serde::de::Error::custom)?)),
290 "directory" => Ok(StixObjectEnum::Directory(serde_json::from_value(v.clone()).map_err(serde::de::Error::custom)?)),
291 "mutex" => Ok(StixObjectEnum::Mutex(serde_json::from_value(v.clone()).map_err(serde::de::Error::custom)?)),
292 "windows-registry-key" => Ok(StixObjectEnum::WindowsRegistryKey(serde_json::from_value(v.clone()).map_err(serde::de::Error::custom)?)),
293 "x509-certificate" => Ok(StixObjectEnum::X509Certificate(serde_json::from_value(v.clone()).map_err(serde::de::Error::custom)?)),
294 "malware-analysis" => Ok(StixObjectEnum::MalwareAnalysis(serde_json::from_value(v).map_err(serde::de::Error::custom)?)),
295 "sighting" => Ok(StixObjectEnum::Sighting(serde_json::from_value(v).map_err(serde::de::Error::custom)?)),
296 "grouping" => Ok(StixObjectEnum::Grouping(serde_json::from_value(v.clone()).map_err(serde::de::Error::custom)?)),
297 "incident" => Ok(StixObjectEnum::Incident(serde_json::from_value(v.clone()).map_err(serde::de::Error::custom)?)),
298 "location" => Ok(StixObjectEnum::Location(serde_json::from_value(v.clone()).map_err(serde::de::Error::custom)?)),
299 "opinion" => Ok(StixObjectEnum::Opinion(serde_json::from_value(v.clone()).map_err(serde::de::Error::custom)?)),
300 "relationship" => Ok(StixObjectEnum::Relationship(serde_json::from_value(v).map_err(serde::de::Error::custom)?)),
301 "marking-definition" => Ok(StixObjectEnum::MarkingDefinition(serde_json::from_value(v.clone()).map_err(serde::de::Error::custom)?)),
302 "language-content" => Ok(StixObjectEnum::LanguageContent(serde_json::from_value(v.clone()).map_err(serde::de::Error::custom)?)),
303 "extension-definition" => Ok(StixObjectEnum::ExtensionDefinition(serde_json::from_value(v.clone()).map_err(serde::de::Error::custom)?)),
304 other if other.starts_with("x-") => Ok(StixObjectEnum::Custom(v.clone())),
305 "attack-pattern" => Ok(StixObjectEnum::AttackPattern(serde_json::from_value(v.clone()).map_err(serde::de::Error::custom)?)),
306 "campaign" => Ok(StixObjectEnum::Campaign(serde_json::from_value(v.clone()).map_err(serde::de::Error::custom)?)),
307 "threat-actor" => Ok(StixObjectEnum::ThreatActor(serde_json::from_value(v.clone()).map_err(serde::de::Error::custom)?)),
308 "tool" => Ok(StixObjectEnum::Tool(serde_json::from_value(v.clone()).map_err(serde::de::Error::custom)?)),
309 "vulnerability" => Ok(StixObjectEnum::Vulnerability(serde_json::from_value(v.clone()).map_err(serde::de::Error::custom)?)),
310 "course-of-action" => Ok(StixObjectEnum::CourseOfAction(serde_json::from_value(v.clone()).map_err(serde::de::Error::custom)?)),
311 "intrusion-set" => Ok(StixObjectEnum::IntrusionSet(serde_json::from_value(v.clone()).map_err(serde::de::Error::custom)?)),
312 "infrastructure" => Ok(StixObjectEnum::Infrastructure(serde_json::from_value(v.clone()).map_err(serde::de::Error::custom)?)),
313 "report" => Ok(StixObjectEnum::Report(serde_json::from_value(v.clone()).map_err(serde::de::Error::custom)?)),
314 "note" => Ok(StixObjectEnum::Note(serde_json::from_value(v.clone()).map_err(serde::de::Error::custom)?)),
315 other => Err(serde::de::Error::custom(format!("unknown type: {}", other))),
316 }
317 }
318}
319