Skip to content

Commit 54f9423

Browse files
authored
Merge pull request #206 from mitre-attack/detection_strategies_fix
2 parents 7f870a6 + 41307a9 commit 54f9423

10 files changed

Lines changed: 32 additions & 51 deletions

File tree

examples/.env.example

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@
1212
#
1313
# the default download directory from the above command is "attack-releases"
1414

15-
STIX_BASE_DIR=attack-releases/stix-2.0/v17.1
16-
STIX_BUNDLE=attack-releases/stix-2.0/v17.1/enterprise-attack.json
15+
STIX_BASE_DIR=attack-releases/stix-2.0/v18.0
16+
STIX_BUNDLE=attack-releases/stix-2.0/v18.0/enterprise-attack.json

examples/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ Setting up these tools is out of scope for this README.
3838

3939
### Downloading ATT&CK STIX Bundles
4040

41-
Many example scripts require ATT&CK STIX bundles, which must be downloaded and placed in the directory specified in your `.env` file (e.g., `attack-releases/stix-2.0/v17.1`).
41+
Many example scripts require ATT&CK STIX bundles, which must be downloaded and placed in the directory specified in your `.env` file (e.g., `attack-releases/stix-2.0/v18.0`).
4242
You can download these bundles using the provided CLI command if you have mitreattack-python installed:
4343

