or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

cli-tools.mdcollections.mdexcel-export.mdindex.mdnavigation-layers.mdstix20-data-access.mdversion-comparison.mdversion-management.md

version-comparison.mddocs/

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

```