0
# Version Comparison and Diff Analysis
1
2
Compare different versions of ATT&CK data and generate detailed changelog reports showing additions, modifications, and removals between releases. This module provides comprehensive tools for analyzing changes in the ATT&CK framework over time, tracking the evolution of techniques, groups, software, and relationships.
3
4
## Capabilities
5
6
### Main Diff Classes
7
8
Core classes for comparing ATT&CK versions and generating change reports.
9
10
```python { .api }
11
class DiffStix:
12
def __init__(self, old_data: dict, new_data: dict):
13
"""
14
Initialize ATT&CK version comparison.
15
16
Args:
17
old_data (dict): Earlier version STIX data bundle
18
new_data (dict): Later version STIX data bundle
19
"""
20
21
def generate_changelog(self) -> dict:
22
"""
23
Generate comprehensive changelog between versions.
24
25
Returns:
26
dict: Structured changelog with additions, modifications, and removals
27
"""
28
29
def export_changelog(self, output_file: str, format: str = "json") -> None:
30
"""
31
Export changelog to file.
32
33
Args:
34
output_file (str): Path to output file
35
format (str): Output format ("json", "markdown", "html")
36
"""
37
38
def get_added_objects(self, object_type: str = None) -> List[dict]:
39
"""
40
Get objects added in the new version.
41
42
Args:
43
object_type (str, optional): Filter by STIX object type
44
45
Returns:
46
List[dict]: List of added objects
47
"""
48
49
def get_removed_objects(self, object_type: str = None) -> List[dict]:
50
"""
51
Get objects removed in the new version.
52
53
Args:
54
object_type (str, optional): Filter by STIX object type
55
56
Returns:
57
List[dict]: List of removed objects
58
"""
59
60
def get_modified_objects(self, object_type: str = None) -> List[dict]:
61
"""
62
Get objects modified between versions.
63
64
Args:
65
object_type (str, optional): Filter by STIX object type
66
67
Returns:
68
List[dict]: List of modified objects with change details
69
"""
70
71
def compare_relationships(self) -> dict:
72
"""
73
Compare relationships between versions.
74
75
Returns:
76
dict: Changes in relationships (added, removed, modified)
77
"""
78
79
def generate_summary_statistics(self) -> dict:
80
"""
81
Generate summary statistics of changes.
82
83
Returns:
84
dict: Summary counts of additions, modifications, removals by type
85
"""
86
```
87
88
### Version Tracking Classes
89
90
Support classes for tracking object versions and changes.
91
92
```python { .api }
93
class AttackObjectVersion:
94
def __init__(self, stix_object: dict, version_info: dict):
95
"""
96
Track version information for an ATT&CK object.
97
98
Args:
99
stix_object (dict): STIX object data
100
version_info (dict): Version metadata
101
"""
102
103
def get_version_string(self) -> str:
104
"""
105
Get version string for the object.
106
107
Returns:
108
str: Version identifier
109
"""
110
111
def compare_with(self, other: 'AttackObjectVersion') -> dict:
112
"""
113
Compare this version with another version of the same object.
114
115
Args:
116
other (AttackObjectVersion): Other version to compare with
117
118
Returns:
119
dict: Detailed comparison results
120
"""
121
122
def get_field_changes(self, other: 'AttackObjectVersion') -> Dict[str, dict]:
123
"""
124
Get field-level changes between versions.
125
126
Args:
127
other (AttackObjectVersion): Other version to compare with
128
129
Returns:
130
Dict[str, dict]: Field changes with old and new values
131
"""
132
```
133
134
### JSON Encoding for Changes
135
136
Specialized JSON encoder for serializing change data.
137
138
```python { .api }
139
class AttackChangesEncoder(json.JSONEncoder):
140
def default(self, obj):
141
"""
142
Custom JSON encoder for ATT&CK change objects.
143
144
Args:
145
obj: Object to encode
146
147
Returns:
148
Serializable representation of the object
149
"""
150
```
151
152
### CLI Function
153
154
Command-line interface for version comparison workflows.
155
156
```python { .api }
157
def main(args: List[str] = None) -> None:
158
"""
159
CLI entry point for diff_stix command.
160
161
Args:
162
args (List[str], optional): Command-line arguments
163
--old: Path to older version STIX file
164
--new: Path to newer version STIX file
165
--output: Output file path for changelog
166
--format: Output format (json, markdown, html)
167
--filter: Filter by object type
168
--summary-only: Generate summary statistics only
169
"""
170
```
171
172
## Usage Examples
173
174
### Basic Version Comparison
175
176
```python
177
import json
178
from mitreattack.diffStix import DiffStix
179
180
# Load two versions of ATT&CK data
181
with open("enterprise-attack-v14.1.json", "r") as f:
182
old_data = json.load(f)
183
184
with open("enterprise-attack-v15.0.json", "r") as f:
185
new_data = json.load(f)
186
187
# Initialize comparison
188
diff_analyzer = DiffStix(old_data, new_data)
189
190
# Generate changelog
191
changelog = diff_analyzer.generate_changelog()
192
193
# Export to file
194
diff_analyzer.export_changelog("v14.1_to_v15.0_changelog.json", format="json")
195
diff_analyzer.export_changelog("v14.1_to_v15.0_changelog.md", format="markdown")
196
197
print("Changelog generated successfully")
198
```
199
200
### Analyzing Specific Changes
201
202
```python
203
from mitreattack.diffStix import DiffStix
204
import json
205
206
# Load version data
207
with open("old_version.json", "r") as f:
208
old_data = json.load(f)
209
210
with open("new_version.json", "r") as f:
211
new_data = json.load(f)
212
213
diff_analyzer = DiffStix(old_data, new_data)
214
215
# Get added techniques
216
added_techniques = diff_analyzer.get_added_objects("attack-pattern")
217
print(f"Added techniques: {len(added_techniques)}")
218
219
for technique in added_techniques[:5]: # Show first 5
220
print(f" - {technique.get('name', 'Unknown')} ({technique.get('external_references', [{}])[0].get('external_id', 'No ID')})")
221
222
# Get removed groups
223
removed_groups = diff_analyzer.get_removed_objects("intrusion-set")
224
print(f"\\nRemoved groups: {len(removed_groups)}")
225
226
# Get modified software
227
modified_software = diff_analyzer.get_modified_objects("malware")
228
modified_tools = diff_analyzer.get_modified_objects("tool")
229
print(f"\\nModified software: {len(modified_software + modified_tools)}")
230
231
# Relationship changes
232
relationship_changes = diff_analyzer.compare_relationships()
233
print(f"\\nRelationship changes:")
234
print(f" Added: {len(relationship_changes.get('added', []))}")
235
print(f" Removed: {len(relationship_changes.get('removed', []))}")
236
print(f" Modified: {len(relationship_changes.get('modified', []))}")
237
```
238
239
### Summary Statistics and Reporting
240
241
```python
242
from mitreattack.diffStix import DiffStix
243
import json
244
245
# Perform comparison
246
diff_analyzer = DiffStix(old_data, new_data)
247
248
# Generate summary statistics
249
summary = diff_analyzer.generate_summary_statistics()
250
251
print("ATT&CK Version Comparison Summary")
252
print("=" * 40)
253
254
for object_type, changes in summary.items():
255
if isinstance(changes, dict):
256
print(f"{object_type.upper()}:")
257
print(f" Added: {changes.get('added', 0)}")
258
print(f" Removed: {changes.get('removed', 0)}")
259
print(f" Modified: {changes.get('modified', 0)}")
260
print()
261
262
# Export summary as JSON
263
with open("version_comparison_summary.json", "w") as f:
264
json.dump(summary, f, indent=2, cls=AttackChangesEncoder)
265
```
266
267
### Detailed Object Change Analysis
268
269
```python
270
from mitreattack.diffStix import DiffStix, AttackObjectVersion
271
import json
272
273
diff_analyzer = DiffStix(old_data, new_data)
274
275
# Get modified techniques for detailed analysis
276
modified_techniques = diff_analyzer.get_modified_objects("attack-pattern")
277
278
print(f"Analyzing {len(modified_techniques)} modified techniques...")
279
280
for modified_technique in modified_techniques[:3]: # Analyze first 3
281
technique_id = modified_technique.get('external_references', [{}])[0].get('external_id', 'Unknown')
282
technique_name = modified_technique.get('name', 'Unknown')
283
284
print(f"\\nTechnique: {technique_name} ({technique_id})")
285
286
# Get detailed field changes
287
if 'changes' in modified_technique:
288
changes = modified_technique['changes']
289
290
for field, change_info in changes.items():
291
if isinstance(change_info, dict) and 'old' in change_info and 'new' in change_info:
292
old_value = change_info['old']
293
new_value = change_info['new']
294
295
# Truncate long values for display
296
if len(str(old_value)) > 100:
297
old_value = str(old_value)[:97] + "..."
298
if len(str(new_value)) > 100:
299
new_value = str(new_value)[:97] + "..."
300
301
print(f" {field}:")
302
print(f" Old: {old_value}")
303
print(f" New: {new_value}")
304
```
305
306
### CLI Usage for Version Comparison
307
308
```bash
309
# Compare two specific versions
310
diff_stix --old enterprise-attack-v14.1.json --new enterprise-attack-v15.0.json --output changelog.json
311
312
# Generate markdown changelog
313
diff_stix --old old_version.json --new new_version.json --output changelog.md --format markdown
314
315
# Filter by specific object type
316
diff_stix --old old_version.json --new new_version.json --filter attack-pattern --output technique_changes.json
317
318
# Generate summary only
319
diff_stix --old old_version.json --new new_version.json --summary-only --output summary.json
320
321
# Compare with HTML output
322
diff_stix --old old_version.json --new new_version.json --format html --output changes_report.html
323
```
324
325
### Automated Version Tracking Workflow
326
327
```python
328
import json
329
from pathlib import Path
330
from mitreattack.diffStix import DiffStix
331
from mitreattack.download_stix import download_stix, get_available_versions
332
from mitreattack.release_info import STIX20
333
334
def track_attack_evolution(versions: List[str], domain: str = "enterprise-attack"):
335
"""
336
Track ATT&CK evolution across multiple versions.
337
"""
338
download_dir = Path("./version_tracking/")
339
download_dir.mkdir(exist_ok=True)
340
341
# Download all requested versions
342
version_files = {}
343
for version in versions:
344
file_path = download_stix(
345
stix_version="2.0",
346
domain=domain,
347
download_dir=str(download_dir),
348
release=version,
349
known_hash=STIX20[version]
350
)
351
version_files[version] = file_path
352
353
# Compare consecutive versions
354
evolution_report = {"comparisons": []}
355
356
for i in range(len(versions) - 1):
357
old_version = versions[i]
358
new_version = versions[i + 1]
359
360
print(f"Comparing {old_version} -> {new_version}")
361
362
# Load data
363
with open(version_files[old_version], "r") as f:
364
old_data = json.load(f)
365
with open(version_files[new_version], "r") as f:
366
new_data = json.load(f)
367
368
# Perform comparison
369
diff_analyzer = DiffStix(old_data, new_data)
370
summary = diff_analyzer.generate_summary_statistics()
371
372
comparison_result = {
373
"from_version": old_version,
374
"to_version": new_version,
375
"summary": summary
376
}
377
evolution_report["comparisons"].append(comparison_result)
378
379
# Export detailed changelog
380
changelog_file = download_dir / f"{old_version}_to_{new_version}_changelog.json"
381
diff_analyzer.export_changelog(str(changelog_file), format="json")
382
383
# Save evolution report
384
with open(download_dir / "evolution_report.json", "w") as f:
385
json.dump(evolution_report, f, indent=2)
386
387
print(f"Evolution tracking complete. Reports saved to {download_dir}")
388
return evolution_report
389
390
# Track evolution across recent versions
391
recent_versions = ["13.1", "14.0", "14.1", "15.0", "15.1"]
392
evolution_data = track_attack_evolution(recent_versions)
393
394
# Print summary
395
for comparison in evolution_data["comparisons"]:
396
from_ver = comparison["from_version"]
397
to_ver = comparison["to_version"]
398
summary = comparison["summary"]
399
400
print(f"\\n{from_ver} -> {to_ver}:")
401
402
total_added = sum(stats.get("added", 0) for stats in summary.values() if isinstance(stats, dict))
403
total_modified = sum(stats.get("modified", 0) for stats in summary.values() if isinstance(stats, dict))
404
total_removed = sum(stats.get("removed", 0) for stats in summary.values() if isinstance(stats, dict))
405
406
print(f" Total changes: +{total_added} / ~{total_modified} / -{total_removed}")
407
```
408
409
### Custom Change Analysis
410
411
```python
412
from mitreattack.diffStix import DiffStix
413
import json
414
415
def analyze_technique_changes(old_data: dict, new_data: dict):
416
"""
417
Custom analysis focusing on technique changes.
418
"""
419
diff_analyzer = DiffStix(old_data, new_data)
420
421
# Get technique changes
422
added_techniques = diff_analyzer.get_added_objects("attack-pattern")
423
modified_techniques = diff_analyzer.get_modified_objects("attack-pattern")
424
removed_techniques = diff_analyzer.get_removed_objects("attack-pattern")
425
426
analysis_results = {
427
"new_techniques": [],
428
"updated_techniques": [],
429
"deprecated_techniques": [],
430
"platform_changes": {},
431
"tactic_changes": {}
432
}
433
434
# Analyze added techniques
435
for technique in added_techniques:
436
tech_id = technique.get('external_references', [{}])[0].get('external_id', 'Unknown')
437
tech_name = technique.get('name', 'Unknown')
438
platforms = technique.get('x_mitre_platforms', [])
439
440
analysis_results["new_techniques"].append({
441
"id": tech_id,
442
"name": tech_name,
443
"platforms": platforms
444
})
445
446
# Analyze modified techniques for platform changes
447
for technique in modified_techniques:
448
if 'changes' in technique and 'x_mitre_platforms' in technique['changes']:
449
tech_id = technique.get('external_references', [{}])[0].get('external_id', 'Unknown')
450
old_platforms = technique['changes']['x_mitre_platforms'].get('old', [])
451
new_platforms = technique['changes']['x_mitre_platforms'].get('new', [])
452
453
added_platforms = set(new_platforms) - set(old_platforms)
454
removed_platforms = set(old_platforms) - set(new_platforms)
455
456
if added_platforms or removed_platforms:
457
analysis_results["platform_changes"][tech_id] = {
458
"added_platforms": list(added_platforms),
459
"removed_platforms": list(removed_platforms)
460
}
461
462
return analysis_results
463
464
# Perform custom analysis
465
with open("old_attack.json", "r") as f:
466
old_data = json.load(f)
467
with open("new_attack.json", "r") as f:
468
new_data = json.load(f)
469
470
custom_analysis = analyze_technique_changes(old_data, new_data)
471
472
print(f"New techniques: {len(custom_analysis['new_techniques'])}")
473
print(f"Platform changes: {len(custom_analysis['platform_changes'])}")
474
475
# Show platform changes
476
for tech_id, changes in custom_analysis["platform_changes"].items():
477
if changes["added_platforms"]:
478
print(f"{tech_id}: Added platforms: {', '.join(changes['added_platforms'])}")
479
if changes["removed_platforms"]:
480
print(f"{tech_id}: Removed platforms: {', '.join(changes['removed_platforms'])}")
481
```