mal-toolbox 0.1.8__py3-none-any.whl → 0.1.9__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mal_toolbox-0.1.8.dist-info → mal_toolbox-0.1.9.dist-info}/METADATA +7 -1
- {mal_toolbox-0.1.8.dist-info → mal_toolbox-0.1.9.dist-info}/RECORD +13 -13
- {mal_toolbox-0.1.8.dist-info → mal_toolbox-0.1.9.dist-info}/WHEEL +1 -1
- maltoolbox/__init__.py +2 -2
- maltoolbox/attackgraph/__init__.py +5 -0
- maltoolbox/attackgraph/attackgraph.py +26 -1
- maltoolbox/attackgraph/node.py +34 -0
- maltoolbox/attackgraph/query.py +43 -2
- maltoolbox/language/__init__.py +2 -0
- maltoolbox/language/classes_factory.py +3 -2
- {mal_toolbox-0.1.8.dist-info → mal_toolbox-0.1.9.dist-info}/AUTHORS +0 -0
- {mal_toolbox-0.1.8.dist-info → mal_toolbox-0.1.9.dist-info}/LICENSE +0 -0
- {mal_toolbox-0.1.8.dist-info → mal_toolbox-0.1.9.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: mal-toolbox
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.9
|
|
4
4
|
Summary: A collection of tools used to create MAL models and attack graphs.
|
|
5
5
|
Author-email: Andrei Buhaiu <buhaiu@kth.se>, Giuseppe Nebbione <nebbione@kth.se>, Nikolaos Kakouros <nkak@kth.se>, Jakob Nyberg <jaknyb@kth.se>, Joakim Loxdal <loxdal@kth.se>
|
|
6
6
|
License: Apache Software License
|
|
@@ -102,6 +102,12 @@ and it can be used to visualise the instance model and the attack graph.
|
|
|
102
102
|
|
|
103
103
|
# Usage
|
|
104
104
|
|
|
105
|
+
## Installation
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
pip install mal-toolbox
|
|
109
|
+
```
|
|
110
|
+
|
|
105
111
|
## Configuration
|
|
106
112
|
A default configuration file `default.conf` can be found in the package
|
|
107
113
|
directory. This contains the default values to use for logging and can also be
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
maltoolbox/__init__.py,sha256=
|
|
1
|
+
maltoolbox/__init__.py,sha256=tfDCz1FxF71yzZIZyY0uKDOqCV2nThbOBOUCyW72Qvg,2776
|
|
2
2
|
maltoolbox/__main__.py,sha256=1lOOOme_y56VWrEE1jkarTt-WoUo9yilCo8sUrivyns,2680
|
|
3
3
|
maltoolbox/default.conf,sha256=YLGBSJh2q8hn3RzRRBbib9F6E6pcvquoHeALMRtA0wU,295
|
|
4
4
|
maltoolbox/exceptions.py,sha256=0YjPx2v1yYumZ2o7pVZ1s_jS-GAb3Ng979KEFhROSNY,1399
|
|
5
5
|
maltoolbox/file_utils.py,sha256=6KFEEZvf9x8yfNAq7hadF7lUGlLimNFMJ0W_DK2rh6Q,2024
|
|
6
6
|
maltoolbox/model.py,sha256=1HuH58skm1StcKtvMFO-iXkpLOaauB9YSEv1XNdW_-Y,30406
|
|
7
7
|
maltoolbox/wrappers.py,sha256=BYYNcIdTlyumADQCPcy1xmPEabfmi0P1l9RcbdVWm9w,2002
|
|
8
|
-
maltoolbox/attackgraph/__init__.py,sha256=
|
|
8
|
+
maltoolbox/attackgraph/__init__.py,sha256=Oqqj5iCwnrzjDoJEFZnVI_kebjJPVbPXK-mWHy0lf-8,209
|
|
9
9
|
maltoolbox/attackgraph/attacker.py,sha256=hmJtgcwtQgz0r9J_0Pgm7ktfKpFE3dp-HJ2TOz1NiWw,2880
|
|
10
|
-
maltoolbox/attackgraph/attackgraph.py,sha256=
|
|
11
|
-
maltoolbox/attackgraph/node.py,sha256=
|
|
12
|
-
maltoolbox/attackgraph/query.py,sha256=
|
|
10
|
+
maltoolbox/attackgraph/attackgraph.py,sha256=Gx88-3M1HacMZRE9YgIGg-6cxCFVZg23bj_pkxDeEH4,29182
|
|
11
|
+
maltoolbox/attackgraph/node.py,sha256=ZYnXMIucmTIspQvBD-zyB_wnYO6j27HxQahuQGpYbkE,5640
|
|
12
|
+
maltoolbox/attackgraph/query.py,sha256=JnoNTUEIlLv2VIk3u5Rq3GpleOn9TZVGBVijniRY_44,6802
|
|
13
13
|
maltoolbox/attackgraph/analyzers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
14
|
maltoolbox/attackgraph/analyzers/apriori.py,sha256=t_QdfetgNoDXFwlOasrmtBE7XaTaKZvnQCo6X9rs5Xg,6481
|
|
15
15
|
maltoolbox/ingestors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
16
|
maltoolbox/ingestors/neo4j.py,sha256=jdulYsQ2eZT2r0Af_yYjyGkmVx4l5h8viu1Z70NjVAM,8811
|
|
17
|
-
maltoolbox/language/__init__.py,sha256=
|
|
18
|
-
maltoolbox/language/classes_factory.py,sha256=
|
|
17
|
+
maltoolbox/language/__init__.py,sha256=0tvCJDayrLwpqKRny7LBkMOrvcDE6JfJj7U7Jd64Okg,140
|
|
18
|
+
maltoolbox/language/classes_factory.py,sha256=0QFF5Z9e4UeWwavvH1jM4BkeDZqKKZ4Ij7leGmSfXn4,10063
|
|
19
19
|
maltoolbox/language/languagegraph.py,sha256=f79ovmrGQb6tvY9ze-zqP031N6rApB9WsbZd5LRyhk8,47031
|
|
20
20
|
maltoolbox/language/compiler/__init__.py,sha256=fJ22-FlXfr907WCPkqlr_eBTzPqsrg6m3i7J_ZWpuAo,840
|
|
21
21
|
maltoolbox/language/compiler/mal_lexer.py,sha256=wocRzBkLbqYefpGvq2W77x7439-AdZKVgPWhRiRubXg,10776
|
|
@@ -24,9 +24,9 @@ maltoolbox/language/compiler/mal_visitor.py,sha256=9gG06D7GZKlBY-62SmbIkRYkGBUBI
|
|
|
24
24
|
maltoolbox/translators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
25
25
|
maltoolbox/translators/securicad.py,sha256=FAIHnoqFTmNYbCGxLsK6pX5g1oiNFfPTqkT_3qq3GG8,6692
|
|
26
26
|
maltoolbox/translators/updater.py,sha256=Ap08-AsU_7or5ESQvZL2i4nWz3B5pvgfftZyc_-Gd8M,4766
|
|
27
|
-
mal_toolbox-0.1.
|
|
28
|
-
mal_toolbox-0.1.
|
|
29
|
-
mal_toolbox-0.1.
|
|
30
|
-
mal_toolbox-0.1.
|
|
31
|
-
mal_toolbox-0.1.
|
|
32
|
-
mal_toolbox-0.1.
|
|
27
|
+
mal_toolbox-0.1.9.dist-info/AUTHORS,sha256=zxLrLe8EY39WtRKlAY4Oorx4Z2_LHV2ApRvDGZgY7xY,127
|
|
28
|
+
mal_toolbox-0.1.9.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
29
|
+
mal_toolbox-0.1.9.dist-info/METADATA,sha256=ceylz74EpI9pdyIeJQCzeU_HcqpEB4-riD12UjpLyv8,6003
|
|
30
|
+
mal_toolbox-0.1.9.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
|
31
|
+
mal_toolbox-0.1.9.dist-info/top_level.txt,sha256=phqRVLRKGdSUgRY03mcpi2cmbbDo5YGjkV4gkqHFFcM,11
|
|
32
|
+
mal_toolbox-0.1.9.dist-info/RECORD,,
|
maltoolbox/__init__.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
|
2
|
-
# MAL Toolbox v0.1.
|
|
2
|
+
# MAL Toolbox v0.1.9
|
|
3
3
|
# Copyright 2024, Andrei Buhaiu.
|
|
4
4
|
#
|
|
5
5
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -21,7 +21,7 @@ MAL-Toolbox Framework
|
|
|
21
21
|
"""
|
|
22
22
|
|
|
23
23
|
__title__ = 'maltoolbox'
|
|
24
|
-
__version__ = '0.1.
|
|
24
|
+
__version__ = '0.1.9'
|
|
25
25
|
__authors__ = ['Andrei Buhaiu',
|
|
26
26
|
'Giuseppe Nebbione',
|
|
27
27
|
'Nikolaos Kakouros',
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
MAL-Toolbox Attack Graph Module
|
|
3
3
|
"""
|
|
4
4
|
from __future__ import annotations
|
|
5
|
+
import copy
|
|
5
6
|
import logging
|
|
6
7
|
import json
|
|
7
8
|
|
|
@@ -224,6 +225,30 @@ class AttackGraph():
|
|
|
224
225
|
'attackers': serialized_attackers,
|
|
225
226
|
}
|
|
226
227
|
|
|
228
|
+
def __deepcopy__(self, memo):
|
|
229
|
+
copied_attackgraph = AttackGraph(self.lang_graph)
|
|
230
|
+
copied_attackgraph.model = self.model
|
|
231
|
+
|
|
232
|
+
# Deep copy nodes and add references to them
|
|
233
|
+
copied_attackgraph.nodes = copy.deepcopy(self.nodes, memo)
|
|
234
|
+
|
|
235
|
+
# Deep copy attackers and references to them
|
|
236
|
+
copied_attackgraph.attackers = copy.deepcopy(self.attackers, memo)
|
|
237
|
+
|
|
238
|
+
# Copy lookup dicts
|
|
239
|
+
copied_attackgraph._id_to_attacker = \
|
|
240
|
+
copy.deepcopy(self._id_to_attacker, memo)
|
|
241
|
+
copied_attackgraph._id_to_node = \
|
|
242
|
+
copy.deepcopy(self._id_to_node, memo)
|
|
243
|
+
copied_attackgraph._full_name_to_node = \
|
|
244
|
+
copy.deepcopy(self._full_name_to_node, memo)
|
|
245
|
+
|
|
246
|
+
# Copy counters
|
|
247
|
+
copied_attackgraph.next_node_id = self.next_node_id
|
|
248
|
+
copied_attackgraph.next_attacker_id = self.next_attacker_id
|
|
249
|
+
|
|
250
|
+
return copied_attackgraph
|
|
251
|
+
|
|
227
252
|
def save_to_file(self, filename: str) -> None:
|
|
228
253
|
"""Save to json/yml depending on extension"""
|
|
229
254
|
logger.debug('Save attack graph to file "%s".', filename)
|
|
@@ -382,7 +407,7 @@ class AttackGraph():
|
|
|
382
407
|
The attack step node that matches the given full name.
|
|
383
408
|
"""
|
|
384
409
|
|
|
385
|
-
logger.debug(f'Looking up node with
|
|
410
|
+
logger.debug(f'Looking up node with full name "{full_name}"')
|
|
386
411
|
return self._full_name_to_node.get(full_name)
|
|
387
412
|
|
|
388
413
|
def get_attacker_by_id(self, attacker_id: int) -> Optional[Attacker]:
|
maltoolbox/attackgraph/node.py
CHANGED
|
@@ -3,6 +3,7 @@ MAL-Toolbox Attack Graph Node Dataclass
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
from __future__ import annotations
|
|
6
|
+
import copy
|
|
6
7
|
from dataclasses import field, dataclass
|
|
7
8
|
from typing import Any, Optional
|
|
8
9
|
|
|
@@ -69,6 +70,39 @@ class AttackGraphNode:
|
|
|
69
70
|
def __repr__(self) -> str:
|
|
70
71
|
return str(self.to_dict())
|
|
71
72
|
|
|
73
|
+
def __deepcopy__(self, memo) -> AttackGraphNode:
|
|
74
|
+
"""Deep copy an attackgraph node"""
|
|
75
|
+
|
|
76
|
+
copied_node = AttackGraphNode(
|
|
77
|
+
self.type,
|
|
78
|
+
self.name,
|
|
79
|
+
self.ttc,
|
|
80
|
+
self.id,
|
|
81
|
+
self.asset,
|
|
82
|
+
[],
|
|
83
|
+
[],
|
|
84
|
+
self.defense_status,
|
|
85
|
+
self.existence_status,
|
|
86
|
+
self.is_viable,
|
|
87
|
+
self.is_necessary,
|
|
88
|
+
copy.deepcopy(self.compromised_by, memo),
|
|
89
|
+
self.mitre_info,
|
|
90
|
+
copy.deepcopy(self.tags, memo),
|
|
91
|
+
copy.deepcopy(self.attributes, memo),
|
|
92
|
+
copy.deepcopy(self.extras, memo)
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
# Remember that self was already copied
|
|
96
|
+
memo[id(self)] = copied_node
|
|
97
|
+
|
|
98
|
+
# Deep copy children and parents, send memo (avoid infinite recursion)
|
|
99
|
+
if self.parents:
|
|
100
|
+
copied_node.parents = copy.deepcopy(self.parents, memo)
|
|
101
|
+
if self.children:
|
|
102
|
+
copied_node.children = copy.deepcopy(self.children, memo)
|
|
103
|
+
|
|
104
|
+
return copied_node
|
|
105
|
+
|
|
72
106
|
def is_compromised(self) -> bool:
|
|
73
107
|
"""
|
|
74
108
|
Return True if any attackers have compromised this node.
|
maltoolbox/attackgraph/query.py
CHANGED
|
@@ -37,10 +37,21 @@ def is_node_traversable_by_attacker(
|
|
|
37
37
|
attacker.id
|
|
38
38
|
)
|
|
39
39
|
if not node.is_viable:
|
|
40
|
+
logger.debug(
|
|
41
|
+
'"%s"(%d) is not traversable because it is non-viable',
|
|
42
|
+
node.full_name,
|
|
43
|
+
node.id,
|
|
44
|
+
)
|
|
40
45
|
return False
|
|
41
46
|
|
|
42
47
|
match(node.type):
|
|
43
48
|
case 'or':
|
|
49
|
+
logger.debug(
|
|
50
|
+
'"%s"(%d) is traversable because it is viable and '
|
|
51
|
+
'of type "or".',
|
|
52
|
+
node.full_name,
|
|
53
|
+
node.id
|
|
54
|
+
)
|
|
44
55
|
return True
|
|
45
56
|
|
|
46
57
|
case 'and':
|
|
@@ -49,14 +60,44 @@ def is_node_traversable_by_attacker(
|
|
|
49
60
|
not parent.is_compromised_by(attacker):
|
|
50
61
|
# If the parent is not present in the attacks steps
|
|
51
62
|
# already reached and is necessary.
|
|
63
|
+
logger.debug(
|
|
64
|
+
'"%s"(%d) is not traversable because while it is '
|
|
65
|
+
'viable, and of type "and", its necessary parent '
|
|
66
|
+
'"%s(%d)" has not already been compromised.',
|
|
67
|
+
node.full_name,
|
|
68
|
+
node.id,
|
|
69
|
+
parent.full_name,
|
|
70
|
+
parent.id
|
|
71
|
+
)
|
|
52
72
|
return False
|
|
73
|
+
logger.debug(
|
|
74
|
+
'"%s"(%d) is traversable because it is viable, '
|
|
75
|
+
'of type "and", and all of its necessary parents have '
|
|
76
|
+
'already been compromised.',
|
|
77
|
+
node.full_name,
|
|
78
|
+
node.id
|
|
79
|
+
)
|
|
53
80
|
return True
|
|
54
81
|
|
|
55
|
-
case 'exist' | 'notExist' |
|
|
82
|
+
case 'exist' | 'notExist' | 'defense':
|
|
83
|
+
logger.warning(
|
|
84
|
+
'Nodes of type "exist", "notExist", and "defense" are never '
|
|
85
|
+
'marked as traversable. However, we do not normally check '
|
|
86
|
+
'if they are traversable. Node "%s"(%d) of type "%s" was '
|
|
87
|
+
'checked for traversability.',
|
|
88
|
+
node.full_name,
|
|
89
|
+
node.id,
|
|
90
|
+
node.type
|
|
91
|
+
)
|
|
56
92
|
return False
|
|
57
93
|
|
|
58
94
|
case _:
|
|
59
|
-
logger.error(
|
|
95
|
+
logger.error(
|
|
96
|
+
'Node "%s"(%d) has an unknown type "%s".',
|
|
97
|
+
node.full_name,
|
|
98
|
+
node.id,
|
|
99
|
+
node.type
|
|
100
|
+
)
|
|
60
101
|
return False
|
|
61
102
|
|
|
62
103
|
def get_attack_surface(
|
maltoolbox/language/__init__.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""
|
|
2
2
|
MAL-Toolbox Language Classes Factory Module
|
|
3
|
+
Uses python_jsonschema_objects to generate python classes from a MAL language
|
|
3
4
|
"""
|
|
4
5
|
from __future__ import annotations
|
|
5
6
|
import json
|
|
@@ -25,7 +26,7 @@ class LanguageClassesFactory:
|
|
|
25
26
|
|
|
26
27
|
def _generate_assets(self) -> None:
|
|
27
28
|
"""
|
|
28
|
-
Generate JSON Schema for
|
|
29
|
+
Generate JSON Schema for asset types in the language specification.
|
|
29
30
|
"""
|
|
30
31
|
for asset in self.lang_graph.assets:
|
|
31
32
|
logger.debug('Creating %s asset JSON schema entry.', asset.name)
|
|
@@ -67,7 +68,7 @@ class LanguageClassesFactory:
|
|
|
67
68
|
|
|
68
69
|
def _generate_associations(self) -> None:
|
|
69
70
|
"""
|
|
70
|
-
Generate JSON Schema for
|
|
71
|
+
Generate JSON Schema for association types in the language specification.
|
|
71
72
|
"""
|
|
72
73
|
def create_association_entry(assoc: SchemaGeneratedClass):
|
|
73
74
|
logger.debug('Creating %s association JSON schema entry.', assoc.name)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|