Skip to content

Commit e16c779

Browse files
committed
updated models with latest stix object types
1 parent 8eeb54a commit e16c779

1 file changed

Lines changed: 158 additions & 1 deletion

File tree

src/attackcti/models.py

Lines changed: 158 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,33 @@
1-
from typing import List, Optional, Dict, Any
1+
"""Pydantic models for ATT&CK STIX objects."""
2+
3+
from dataclasses import dataclass
4+
from typing import Any, Dict, List, Optional
5+
26
from pydantic import BaseModel, Field, model_validator
37

8+
9+
@dataclass(frozen=True)
10+
class LoadedStix:
11+
"""Container for parsed STIX objects and detected spec version."""
12+
13+
spec_version: str | None
14+
objects: list[Any]
15+
16+
class LogSourceReference(BaseModel):
17+
"""A log source reference used by an analytic."""
18+
19+
x_mitre_data_component_ref: str
20+
name: str
21+
channel: str
22+
23+
24+
class MutableElement(BaseModel):
25+
"""Environment-specific analytic tuning knobs."""
26+
27+
field: str
28+
description: str
29+
30+
431
class ExternalReference(BaseModel):
532
"""STIX external reference entry."""
633

@@ -27,6 +54,21 @@ class STIXCore(BaseModel):
2754

2855
@model_validator(mode='before')
2956
def extract_common_fields(cls, values):
57+
"""Extract common fields from the input dictionary.
58+
59+
This method processes the input dictionary to extract and set common fields,
60+
such as the URL from the first external reference, if available.
61+
62+
Parameters
63+
----------
64+
values : dict
65+
The input dictionary containing STIX object data.
66+
67+
Returns
68+
-------
69+
dict
70+
The updated dictionary with extracted common fields.
71+
"""
3072
external_references = values.get('external_references')
3173
if external_references and len(external_references) > 0:
3274
first_ref = external_references[0]
@@ -36,6 +78,18 @@ def extract_common_fields(cls, values):
3678

3779
@classmethod
3880
def extract_external_id(cls, external_references: List[ExternalReference]):
81+
"""Extract the external ID from the first external reference.
82+
83+
Parameters
84+
----------
85+
external_references : List[ExternalReference]
86+
A list of external references associated with the STIX object.
87+
88+
Returns
89+
-------
90+
str or None
91+
The external ID from the first external reference, or None if not available.
92+
"""
3993
if external_references and len(external_references) > 0:
4094
return external_references[0].external_id
4195
return None
@@ -63,6 +117,18 @@ class Technique(STIXCore):
63117

64118
@model_validator(mode='before')
65119
def extract_phase_name(cls, values: Dict[str, Any]):
120+
"""Extract phase names from the technique field.
121+
122+
Parameters
123+
----------
124+
values : Dict[str, Any]
125+
The input dictionary containing technique data.
126+
127+
Returns
128+
-------
129+
Dict[str, Any]
130+
The updated dictionary with extracted phase names.
131+
"""
66132
if 'kill_chain_phases' in values:
67133
kill_chain_phases = values['kill_chain_phases']
68134
phase_names = [phase['phase_name'] for phase in kill_chain_phases if 'phase_name' in phase]
@@ -71,6 +137,11 @@ def extract_phase_name(cls, values: Dict[str, Any]):
71137

72138
@model_validator(mode='after')
73139
def set_technique_id(self):
140+
"""Set the technique ID based on the external references.
141+
142+
This method extracts the external ID from the first external reference
143+
and assigns it to the `technique_id` attribute.
144+
"""
74145
self.technique_id = self.extract_external_id(self.external_references)
75146
return self
76147

@@ -84,6 +155,11 @@ class Mitigation(STIXCore):
84155

85156
@model_validator(mode='after')
86157
def set_mitigation_id(self):
158+
"""Set the mitigation ID based on the external references.
159+
160+
This method extracts the external ID from the first external reference
161+
and assigns it to the `mitigation_id` attribute.
162+
"""
87163
self.mitigation_id = self.extract_external_id(self.external_references)
88164
return self
89165

@@ -98,6 +174,11 @@ class Group(STIXCore):
98174

99175
@model_validator(mode='after')
100176
def set_group_id(self):
177+
"""Set the group ID based on the external references.
178+
179+
This method extracts the external ID from the first external reference
180+
and assigns it to the `group_id` attribute.
181+
"""
101182
self.group_id = self.extract_external_id(self.external_references)
102183
return self
103184

@@ -115,6 +196,11 @@ class Software(STIXCore):
115196

