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

Commit cf8292c

Browse files
author
clittle
committed
Updated to full v4 spec, improved handling for load errors
1 parent c18ab47 commit cf8292c

5 files changed

Lines changed: 97 additions & 74 deletions

File tree

layers/core/exceptions.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ class UnknownTechniqueProperty(Exception):
2121
pass
2222

2323

24+
class MissingParameters(Exception):
25+
pass
26+
2427
def handler(caller, msg):
2528
"""
2629
Prints a debug/warning/error message
@@ -85,3 +88,20 @@ def categoryChecker(caller, testee, valid, field):
8588
if testee not in valid:
8689
handler(caller, '{} not a valid value for {}'.format(testee, field))
8790
raise BadInput
91+
92+
def loadChecker(caller, testee, required, field):
93+
"""
94+
Verifies that the tested object contains all required fields
95+
:param caller: the entity that called this function (used for error
96+
messages)
97+
:param testee: the element to test
98+
:param requireds: a list of required values for the testee
99+
:param field: what the element is to be used as (used for error
100+
messages)
101+
:raises BadInput: error denoting the testee element is not one of
102+
the valid options
103+
"""
104+
for entry in required:
105+
if entry not in testee:
106+
handler(caller, '{} is not present in {} [{}]'.format(entry, field, testee))
107+
raise MissingParameters

layers/core/layer.py

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -75,18 +75,7 @@ def _build(self):
7575
Loads the data stored in self.data into a LayerObj (self.layer)
7676
"""
7777
try:
78-
if 'version' in self._data:
79-
ver_obj = dict(layer=self._data['version'])
80-
else:
81-
ver_obj = self._data['versions']
82-
except:
83-
handler(type(self).__name__, 'Layer version is malformed. '
84-
'Unable to load')
85-
self.__layer = None
86-
return
87-
try:
88-
self.__layer = _LayerObj(ver_obj, self._data['name'],
89-
self._data['domain'])
78+
self.__layer = _LayerObj(self._data['name'], self._data['domain'])
9079
except BadType or BadInput as e:
9180
handler(type(self).__name__, 'Layer is malformed: {}. '
9281
'Unable to load.'.format(e))
@@ -99,7 +88,7 @@ def _build(self):
9988
return
10089

10190
for key in self._data:
102-
if key not in ['version', 'versions', 'name', 'domain']:
91+
if key not in ['name', 'domain']:
10392
try:
10493
self.__layer._linker(key, self._data[key])
10594
except Exception as e:

layers/core/layerobj.py

Lines changed: 64 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from ..core.metadata import Metadata
88
from ..core.versions import Versions
99
from ..core.exceptions import UNSETVALUE, typeChecker, BadInput, handler, \
10-
categoryChecker, UnknownLayerProperty
10+
categoryChecker, UnknownLayerProperty, loadChecker, MissingParameters
1111
except ValueError:
1212
from core.filter import Filter
1313
from core.layout import Layout
@@ -17,19 +17,18 @@
1717
from core.metadata import Metadata
1818
from core.versions import Versions
1919
from core.exceptions import UNSETVALUE, typeChecker, BadInput, handler, \
20-
categoryChecker, UnknownLayerProperty
20+
categoryChecker, UnknownLayerProperty, loadChecker, MissingParameters
2121

2222
class _LayerObj:
23-
def __init__(self, versions, name, domain):
23+
def __init__(self, name, domain):
2424
"""
2525
Initialization - Creates a layer object
2626
27-
:param version: The corresponding att&ck layer version
2827
:param name: The name for this layer
29-
:param domain: The domain for this layer (mitre-enterprise
30-
or mitre-mobile)
28+
:param domain: The domain for this layer (enterprise-attack
29+
or mobile-attack)
3130
"""
32-
self.versions = versions
31+
self.__versions = UNSETVALUE
3332
self.name = name
3433
self.__description = UNSETVALUE
3534
self.domain = domain
@@ -48,28 +47,35 @@ def __init__(self, versions, name, domain):
4847

4948
@property
5049
def version(self):
51-
return self.__versions.layer
50+
if self.__versions != UNSETVALUE:
51+
return self.__versions.layer
5252

5353
@version.setter
5454
def version(self, version):
5555
typeChecker(type(self).__name__, version, str, "version")
5656
categoryChecker(type(self).__name__, version, ["3.0", "4.0"], "version")
57+
if self.__versions is UNSETVALUE:
58+
self.__versions = Versions()
5759
self.__versions.layer = version
5860

5961
@property
6062
def versions(self):
61-
return self.__versions
63+
if self.__versions != UNSETVALUE:
64+
return self.__versions
6265

