Skip to content
This repository was archived by the owner on Dec 11, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# v1.7 - 27 October 2020
## Improvements
- Removed pre-ATT&CK domain from scripts to support migration of that content to enterprise tactics. See issue [#36](https://github.com/mitre-attack/attack-scripts/issues/36).
- Updated scripts which produce layers to use v4.0 of the Navigator Layer Format. See issue [#47](https://github.com/mitre-attack/attack-scripts/issues/47).

# v1.6 - 5 October 2020
## Improvements
- Added [layer to SVG](https://github.com/mitre-attack/attack-scripts/tree/master/layers#to_svgpy) converter. See issue [#1](https://github.com/mitre-attack/attack-scripts/issues/1).
Expand Down
33 changes: 21 additions & 12 deletions layers/README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
# layers

This folder contains modules and scripts for working with ATT&CK Navigator layers. ATT&CK Navigator Layers are a set of annotations overlayed on top of the ATT&CK Matrix. For more about ATT&CK Navigator layers, visit the ATT&CK Navigator repository. The core module allows users to load, validate, manipulate, and save ATT&CK layers. A brief overview of the components can be found below. All scripts adhere to the MITRE ATT&CK Navigator Layer file format, [version 3.0](https://github.com/mitre-attack/attack-navigator/blob/develop/layers/LAYERFORMATv3.md).
This folder contains modules and scripts for working with ATT&CK Navigator layers. ATT&CK Navigator Layers are a set of annotations overlayed on top of the ATT&CK Matrix. For more about ATT&CK Navigator layers, visit the ATT&CK Navigator repository. The core module allows users to load, validate, manipulate, and save ATT&CK layers. A brief overview of the components can be found below. All scripts adhere to the MITRE ATT&CK Navigator Layer file format, [version 4.0](https://github.com/mitre-attack/attack-navigator/blob/develop/layers/LAYERFORMATv4.md), but will accept legacy [version 3.0](https://github.com/mitre-attack/attack-navigator/blob/develop/layers/LAYERFORMATv3.md) layers, upgrading them to version 4.

#### Core Modules
| script | description |
|:-------|:------------|
| [filter](core/filter.py) | Implements a basic [filter object](https://github.com/mitre-attack/attack-navigator/blob/develop/layers/LAYERFORMATv3.md#filter-object-properties). |
| [gradient](core/gradient.py) | Implements a basic [gradient object](https://github.com/mitre-attack/attack-navigator/blob/develop/layers/LAYERFORMATv3.md#gradient-object-properties). |
| [filter](core/filter.py) | Implements a basic [filter object](https://github.com/mitre-attack/attack-navigator/blob/develop/layers/LAYERFORMATv4.md#filter-object-properties). |
| [gradient](core/gradient.py) | Implements a basic [gradient object](https://github.com/mitre-attack/attack-navigator/blob/develop/layers/LAYERFORMATv4.md#gradient-object-properties). |
| [layer](core/layer.py) | Provides an interface for interacting with core module's layer representation. A further breakdown can be found in the corresponding [section](#Layer) below. |
| [layout](core/layout.py) | Implements a basic [layout object](https://github.com/mitre-attack/attack-navigator/blob/develop/layers/LAYERFORMATv3.md#layout-object-properties). |
| [legenditem](core/legenditem.py) | Implements a basic [legenditem object](https://github.com/mitre-attack/attack-navigator/blob/develop/layers/LAYERFORMATv3.md#legenditem-object-properties). |
| [metadata](core/metadata.py) | Implements a basic [metadata object](https://github.com/mitre-attack/attack-navigator/blob/develop/layers/LAYERFORMATv3.md#metadata-object-properties). |
| [technique](core/technique.py) | Implements a basic [technique object](https://github.com/mitre-attack/attack-navigator/blob/develop/layers/LAYERFORMATv3.md#technique-object-properties). |

| [layout](core/layout.py) | Implements a basic [layout object](https://github.com/mitre-attack/attack-navigator/blob/develop/layers/LAYERFORMATv4.md#layout-object-properties). |
| [legenditem](core/legenditem.py) | Implements a basic [legenditem object](https://github.com/mitre-attack/attack-navigator/blob/develop/layers/LAYERFORMATv4.md#legenditem-object-properties). |
| [metadata](core/metadata.py) | Implements a basic [metadata object](https://github.com/mitre-attack/attack-navigator/blob/develop/layers/LAYERFORMATv4.md#metadata-object-properties). |
| [technique](core/technique.py) | Implements a basic [technique object](https://github.com/mitre-attack/attack-navigator/blob/develop/layers/LAYERFORMATv4.md#technique-object-properties). |
| [versions](core/versions.py) | Impelments a basic [versions object](https://github.com/mitre-attack/attack-navigator/blob/develop/layers/LAYERFORMATv4.md#versions-object-properties).|
#### Manipulator Scripts
| script | description |
|:-------|:------------|
Expand All @@ -36,7 +36,7 @@ This folder contains modules and scripts for working with ATT&CK Navigator layer
| [layerExporter_cli.py](layerExporter_cli.py) | A commandline utility to export Layer files to excel or svg formats using the exporter tools. Run with `-h` for usage. |

## Layer
The Layer class provides format validation and read/write capabilities to aid in working with ATT&CK Navigator Layers in python. It is the primary interface through which other Layer-related classes defined in the core module should be used. The Layer class API and a usage example are below.
The Layer class provides format validation and read/write capabilities to aid in working with ATT&CK Navigator Layers in python. It is the primary interface through which other Layer-related classes defined in the core module should be used. The Layer class API and a usage example are below. The class currently supports version 3 and 4 of the ATT&CK Layer spec, and will upgrade version 3 layers into compatible version 4 ones whenever possible.

| method [x = Layer()]| description |
|:-------|:------------|
Expand All @@ -50,22 +50,31 @@ The Layer class provides format validation and read/write capabilities to aid in
#### Example Usage

```python
example_layer_dict = {
example_layer3_dict = {
"name": "example layer",
"version": "3.0",
"domain": "mitre-enterprise"
}

example_layer4_dict = {
"name": "layer v4 example",
"versions" : {
"layer" : "4.0",
"navigator": "4.0"
},
"domain": "enterprise-attack"
}

example_layer_location = "/path/to/layer/file.json"
example_layer_out_location = "/path/to/new/layer/file.json"

from layers.core import Layer

layer1 = Layer(example_layer_dict) # Create a new layer and load existing data
layer1 = Layer(example_layer3_dict) # Create a new layer and load existing data
layer1.to_file(example_layer_out_location) # Write out the loaded layer to the specified file

layer2 = Layer() # Create a new layer object
layer2.from_dict(example_layer_dict) # Load layer data into existing layer object
layer2.from_dict(example_layer4_dict) # Load layer data into existing layer object
print(layer2.to_dict()) # Retrieve the loaded layer's data as a dictionary, and print it

layer3 = Layer() # Create a new layer object
Expand Down
20 changes: 20 additions & 0 deletions layers/core/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ class UnknownTechniqueProperty(Exception):
pass


class MissingParameters(Exception):
pass

def handler(caller, msg):
"""
Prints a debug/warning/error message
Expand Down Expand Up @@ -85,3 +88,20 @@ def categoryChecker(caller, testee, valid, field):
if testee not in valid:
handler(caller, '{} not a valid value for {}'.format(testee, field))
raise BadInput

def loadChecker(caller, testee, required, field):
"""
Verifies that the tested object contains all required fields
:param caller: the entity that called this function (used for error
messages)
:param testee: the element to test
:param requireds: a list of required values for the testee
:param field: what the element is to be used as (used for error
messages)
:raises BadInput: error denoting the testee element is not one of
the valid options
"""
for entry in required:
if entry not in testee:
handler(caller, '{} is not present in {} [{}]'.format(entry, field, testee))
raise MissingParameters
37 changes: 21 additions & 16 deletions layers/core/filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,17 @@


class Filter:
def __init__(self, domain="mitre-enterprise"):
def __init__(self, domain="enterprise-attack"):
"""
Initialization - Creates a filter object, with an optional
domain input

:param domain: The domain used for this layer (mitre-enterprise
or mitre-mobile)
"""
self.__stages = UNSETVALUE
self.domain = domain
self.__platforms = UNSETVALUE

@property
def stages(self):
if self.__stages != UNSETVALUE:
return self.__stages

@stages.setter
def stages(self, stage):
typeCheckerArray(type(self).__name__, stage, str, "stage")
categoryChecker(type(self).__name__, stage[0], ["act", "prepare"],
"stages")
self.__stages = stage

@property
def platforms(self):
if self.__platforms != UNSETVALUE:
Expand Down Expand Up @@ -59,7 +46,25 @@ def get_dict(self):
if entry == 'domain':
continue
if listing[entry] != UNSETVALUE:
temp[entry.split(type(self).__name__ + '__')[-1]] \
= listing[entry]
subname = entry.split('__')[-1]
if subname != 'stages':
temp[subname] = listing[entry]
if len(temp) > 0:
return temp

class Filterv3(Filter):
def __init__(self, domain="mitre-enterprise"):
self.__stages = UNSETVALUE
super().__init__(domain)

@property
def stages(self):
if self.__stages != UNSETVALUE:
return self.__stages

@stages.setter
def stages(self, stage):
typeCheckerArray(type(self).__name__, stage, str, "stage")
categoryChecker(type(self).__name__, stage[0], ["act", "prepare"],
"stages")
self.__stages = stage
5 changes: 2 additions & 3 deletions layers/core/layer.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,7 @@ def _build(self):
Loads the data stored in self.data into a LayerObj (self.layer)
"""
try:
self.__layer = _LayerObj(self._data['version'], self._data['name'],
self._data['domain'])
self.__layer = _LayerObj(self._data['name'], self._data['domain'])
except BadType or BadInput as e:
handler(type(self).__name__, 'Layer is malformed: {}. '
'Unable to load.'.format(e))
Expand All @@ -89,7 +88,7 @@ def _build(self):
return

for key in self._data:
if key not in ['version', 'name', 'domain']:
if key not in ['name', 'domain']:
try:
self.__layer._linker(key, self._data[key])
except Exception as e:
Expand Down
Loading