From 1bb0d66594cd44005f4a567c452ddecde84d71b8 Mon Sep 17 00:00:00 2001 From: Charissa Miller <48832936+clemiller@users.noreply.github.com> Date: Fri, 27 Oct 2023 12:01:17 -0400 Subject: [PATCH 1/4] update layer version to 4.5 --- mitreattack/navlayers/core/versions.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mitreattack/navlayers/core/versions.py b/mitreattack/navlayers/core/versions.py index 269323db..138ac36f 100644 --- a/mitreattack/navlayers/core/versions.py +++ b/mitreattack/navlayers/core/versions.py @@ -60,15 +60,15 @@ def layer(self, layer): """Setter for layer.""" typeChecker(type(self).__name__, layer, str, "layer") try: - categoryChecker(type(self).__name__, layer, ["3.0", "4.0", "4.1", "4.2", "4.3", "4.4"], "layer version") + categoryChecker(type(self).__name__, layer, ["3.0", "4.0", "4.1", "4.2", "4.3", "4.4", "4.5"], "layer version") except BadInput: print( - f"[WARNING] - unrecognized layer version {layer}. Defaulting to the 4.4 schema, this may result in " + f"[WARNING] - unrecognized layer version {layer}. Defaulting to the 4.5 schema, this may result in " f"unexpected behavior." ) - if layer in ["3.0", "4.0", "4.1", "4.2", "4.3"]: - print(f"[NOTICE] - Forcibly upgrading version from {layer} to 4.4.") - layer = "4.4" + if layer in ["3.0", "4.0", "4.1", "4.2", "4.3", "4.4"]: + print(f"[NOTICE] - Forcibly upgrading version from {layer} to 4.5.") + layer = "4.5" self.__layer = layer def get_dict(self): From 67bf976bfda428a6c09f733a4b636dd362effb36 Mon Sep 17 00:00:00 2001 From: Charissa Miller <48832936+clemiller@users.noreply.github.com> Date: Fri, 27 Oct 2023 12:01:33 -0400 Subject: [PATCH 2/4] update default versions --- mitreattack/navlayers/core/versions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mitreattack/navlayers/core/versions.py b/mitreattack/navlayers/core/versions.py index 138ac36f..19bf212b 100644 --- a/mitreattack/navlayers/core/versions.py +++ b/mitreattack/navlayers/core/versions.py @@ -2,7 +2,7 @@ from mitreattack.navlayers.core.exceptions import typeChecker, categoryChecker, UNSETVALUE, BadInput -defaults = dict(layer="4.3", navigator="4.5.5") +defaults = dict(layer="4.5", navigator="4.9.0") class Versions: From e0094f8122d55252cef11737e8f6453c2cf77b7d Mon Sep 17 00:00:00 2001 From: Charissa Miller <48832936+clemiller@users.noreply.github.com> Date: Fri, 27 Oct 2023 12:11:54 -0400 Subject: [PATCH 3/4] support for "selectVisibleTechniques" --- mitreattack/navlayers/core/README.md | 1 + mitreattack/navlayers/core/layerobj.py | 18 ++++++++++++++++++ tests/resources/testing_data.py | 20 +++++++++++++++----- 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/mitreattack/navlayers/core/README.md b/mitreattack/navlayers/core/README.md index 98e101b2..d1aae28e 100644 --- a/mitreattack/navlayers/core/README.md +++ b/mitreattack/navlayers/core/README.md @@ -171,6 +171,7 @@ in the source code for them. _LayerObj().tacticRowBackground # Color code for tactic background _LayerObj().selectTechniquesAcrossTactics # Bool determining whether or not to select cross-tactic _LayerObj().selectSubtechniquesWithParent # Bool determining whether or not to select subtechniques + _LayerObj().selectVisibleTechniques # Bool determining whether or not to select only visible techniques _LayerObj().metadata # List of links to Metadata items _LayerObj().get_dict() # Export Layer as a dictionary object ``` diff --git a/mitreattack/navlayers/core/layerobj.py b/mitreattack/navlayers/core/layerobj.py index 97cf0e13..f9ca3ed4 100644 --- a/mitreattack/navlayers/core/layerobj.py +++ b/mitreattack/navlayers/core/layerobj.py @@ -45,6 +45,7 @@ def __init__(self, name, domain): self.__tacticRowBackground = UNSETVALUE self.__selectTechniquesAcrossTactics = UNSETVALUE self.__selectSubtechniquesWithParent = UNSETVALUE + self.__selectVisibleTechniques = UNSETVALUE self.__metadata = UNSETVALUE self.__links = UNSETVALUE @@ -300,6 +301,17 @@ def selectSubtechniquesWithParent(self, selectSubtechniquesWithParent): typeChecker(type(self).__name__, selectSubtechniquesWithParent, bool, "selectSubtechniquesWithParent") self.__selectSubtechniquesWithParent = selectSubtechniquesWithParent + @property + def selectVisibleTechniques(self): + """Getter for selectVisibleTechniques.""" + if self.__selectVisibleTechniques != UNSETVALUE: + return self.__selectVisibleTechniques + + @selectVisibleTechniques.setter + def selectVisibleTechniques(self, selectVisibleTechniques): + typeChecker(type(self).__name__, selectVisibleTechniques, bool, "selectVisibleTechniques") + self.__selectVisibleTechniques = selectVisibleTechniques + @property def metadata(self): """Getter for metadata.""" @@ -389,6 +401,8 @@ def _enumerate(self): temp.append("selectTechniquesAcrossTactics") if self.selectSubtechniquesWithParent: temp.append("selectSubtechniquesWithParent") + if self.selectVisibleTechniques: + temp.append("selectVisibleTechniques") if self.metadata: temp.append("metadata") return temp @@ -426,6 +440,8 @@ def get_dict(self): temp["selectTechniquesAcrossTactics"] = self.selectTechniquesAcrossTactics if self.selectSubtechniquesWithParent is not None: temp["selectSubtechniquesWithParent"] = self.selectSubtechniquesWithParent + if self.selectVisibleTechniques is not None: + temp["selectVisibleTechniques"] = self.selectVisibleTechniques if self.metadata: temp["metadata"] = [x.get_dict() for x in self.metadata] return temp @@ -470,6 +486,8 @@ def _linker(self, field, data): self.selectTechniquesAcrossTactics = data elif field == "selectSubtechniquesWithParent": self.selectSubtechniquesWithParent = data + elif field == "selectVisibleTechniques": + self.selectVisibleTechniques = data elif field == "metadata": self.metadata = data elif field == "links": diff --git a/tests/resources/testing_data.py b/tests/resources/testing_data.py index c9e2b7e2..a9e89a87 100644 --- a/tests/resources/testing_data.py +++ b/tests/resources/testing_data.py @@ -39,6 +39,7 @@ "tacticRowBackground": "#dddddd", "selectTechniquesAcrossTactics": True, "selectSubtechniquesWithParent": False, + "selectVisibleTechniques": False, } agg_layer_1 = """ { @@ -257,7 +258,8 @@ "showTacticRowBackground": false, "tacticRowBackground": "#dddddd", "selectTechniquesAcrossTactics": true, - "selectSubtechniquesWithParent": false + "selectSubtechniquesWithParent": false, + "selectVisibleTechniques": false, }""" agg_layer_6 = """ @@ -343,7 +345,8 @@ "showTacticRowBackground": false, "tacticRowBackground": "#dddddd", "selectTechniquesAcrossTactics": true, - "selectSubtechniquesWithParent": false + "selectSubtechniquesWithParent": false, + "selectVisibleTechniques": false, } """ @@ -472,6 +475,7 @@ "tacticRowBackground": "#dddddd", "selectTechniquesAcrossTactics": false, "selectSubtechniquesWithParent": false, + "selectVisibleTechniques": false, "metadata": [ { "name": "layer metadata 1", @@ -821,7 +825,8 @@ "showTacticRowBackground": false, "tacticRowBackground": "#4400ff", "selectTechniquesAcrossTactics": false, - "selectSubtechniquesWithParent": false + "selectSubtechniquesWithParent": false, + "selectVisibleTechniques": false, } """ @@ -7361,7 +7366,8 @@ "showTacticRowBackground": false, "tacticRowBackground": "#dddddd", "selectTechniquesAcrossTactics": true, - "selectSubtechniquesWithParent": false + "selectSubtechniquesWithParent": false, + "selectVisibleTechniques": false, }""" example_layer_v41 = """ @@ -7446,6 +7452,7 @@ "tacticRowBackground": "#dddddd", "selectTechniquesAcrossTactics": false, "selectSubtechniquesWithParent": false, + "selectVisibleTechniques": false, "metadata": [ { "name": "layer metadata 1", @@ -7590,7 +7597,8 @@ "showTacticRowBackground": false, "tacticRowBackground": "#dddddd", "selectTechniquesAcrossTactics": true, - "selectSubtechniquesWithParent": false + "selectSubtechniquesWithParent": false, + "selectVisibleTechniques": false, }""" example_layer_v3_dict = { @@ -7699,6 +7707,7 @@ "tacticRowBackground": "#dddddd", "selectTechniquesAcrossTactics": False, "selectSubtechniquesWithParent": False, + "selectVisibleTechniques": False, "metadata": [ {"name": "layer metadata 1", "value": "layer metadata 1 value"}, {"divider": True}, @@ -7759,6 +7768,7 @@ "tacticRowBackground": "#dddddd", "selectTechniquesAcrossTactics": False, "selectSubtechniquesWithParent": False, + "selectVisibleTechniques": False, "metadata": [ {"name": "layer metadata 1", "value": "layer metadata 1 value"}, {"name": "layer metadata 2", "value": "layer metadata 2 value"}, From 2fdab4cef962a01ab4e4f01b477ecf59a163c984 Mon Sep 17 00:00:00 2001 From: Charissa Miller <48832936+clemiller@users.noreply.github.com> Date: Fri, 27 Oct 2023 12:20:49 -0400 Subject: [PATCH 4/4] support for "expandedSubtechniques" --- mitreattack/navlayers/core/README.md | 6 +++++- mitreattack/navlayers/core/layerobj.py | 2 ++ mitreattack/navlayers/core/layout.py | 12 ++++++++++++ tests/resources/testing_data.py | 21 +++++++++++++++------ 4 files changed, 34 insertions(+), 7 deletions(-) diff --git a/mitreattack/navlayers/core/README.md b/mitreattack/navlayers/core/README.md index d1aae28e..cc2875cf 100644 --- a/mitreattack/navlayers/core/README.md +++ b/mitreattack/navlayers/core/README.md @@ -49,7 +49,8 @@ layer_example.layer.layout = dict(layout="side", showName=True, showAggregateScores=True, countUnscored=True, - aggregateFunction="sum") # average, sum, max, min + aggregateFunction="sum", # average, sum, max, min + expandedSubtechniques="annotated") # all, annotated, none # configure whether or not to hide disabled techniques layer_example.layer.hideDisabled = True @@ -102,6 +103,7 @@ layout_obj.layout = "side" layout_obj.showID = True layout_obj.showName = True layout_obj.showAggregateScores = True +layout_obj.expandedSubtechniques = "annotated" layout_obj.countUnscored = True layout_obj.aggregateFunction = "sum" # average, sum, max, min layer_build.layout = layout_obj @@ -199,6 +201,8 @@ for backwards compatibility reasons. Layout().countUnscored # Bool denoting whether ot not to count unscored techniques as 0s for Aggregates Layout().aggregateFunction # A enum integer denoting which aggregate function to utilize # 1 - Average, 2 - min, 3 - max, 4 - sum + Layout().expandedSubtechniques # String denoting how to display sub-techniques in the layer + # "all" - expand all sub-techniques, "annotated" - expand only annotated sub-techniques, "none" - collapse all sub-techniques Layout().get_dict() # Export Layout data as a dictionary object Layout().compute_aggregate() # Compute the aggregate score for a technique and it's subtechniques ``` diff --git a/mitreattack/navlayers/core/layerobj.py b/mitreattack/navlayers/core/layerobj.py index f9ca3ed4..48deae4c 100644 --- a/mitreattack/navlayers/core/layerobj.py +++ b/mitreattack/navlayers/core/layerobj.py @@ -180,6 +180,8 @@ def layout(self, layout): temp.countUnscored = layout["countUnscored"] if "aggregateFunction" in layout: temp.aggregateFunction = layout["aggregateFunction"] + if "expandedSubtechniques" in layout: + temp.expandedSubtechniques = layout["expandedSubtechniques"] self.__layout = temp @property diff --git a/mitreattack/navlayers/core/layout.py b/mitreattack/navlayers/core/layout.py index 217eebed..0bd9216c 100644 --- a/mitreattack/navlayers/core/layout.py +++ b/mitreattack/navlayers/core/layout.py @@ -25,6 +25,7 @@ def __init__(self): self.__showAggregateScores = UNSETVALUE self.__countUnscored = UNSETVALUE self.__aggregateFunction = Aggregates.average + self.__expandedSubtechniques = UNSETVALUE def compute_aggregate(self, technique, subtechniques): """Compute the aggregate score for a technique and any subtechniques. @@ -111,6 +112,17 @@ def showName(self, showName): typeChecker(type(self).__name__, showName, bool, "showName") self.__showName = showName + @property + def expandedSubtechniques(self): + """Getter for expandedSubtechniques.""" + if self.__expandedSubtechniques != UNSETVALUE: + return self.__expandedSubtechniques + + @expandedSubtechniques.setter + def expandedSubtechniques(self, expandedSubtechniques): + typeChecker(type(self).__name__, expandedSubtechniques, bool, "expandedSubtechniques") + self.__expandedSubtechniques = expandedSubtechniques + @property def showAggregateScores(self): """Getter for showAggregateScores.""" diff --git a/tests/resources/testing_data.py b/tests/resources/testing_data.py index a9e89a87..488545b8 100644 --- a/tests/resources/testing_data.py +++ b/tests/resources/testing_data.py @@ -28,6 +28,7 @@ "showName": True, "showAggregateScores": False, "countUnscored": False, + "expandedSubtechniques": "annotated", }, "hideDisabled": False, "techniques": [], @@ -56,7 +57,8 @@ "layout": "side", "showID": false, "showName": true, - "showAggregateScores": true + "showAggregateScores": true, + "expandedSubtechniques": "annotated", }, "techniques": [ { @@ -104,7 +106,8 @@ "showID": false, "showName": true, "showAggregateScores": true, - "aggregateFunction": "min" + "aggregateFunction": "min", + "expandedSubtechniques": "annotated", }, "techniques": [ { @@ -153,7 +156,8 @@ "showID": false, "showName": true, "showAggregateScores": true, - "aggregateFunction": "min" + "aggregateFunction": "min", + "expandedSubtechniques": "annotated", }, "techniques": [ { @@ -217,7 +221,8 @@ "showID": false, "showName": true, "showAggregateScores": true, - "countUnscored": false + "countUnscored": false, + "expandedSubtechniques": "annotated", }, "hideDisabled": false, "techniques": [ @@ -294,7 +299,8 @@ "showID": false, "showName": true, "showAggregateScores": true, - "countUnscored": false + "countUnscored": false, + "expandedSubtechniques": "annotated", }, "hideDisabled": false, "techniques": [ @@ -366,7 +372,8 @@ "showID": false, "showName": true, "showAggregateScores": true, - "aggregateFunction": "average" + "aggregateFunction": "average", + "expandedSubtechniques": "annotated", }, "techniques": [ { @@ -7662,6 +7669,7 @@ "showAggregateScores": True, "countUnscored": True, "aggregateFunction": "average", + "expandedSubtechniques": "annotated", }, "hideDisabled": False, "techniques": [ @@ -7730,6 +7738,7 @@ "showAggregateScores": True, "countUnscored": True, "aggregateFunction": "average", + "expandedSubtechniques": "annotated", }, "hideDisabled": False, "techniques": [