4444
```sh

examples/generate_excel_files.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ def main():
88
output_dir = "output/"
99

1010
# Path to the STIX bundles for each domain (assumes STIX files are downloaded)
11-
stix_base_dir = os.environ.get("STIX_BASE_DIR", "attack-releases/stix-2.0/v17.1")
11+
stix_base_dir = os.environ.get("STIX_BASE_DIR", "attack-releases/stix-2.0/v18.0")
1212
stix_files = {
1313
"enterprise-attack": os.path.join(stix_base_dir, "enterprise-attack.json"),
1414
"mobile-attack": os.path.join(stix_base_dir, "mobile-attack.json"),

mitreattack/diffStix/changelog_helper.py

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -372,15 +372,16 @@ def load_data(self):
372372

373373
# Description changes
374374
#####################
375-
old_lines = old_stix_obj["description"].replace("\n", " ").splitlines()
376-
new_lines = new_stix_obj["description"].replace("\n", " ").splitlines()
377-
old_lines_unique = [line for line in old_lines if line not in new_lines]
378-
new_lines_unique = [line for line in new_lines if line not in old_lines]
379-
if old_lines_unique or new_lines_unique:
380-
html_diff = difflib.HtmlDiff(wrapcolumn=60)
381-
html_diff._legend = "" # type: ignore[attr-defined]
382-
delta = html_diff.make_table(old_lines, new_lines, "Old Description", "New Description")
383-
new_stix_obj["description_change_table"] = delta
375+
if "description" in old_stix_obj and "description" in new_stix_obj:
376+
old_lines = old_stix_obj["description"].replace("\n", " ").splitlines()
377+
new_lines = new_stix_obj["description"].replace("\n", " ").splitlines()
378+
old_lines_unique = [line for line in old_lines if line not in new_lines]
379+
new_lines_unique = [line for line in new_lines if line not in old_lines]
380+
if old_lines_unique or new_lines_unique:
381+
html_diff = difflib.HtmlDiff(wrapcolumn=60)
382+
html_diff._legend = "" # type: ignore[attr-defined]
383+
delta = html_diff.make_table(old_lines, new_lines, "Old Description", "New Description")
384+
new_stix_obj["description_change_table"] = delta
384385

385386
# Relationship changes
386387
######################
@@ -1631,16 +1632,17 @@ def is_patch_change(old_stix_obj: dict, new_stix_obj: dict) -> bool:
16311632
return True
16321633

16331634
# description changed, even though modified date didn't
1634-
old_lines = old_stix_obj["description"].replace("\n", " ").splitlines()
1635-
new_lines = new_stix_obj["description"].replace("\n", " ").splitlines()
1636-
old_lines_unique = [line for line in old_lines if line not in new_lines]
1637-
new_lines_unique = [line for line in new_lines if line not in old_lines]
1638-
if old_lines_unique or new_lines_unique:
1639-
logger.warning(
1640-
f"{stix_id} - {attack_id} has a description change "
1641-
"without the version being incremented or the last modified date changing"
1642-
)
1643-
return True
1635+
if "description" in old_stix_obj and "description" in new_stix_obj:
1636+
old_lines = old_stix_obj["description"].replace("\n", " ").splitlines()
1637+
new_lines = new_stix_obj["description"].replace("\n", " ").splitlines()
1638+
old_lines_unique = [line for line in old_lines if line not in new_lines]
1639+
new_lines_unique = [line for line in new_lines if line not in old_lines]
1640+
if old_lines_unique or new_lines_unique:
1641+
logger.warning(
1642+
f"{stix_id} - {attack_id} has a description change "
1643+
"without the version being incremented or the last modified date changing"
1644+
)
1645+
return True
16441646

16451647
# doesn't meet the definintion of a patch change
16461648
return False

mitreattack/navlayers/generators/gen_helpers.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ def build_data_strings(data_sources, data_components):
5151
"""
5252
out = dict()
5353
for component in data_components:
54+
if "x_mitre_data_source_ref" not in component:
55+
continue
5456
ref = component["x_mitre_data_source_ref"]
5557
try:
5658
source = [x for x in data_sources if x["id"] == ref][0]

mitreattack/navlayers/generators/overview_generator.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ def __init__(self, source, domain="enterprise", resource=None):
6464
"x-mitre-data-component": dict(),
6565
"campaign": dict(),
6666
"asset": dict(),
67+
"x-mitre-detection-strategy": dict(),
6768
}
6869

6970
# Scan through all relationships to identify ones that target attack techniques (attack-pattern). Then, sort

mitreattack/navlayers/generators/usage_generator.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,9 +155,8 @@ def generate_layer(self, match):
155155
)
156156
raw_layer["techniques"] = processed_listing
157157
output_layer = Layer(raw_layer)
158-
if matched_obj["type"] != "x-mitre-data-component":
159-
name = matched_obj["name"]
160-
else:
158+
name = matched_obj["name"]
159+
if matched_obj["type"] == "x-mitre-data-component" and matched_obj["id"] in self.source_mapping:
161160
name = self.source_mapping[matched_obj["id"]]
162161
output_layer.description = (
163162
f"{self.domain.capitalize() if len(self.domain) > 3 else self.domain.upper()} "

tests/test_cli.py

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
from mitreattack.navlayers.layerGenerator_cli import main as LGC_main
2020

2121

22-
@pytest.mark.skip("Unsupported functionality of mitreattack-python. keeping the unit test for legacy awareness")
2322
def test_export_svg(tmp_path: Path, layer_v43: Layer, stix_file_enterprise_latest: str):
2423
"""Test SVG Export capabilities from CLI."""
2524
demo_file = tmp_path / "demo_file.json"
@@ -43,7 +42,6 @@ def test_export_svg(tmp_path: Path, layer_v43: Layer, stix_file_enterprise_lates
4342
assert test_export_svg_file.exists()
4443

4544

46-
@pytest.mark.skip("Unsupported functionality of mitreattack-python. keeping the unit test for legacy awareness")
4745
def test_export_excel(tmp_path: Path, layer_v43: Layer, stix_file_enterprise_latest: str):
4846
"""Test excel export capabilities from CLI."""
4947
demo_file = tmp_path / "demo_file.json"
@@ -67,7 +65,6 @@ def test_export_excel(tmp_path: Path, layer_v43: Layer, stix_file_enterprise_lat
6765
assert test_export_xlsx_file.exists()
6866

6967

70-
@pytest.mark.skip("Unsupported functionality of mitreattack-python. keeping the unit test for legacy awareness")
7168
def test_generate_overview_group(tmp_path: Path, stix_file_mobile_latest: str):
7269
"""Test CLI group overview generation."""
7370
output_layer_file = tmp_path / "test_overview_group.json"
@@ -88,7 +85,6 @@ def test_generate_overview_group(tmp_path: Path, stix_file_mobile_latest: str):
8885
assert output_layer_file.exists()
8986

9087

91-
@pytest.mark.skip("Unsupported functionality of mitreattack-python. keeping the unit test for legacy awareness")
9288
def test_generate_overview_software(tmp_path: Path, stix_file_mobile_latest: str):
9389
"""Test CLI software overview generation."""
9490
output_layer_file = tmp_path / "test_overview_software.json"
@@ -109,7 +105,6 @@ def test_generate_overview_software(tmp_path: Path, stix_file_mobile_latest: str
109105
assert output_layer_file.exists()
110106

111107

112-
@pytest.mark.skip("Unsupported functionality of mitreattack-python. keeping the unit test for legacy awareness")
113108
def test_generate_overview_mitigation(tmp_path: Path, stix_file_enterprise_latest: str):
114109
"""Test CLI mitigation overview generation."""
115110
output_layer_file = tmp_path / "test_overview_mitigation.json"
@@ -130,7 +125,6 @@ def test_generate_overview_mitigation(tmp_path: Path, stix_file_enterprise_lates
130125
assert output_layer_file.exists()
131126

132127

133-
@pytest.mark.skip("Unsupported functionality of mitreattack-python. keeping the unit test for legacy awareness")
134128
def test_generate_overview_datasource(tmp_path: Path, stix_file_enterprise_latest: str):
135129
"""Test CLI datasource overview generation."""
136130
output_layer_file = tmp_path / "test_overview_datasource.json"
@@ -151,7 +145,6 @@ def test_generate_overview_datasource(tmp_path: Path, stix_file_enterprise_lates
151145
assert output_layer_file.exists()
152146

153147

154-
@pytest.mark.skip("Unsupported functionality of mitreattack-python. keeping the unit test for legacy awareness")
155148
def test_generate_mapped_group(tmp_path: Path, stix_file_enterprise_latest: str):
156149
"""Test CLI group mapped generation (APT1)."""
157150
output_layer_file = tmp_path / "test_mapped_group.json"
@@ -172,7 +165,6 @@ def test_generate_mapped_group(tmp_path: Path, stix_file_enterprise_latest: str)
172165
assert output_layer_file.exists()
173166

174167

175-
@pytest.mark.skip("Unsupported functionality of mitreattack-python. keeping the unit test for legacy awareness")
176168
def test_generate_mapped_software(tmp_path: Path, stix_file_enterprise_latest: str):
177169
"""Test CLI software mapped generation (S0202)."""
178170
output_layer_file = tmp_path / "test_mapped_software.json"
@@ -193,7 +185,6 @@ def test_generate_mapped_software(tmp_path: Path, stix_file_enterprise_latest: s
193185
assert output_layer_file.exists()
194186

195187

196-
@pytest.mark.skip("Unsupported functionality of mitreattack-python. keeping the unit test for legacy awareness")
197188
def test_generate_mapped_mitigation(tmp_path: Path, stix_file_mobile_latest: str):
198189
"""Test CLI mitigation mapped generation (M1013)."""
199190
output_layer_file = tmp_path / "test_mapped_mitigation.json"
@@ -214,7 +205,6 @@ def test_generate_mapped_mitigation(tmp_path: Path, stix_file_mobile_latest: str
214205
assert output_layer_file.exists()
215206

216207

217-
@pytest.mark.skip("Unsupported functionality of mitreattack-python. keeping the unit test for legacy awareness")
218208
def test_generate_mapped_datasource(tmp_path: Path, stix_file_enterprise_latest: str):
219209
"""Test CLI datasource mapped generation."""
220210
output_layer_file = tmp_path / "test_mapped_datasource.json"
@@ -277,7 +267,6 @@ def test_generate_batch_software(tmp_path: Path, stix_file_ics_latest: str):
277267
assert output_layers_dir.is_dir()
278268

279269

280-
@pytest.mark.skip("Unsupported functionality of mitreattack-python. keeping the unit test for legacy awareness")
281270
def test_generate_batch_mitigation(tmp_path: Path, stix_file_enterprise_latest: str):
282271
"""Test CLI mitigation batch generation."""
283272
output_layers_dir = tmp_path / "test_batch_mitigation"
@@ -298,7 +287,6 @@ def test_generate_batch_mitigation(tmp_path: Path, stix_file_enterprise_latest:
298287
assert output_layers_dir.is_dir()
299288

300289

301-
@pytest.mark.skip("Unsupported functionality of mitreattack-python. keeping the unit test for legacy awareness")
302290
def test_generate_batch_datasource(tmp_path: Path, stix_file_enterprise_latest: str):
303291
"""Test CLI datasource batch generation."""
304292
output_layers_dir = tmp_path / "test_batch_datasource"

tests/test_mitreattackdata.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -160,11 +160,6 @@ def test_all_campaigns_using_techniques(self, mitre_attack_data_enterprise: Mitr
160160
campaigns = mitre_attack_data_enterprise.get_all_campaigns_using_all_techniques()
161161
assert campaigns
162162

163-
# def test_all_datacomponents_detecting_all_techniques(self, mitre_attack_data_enterprise: MitreAttackData):
164-
# """Test that all datacomponents detecting all techniques can be retrieved."""
165-
# datacomponents = mitre_attack_data_enterprise.get_all_datacomponents_detecting_all_techniques()
166-
# assert datacomponents
167-
168163
def test_all_groups_attributing_to_all_campaigns(self, mitre_attack_data_enterprise: MitreAttackData):
169164
"""Test that all groups attributing to all campaigns can be retrieved."""
170165
groups = mitre_attack_data_enterprise.get_all_groups_attributing_to_all_campaigns()
@@ -210,11 +205,6 @@ def test_all_subtechniques_of_all_techniques(self, mitre_attack_data_enterprise:
210205
subtechniques = mitre_attack_data_enterprise.get_all_subtechniques_of_all_techniques()
211206
assert subtechniques
212207

213-
# def test_all_techniques_detected_by_all_datacomponents(self, mitre_attack_data_enterprise: MitreAttackData):
214-
# """Test that all techniques detected by all datacomponents can be retrieved."""
215-
# techniques = mitre_attack_data_enterprise.get_all_techniques_detected_by_all_datacomponents()
216-
# assert techniques
217-
218208
def test_all_techniques_mitigated_by_all_mitigations(self, mitre_attack_data_enterprise: MitreAttackData):
219209
"""Test that all techniques mitigated by all mitigations can be retrieved."""
220210
techniques = mitre_attack_data_enterprise.get_all_techniques_mitigated_by_all_mitigations()

tests/test_to_excel.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,8 @@ def check_excel_files_exist(excel_folder: Path, domain: str):
4949
assert (excel_folder / f"{domain}-software.xlsx").exists()
5050
assert (excel_folder / f"{domain}-tactics.xlsx").exists()
5151
assert (excel_folder / f"{domain}-techniques.xlsx").exists()
52-
# TODO: add in check for analytics/detection strategies after ATT&CK v18 is released
53-
# assert (excel_folder / f"{domain}-analytics.xlsx").exists()
54-
# assert (excel_folder / f"{domain}-detectionstrategies.xlsx").exists()
52+
assert (excel_folder / f"{domain}-analytics.xlsx").exists()
53+
assert (excel_folder / f"{domain}-detectionstrategies.xlsx").exists()
5554

5655

5756
def test_enterprise_latest(tmp_path: Path, memstore_enterprise_latest: stix2.MemoryStore):

0 commit comments

Comments
 (0)