6366
@versions.setter
6467
def versions(self, versions):
6568
typeChecker(type(self).__name__, versions, dict, "version")
66-
attack = None
67-
nav = None
69+
attack = UNSETVALUE
6870
if 'attack' in versions:
6971
attack = versions['attack']
70-
if 'navigator' in versions:
71-
nav = versions['navigator']
72-
self.__versions = Versions(versions['layer'], attack, nav)
72+
try:
73+
loadChecker(type(self).__name__, versions, ['layer', 'navigator'], "versions")
74+
self.__versions = Versions(versions['layer'], attack, versions['navigator'])
75+
except MissingParameters as e:
76+
handler(type(self).__name__, 'versions {} is missing parameters: '
77+
'{}. Skipping.'
78+
.format(versions, e))
7379

7480
@property
7581
def name(self):
@@ -112,20 +118,16 @@ def filters(self):
112118

113119
@filters.setter
114120
def filters(self, filters):
115-
if self.version == "4.0":
116-
temp = Filterv4(self.domain)
117-
else:
118-
temp = Filter(self.domain)
121+
temp = Filterv4(self.domain)
119122
try:
120-
if self.version != "4.0":
121-
temp.stages = filters['stages']
123+
loadChecker(type(self).__name__, filters, ['platforms'], "filters")
124+
# force upgrade to v4
122125
temp.platforms = filters['platforms']
123126
self.__filters = temp
124-
except KeyError as e:
125-
handler(type(self).__name__, "Unable to properly extract "
126-
"information from filter: {}."
127-
.format(e))
128-
raise BadInput
127+
except MissingParameters as e:
128+
handler(type(self).__name__, 'Filters {} is missing parameters: '
129+
'{}. Skipping.'
130+
.format(filters, e))
129131

130132
@property
131133
def sorting(self):
@@ -173,17 +175,17 @@ def techniques(self):
173175
def techniques(self, techniques):
174176
typeChecker(type(self).__name__, techniques, list, "techniques")
175177
self.__techniques = []
176-
entry = ""
177-
try:
178-
for entry in techniques:
178+
179+
for entry in techniques:
180+
try:
181+
loadChecker(type(self).__name__, entry, ['techniqueID'], "technique")
179182
temp = Technique(entry['techniqueID'])
180183
temp._loader(entry)
181184
self.__techniques.append(temp)
182-
except KeyError as e:
183-
handler(type(self).__name__, "Unable to properly extract "
184-
"information from technique {}: {}."
185-
.format(entry, e))
186-
raise BadInput
185+
except MissingParameters as e:
186+
handler(type(self).__name__, 'Technique {} is missing parameters: '
187+
'{}. Skipping.'
188+
.format(entry, e))
187189

188190
@property
189191
def gradient(self):
@@ -193,12 +195,12 @@ def gradient(self):
193195
@gradient.setter
194196
def gradient(self, gradient):
195197
try:
196-
self.__gradient = Gradient(gradient['colors'],
197-
gradient['minValue'],
198-
gradient['maxValue'])
199-
except KeyError as e:
200-
handler(type(self).__name__, 'Gradient is missing parameters: {}. '
201-
'Unable to load.'.format(e))
198+
loadChecker(type(self).__name__, gradient, ['colors', 'minValue', 'maxValue'], "gradient")
199+
self.__gradient = Gradient(gradient['colors'], gradient['minValue'], gradient['maxValue'])
200+
except MissingParameters as e:
201+
handler(type(self).__name__, 'Gradient {} is missing parameters: '
202+
'{}. Skipping.'
203+
.format(gradient, e))
202204

203205
@property
204206
def legendItems(self):
@@ -209,15 +211,15 @@ def legendItems(self):
209211
def legendItems(self, legendItems):
210212
typeChecker(type(self).__name__, legendItems, list, "legendItems")
211213
self.__legendItems = []
212-
entry = ""
213-
try:
214-
for entry in legendItems:
214+
for entry in legendItems:
215+
try:
216+
loadChecker(type(self).__name__, entry, ['label', 'color'], "legendItem")
215217
temp = LegendItem(entry['label'], entry['color'])
216218
self.__legendItems.append(temp)
217-
except KeyError as e:
218-
handler(type(self).__name__, 'LegendItem {} is missing parameters:'
219-
' {}. Unable to load.'
220-
.format(entry, e))
219+
except MissingParameters as e:
220+
handler(type(self).__name__, 'Legend Item {} is missing parameters: '
221+
'{}. Skipping.'
222+
.format(entry, e))
221223