116197
@model_validator(mode='after')
117198
def set_software_id(self):
199+
"""Set the software ID based on the external references.
200+
201+
This method extracts the external ID from the first external reference
202+
and assigns it to the `software_id` attribute.
203+
"""
118204
self.software_id = self.extract_external_id(self.external_references)
119205
return self
120206

@@ -144,6 +230,11 @@ class Tactic(STIXCore):
144230

145231
@model_validator(mode='after')
146232
def set_tactic_id(self):
233+
"""Set the tactic ID based on the external references.
234+
235+
This method extracts the external ID from the first external reference
236+
and assigns it to the `tactic_id` attribute.
237+
"""
147238
self.tactic_id = self.extract_external_id(self.external_references)
148239
return self
149240

@@ -157,6 +248,11 @@ class Matrix(STIXCore):
157248

158249
@model_validator(mode='after')
159250
def set_matrix_id(self):
251+
"""Set the matrix ID based on the external references.
252+
253+
This method extracts the external ID from the first external reference
254+
and assigns it to the `matrix_id` attribute.
255+
"""
160256
self.matrix_id = self.extract_external_id(self.external_references)
161257
return self
162258

@@ -199,6 +295,11 @@ class Campaign(STIXCore):
199295

200296
@model_validator(mode='after')
201297
def set_campaign_id(self):
298+
"""Set the campaign ID based on the external references.
299+
300+
This method extracts the external ID from the first external reference
301+
and assigns it to the `campaign_id` attribute.
302+
"""
202303
self.campaign_id = self.extract_external_id(self.external_references)
203304
return self
204305

@@ -219,6 +320,18 @@ class GroupTechnique(Group):
219320

220321
@model_validator(mode='before')
221322
def extract_phase_name(cls, values: Dict[str, Any]):
323+
"""Extract phase names from the group field.
324+
325+
Parameters
326+
----------
327+
values : Dict[str, Any]
328+
The input dictionary containing group data.
329+
330+
Returns
331+
-------
332+
Dict[str, Any]
333+
The updated dictionary with extracted phase names.
334+
"""
222335
if 'tactic' in values:
223336
kill_chain_phases = values['tactic']
224337
phase_names = [phase['phase_name'] for phase in kill_chain_phases if 'phase_name' in phase]
@@ -231,3 +344,47 @@ class STIXLocalPaths(BaseModel):
231344
enterprise: Optional[str] = Field(None, description="Path to the local enterprise-attack directory or JSON file.")
232345
mobile: Optional[str] = Field(None, description="Path to the local mobile-attack directory or JSON file.")
233346
ics: Optional[str] = Field(None, description="Path to the local ics-attack directory or JSON file.")
347+
348+
349+
class DetectionStrategy(STIXCore):
350+
"""ATT&CK Detection Strategy model (x-mitre-detection-strategy)."""
351+
352+
detection_strategy: str = Field(..., alias="name")
353+
analytic_refs: List[str] = Field(default_factory=list, alias="x_mitre_analytic_refs")
354+
355+
356+
class Analytic(STIXCore):
357+
"""ATT&CK Analytic model (x-mitre-analytic)."""
358+
359+
analytic: str = Field(..., alias="name")
360+
analytic_description: Optional[str] = Field(None, alias="description")
361+
platforms: Optional[List[str]] = Field(None, alias="x_mitre_platforms")
362+
log_source_references: Optional[List[LogSourceReference]] = Field(
363+
None, alias="x_mitre_log_source_references"
364+
)
365+
mutable_elements: Optional[List[MutableElement]] = Field(None, alias="x_mitre_mutable_elements")
366+
367+
pydantic_model_mapping = {
368+
"techniques": Technique,
369+
"data-component": DataComponent,
370+
"mitigations": Mitigation,
371+
"groups": Group,
372+
"malware": Software,
373+
"tools": Software,
374+
"tool": Software,
375+
"data-source": DataSource,
376+
"relationships": Relationship,
377+
"tactics": Tactic,
378+
"matrix": Matrix,
379+
"identity": Identity,
380+
"marking-definition": MarkingDefinition,
381+
"campaigns": Campaign,
382+
"campaign": Campaign,
383+
"attack-pattern": Technique,
384+
"course-of-action": Mitigation,
385+
"intrusion-set": Group,
386+
"x-mitre-data-source": DataSource,
387+
"x-mitre-data-component": DataComponent,
388+
"x-mitre-detection-strategy": DetectionStrategy,
389+
"x-mitre-analytic": Analytic,
390+
}

0 commit comments

Comments
 (0)