Skip to content
This repository was archived by the owner on Dec 11, 2023. It is now read-only.

Commit cb8a8df

Browse files
committed
add unchanged option to diff_stix
1 parent 605b348 commit cb8a8df

File tree

2 files changed

+35
-7
lines changed

2 files changed

+35
-7
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
# v1.9.0 - 23 March 2021
2+
## Improvements
3+
- [diff_stix.py](scripts/diff_stix.py) now supports an `--unchanged` argument which adds a section listing objects which did _not_ change between releases.
4+
## Fixes
5+
- [diff_stix.py](scripts/diff_stix.py) should no longer crash when exporting layers but with only one domain specified.
6+
17
# v1.8.1 - 3 March 2021
28
## Fixes
39
- Layer library should no longer throw errors when it encounters the "Network" and "PRE" platforms.

scripts/diff_stix.py

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,23 +37,26 @@
3737
"minor_changes": "Minor {obj_type} changes",
3838
"deprecations": "{obj_type} deprecations",
3939
"revocations": "{obj_type} revocations",
40-
"deletions": "{obj_type} deletions"
40+
"deletions": "{obj_type} deletions",
41+
"unchanged": "Unchanged {obj_type}"
4142
}
4243
statusToColor = { # color key for layers
4344
"additions": "#a1d99b",
4445
"changes": "#fcf3a2",
4546
"minor_changes": "#c7c4e0",
4647
"deletions": "#ff00e1", # this will probably never show up but just in case
4748
"revocations": "#ff9000",
48-
"deprecations": "#ff6363"
49+
"deprecations": "#ff6363",
50+
"unchanged": "#ffffff"
4951
}
5052
statusDescriptions = { # explanation of modification types to data objects for legend in layer files
5153
"additions": "objects which are present in the new data and not the old",
5254
"changes": "objects which have a newer version number in the new data compared to the old",
5355
"minor_changes": "objects which have a newer last edit date in the new data than in the old, but the same version number",
5456
"revocations": "objects which are revoked in the new data but not in the old",
5557
"deprecations": "objects which are deprecated in the new data but not in the old",
56-
"deletions": "objects which are present in the old data but not the new"
58+
"deletions": "objects which are present in the old data but not the new",
59+
"unchanged": "objects which did not change between the two versions"
5760
}
5861

5962
class DiffStix(object):
@@ -66,6 +69,7 @@ def __init__(
6669
layers=None,
6770
markdown=None,
6871
minor_changes=False,
72+
unchanged=False,
6973
new='new',
7074
old='old',
7175
show_key=False,
@@ -93,6 +97,7 @@ def __init__(
9397
self.layers = layers
9498
self.markdown = markdown
9599
self.minor_changes = minor_changes
100+
self.unchanged = unchanged
96101
self.new = new
97102
self.old = old
98103
self.show_key = show_key
@@ -109,7 +114,8 @@ def __init__(
109114
# changes: [],
110115
# minor_changes: [],
111116
# revocations: [],
112-
# deprecations: []
117+
# deprecations: [],
118+
# unchanged: []
113119
# }
114120
# mobile-attack...
115121
# }
@@ -236,6 +242,7 @@ def load_taxii(new=False):
236242
minor_changes = set()
237243
revocations = set()
238244
deprecations = set()
245+
unchanged = set()
239246

240247
# find changes, revocations and deprecations
241248
for key in intersection:
@@ -281,6 +288,8 @@ def load_taxii(new=False):
281288
new_date = dateparser.parse(new["id_to_obj"][key]["modified"])
282289
if new_date > old_date:
283290
minor_changes.add(key)
291+
else :
292+
unchanged.add(key)
284293

285294
# set data
286295
if obj_type not in self.data: self.data[obj_type] = {}
@@ -291,6 +300,11 @@ def load_taxii(new=False):
291300
# only create minor_changes data if we want to display it later
292301
if self.minor_changes:
293302
self.data[obj_type][domain]["minor_changes"] = [new["id_to_obj"][key] for key in minor_changes]
303+
304+
# ditto for unchanged
305+
if self.unchanged:
306+
self.data[obj_type][domain]["unchanged"] = [new["id_to_obj"][key] for key in unchanged]
307+
294308
self.data[obj_type][domain]["revocations"] = [new["id_to_obj"][key] for key in revocations]
295309
self.data[obj_type][domain]["deprecations"] = [new["id_to_obj"][key] for key in deprecations]
296310
# only show deletions if objects were deleted
@@ -324,6 +338,8 @@ def get_md_key(self):
324338
)
325339
if self.minor_changes:
326340
key += "* Minor object changes: " + statusDescriptions['minor_changes'] + "\n"
341+
if self.unchanged:
342+
key += "* Unchanged objects: " + statusDescriptions['unchanged'] + "\n"
327343
key += (
328344
"* Object revocations: " + statusDescriptions['revocations'] + "\n"
329345
"* Object deprecations: " + statusDescriptions['deprecations']
@@ -474,7 +490,7 @@ def get_layers_dict(self):
474490
"tactic": phase['phase_name'],
475491
"enabled": True,
476492
"color": statusToColor[status],
477-
"comment": status[:-1] # trim s off end of word
493+
"comment": status[:-1] if status != "unchanged" else status # trim s off end of word
478494
})
479495
used_statuses.add(status)
480496

@@ -528,8 +544,8 @@ def layers_dict_to_files(outfiles, layers):
528544
verboseprint("writing layers dict to layer files... ", end="", flush="true")
529545

530546
# write each layer to separate files
531-
json.dump(layers['enterprise-attack'], open(outfiles[0], "w"), indent=4)
532-
json.dump(layers['mobile-attack'], open(outfiles[1], "w"), indent=4)
547+
if 'enterprise-attack' in layers: json.dump(layers['enterprise-attack'], open(outfiles[0], "w"), indent=4)
548+
if 'mobile-attack' in layers: json.dump(layers['mobile-attack'], open(outfiles[1], "w"), indent=4)
533549

534550
verboseprint("done")
535551

@@ -621,6 +637,11 @@ def layers_dict_to_files(outfiles, layers):
621637
help="show changes to objects which didn't increment the version number"
622638
)
623639

640+
parser.add_argument("--unchanged",
641+
action="store_true",
642+
help="show objects without changes in the markdown output"
643+
)
644+
624645
parser.add_argument("--use-taxii",
625646
action="store_true",
626647
help="Use content from the ATT&CK TAXII server for the -old data"
@@ -648,6 +669,7 @@ def layers_dict_to_files(outfiles, layers):
648669
layers=args.layers,
649670
markdown=args.markdown,
650671
minor_changes=args.minor_changes,
672+
unchanged=args.unchanged,
651673
new=args.new,
652674
old=args.old,
653675
show_key=args.show_key,

0 commit comments

Comments
 (0)