222224
@property
223225
def showTacticRowBackground(self):
@@ -272,13 +274,13 @@ def metadata(self):
272274
def metadata(self, metadata):
273275
typeChecker(type(self).__name__, metadata, list, "metadata")
274276
self.__metadata = []
275-
entry = ""
276-
try:
277-
for entry in metadata:
277+
for entry in metadata:
278+
try:
279+
loadChecker(type(self).__name__, entry, ['name', 'value'], "metadata")
278280
self.__metadata.append(Metadata(entry['name'], entry['value']))
279-
except KeyError as e:
280-
handler(type(self).__name__, 'Metadata {} is missing parameters: '
281-
'{}. Unable to load.'
281+
except MissingParameters as e:
282+
handler(type(self).__name__, 'Metadata {} is missing parameters: '
283+
'{}. Skipping.'
282284
.format(entry, e))
283285

284286
def _enumerate(self):
@@ -321,10 +323,12 @@ def get_dict(self):
321323
Converts the currently loaded layer into a dict
322324
:returns: A dict representation of the current layer object
323325
"""
324-
temp = dict(name=self.name, versions=self.versions.get_dict(), domain=self.domain)
326+
temp = dict(name=self.name, domain=self.domain)
325327

326328
if self.description:
327329
temp['description'] = self.description
330+
if self.versions:
331+
temp['versions'] = self.versions.get_dict()
328332
if self.filters:
329333
temp['filters'] = self.filters.get_dict()
330334
if self.sorting:
@@ -364,6 +368,13 @@ def _linker(self, field, data):
364368
"""
365369
if field == 'description':
366370
self.description = data
371+
elif field.startswith('version'):
372+
if not field.endswith('s'):
373+
# force upgrade
374+
ver_obj = dict(layer="4.0", navigator="4.0")
375+
self.versions = ver_obj
376+
else:
377+
self.versions = data
367378
elif field == 'filters':
368379
self.filters = data
369380
elif field == 'sorting':

layers/core/versions.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66

77
class Versions:
8-
def __init__(self, layer="4.0", attack=UNSETVALUE, navigator=UNSETVALUE):
8+
def __init__(self, layer="4.0", attack=UNSETVALUE, navigator="4.0"):
99
"""
1010
Initialization - Creates a v4 Versions object
1111
@@ -14,7 +14,7 @@ def __init__(self, layer="4.0", attack=UNSETVALUE, navigator=UNSETVALUE):
1414
:param navigator: The navigator version
1515
"""
1616
self.layer = layer
17-
self.attack = attack
17+
self.__attack = attack
1818
self.navigator = navigator
1919

2020
@property
@@ -33,6 +33,7 @@ def navigator(self):
3333
@navigator.setter
3434
def navigator(self, navigator):
3535
typeChecker(type(self).__name__, navigator, str, "navigator")
36+
categoryChecker(type(self).__name__, navigator, ["4.0"], "navigator version")
3637
self.__navigator = navigator
3738

3839
@property
@@ -42,7 +43,10 @@ def layer(self):
4243
@layer.setter
4344
def layer(self, layer):
4445
typeChecker(type(self).__name__, layer, str, "layer")
45-
categoryChecker(type(self).__name__, layer, ["3.0", "4.0"], "version")
46+
categoryChecker(type(self).__name__, layer, ["3.0", "4.0"], "layer version")
47+
if layer == '3.0':
48+
print('[NOTICE] - Forcibly upgrading version from {} to 4.0.'.format(layer))
49+
layer = "4.0"
4650
self.__layer = layer
4751

4852
def get_dict(self):

layers/exporters/svg_templates.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@
88
from exporters.svg_objects import G, SVG_HeaderBlock, SVG_Technique, Text, convertToPx,_optimalFontSize, \
99
_getstringwidth
1010
from core.gradient import Gradient
11-
from core.filter import Filter
11+
from core.filter import Filterv4
1212
except ModuleNotFoundError:
1313
from ..exporters.matrix_gen import MatrixGen
1414
from ..exporters.svg_objects import G, SVG_HeaderBlock, SVG_Technique, Text, convertToPx, _optimalFontSize, \
1515
_getstringwidth
1616
from ..core.gradient import Gradient
17-
from ..core.filter import Filter
17+
from ..core.filter import Filterv4
1818

1919

2020
class BadTemplateException(Exception):
@@ -91,11 +91,10 @@ def _build_headers(self, name, config, desc=None, filters=None, gradient=None):
9191
if config.showFilters:
9292
fi = filters
9393
if fi is None:
94-
fi = Filter()
94+
fi = Filterv4()
9595
fi.platforms = ["Windows", "Linux", "macOS"]
96-
fi.stages = ["act"]
9796
g2 = SVG_HeaderBlock().build(height=header_height, width=header_width, label='filters',
98-
t1text=', '.join(fi.platforms), t2text=fi.stages[0], config=config)
97+
t1text=', '.join(fi.platforms), config=config)
9998
b2 = G(tx=operation_x / header_count * psych + 1.5 * border * psych)
10099
header.append(b2)
101100
b2.append(g2)

0 commit comments

Comments
 (0)