mal-toolbox 0.1.11__py3-none-any.whl → 0.2.0__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.11.dist-info → mal_toolbox-0.2.0.dist-info}/METADATA +2 -2
- {mal_toolbox-0.1.11.dist-info → mal_toolbox-0.2.0.dist-info}/RECORD +15 -15
- {mal_toolbox-0.1.11.dist-info → mal_toolbox-0.2.0.dist-info}/WHEEL +1 -1
- maltoolbox/__init__.py +2 -2
- maltoolbox/attackgraph/analyzers/apriori.py +61 -1
- maltoolbox/attackgraph/attackgraph.py +294 -245
- maltoolbox/attackgraph/node.py +23 -7
- maltoolbox/file_utils.py +6 -2
- maltoolbox/language/__init__.py +5 -1
- maltoolbox/language/classes_factory.py +86 -70
- maltoolbox/language/languagegraph.py +1022 -475
- maltoolbox/model.py +44 -35
- {mal_toolbox-0.1.11.dist-info → mal_toolbox-0.2.0.dist-info}/AUTHORS +0 -0
- {mal_toolbox-0.1.11.dist-info → mal_toolbox-0.2.0.dist-info}/LICENSE +0 -0
- {mal_toolbox-0.1.11.dist-info → mal_toolbox-0.2.0.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: mal-toolbox
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
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
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
maltoolbox/__init__.py,sha256=
|
|
1
|
+
maltoolbox/__init__.py,sha256=4y4QJcwl6OKw8pCiPaUrOWtXRpEgFrKeoNLajc9p2Iw,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
|
-
maltoolbox/file_utils.py,sha256=
|
|
6
|
-
maltoolbox/model.py,sha256=
|
|
5
|
+
maltoolbox/file_utils.py,sha256=TFLm32_RLfB4uEsToZ8ypDcRbbdFZMso34mfvqAb1bY,2139
|
|
6
|
+
maltoolbox/model.py,sha256=mQqwx0cSLV6E8wGBSDDKkKnFdbcPeBcjqIpFdBjtkYE,31042
|
|
7
7
|
maltoolbox/wrappers.py,sha256=BYYNcIdTlyumADQCPcy1xmPEabfmi0P1l9RcbdVWm9w,2002
|
|
8
8
|
maltoolbox/attackgraph/__init__.py,sha256=Oqqj5iCwnrzjDoJEFZnVI_kebjJPVbPXK-mWHy0lf-8,209
|
|
9
9
|
maltoolbox/attackgraph/attacker.py,sha256=OaBNDYZF8shbFuQctzuNYVkOrpNb_KhxxV19k0SRa50,3541
|
|
10
|
-
maltoolbox/attackgraph/attackgraph.py,sha256=
|
|
11
|
-
maltoolbox/attackgraph/node.py,sha256=
|
|
10
|
+
maltoolbox/attackgraph/attackgraph.py,sha256=HLAjF8Qx9W2iv6uSWRULL16ieuy7UM5fLZNwxIr544s,31348
|
|
11
|
+
maltoolbox/attackgraph/node.py,sha256=xrnY_YlX9ZFXDRsj92I9PZMGJR6s1LpTnOIppR6TGXo,6074
|
|
12
12
|
maltoolbox/attackgraph/query.py,sha256=JnoNTUEIlLv2VIk3u5Rq3GpleOn9TZVGBVijniRY_44,6802
|
|
13
13
|
maltoolbox/attackgraph/analyzers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
-
maltoolbox/attackgraph/analyzers/apriori.py,sha256=
|
|
14
|
+
maltoolbox/attackgraph/analyzers/apriori.py,sha256=8dRZL22oym5goXBYtYA1ZYXxk8VwQS0RlNCvNCUhCAY,8743
|
|
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
|
|
19
|
-
maltoolbox/language/languagegraph.py,sha256=
|
|
17
|
+
maltoolbox/language/__init__.py,sha256=WNoPtcYHJE799vR6x269Gx5KPWxBgLOHQoycE1vhOF4,257
|
|
18
|
+
maltoolbox/language/classes_factory.py,sha256=-s4xxwcCGgKj1wzrgsrn-ndLhgU4VoEjrrSuGx8qvYE,10217
|
|
19
|
+
maltoolbox/language/languagegraph.py,sha256=ctoxS33DM7F4DF50SNDB2lTtokUJKyWMhMhss46yv_g,68123
|
|
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
|
|
22
22
|
maltoolbox/language/compiler/mal_parser.py,sha256=M1EVZFV73TNfQHz2KJ8-iloqOD4KUhHyajszD8UrNow,91349
|
|
@@ -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.
|
|
28
|
-
mal_toolbox-0.
|
|
29
|
-
mal_toolbox-0.
|
|
30
|
-
mal_toolbox-0.
|
|
31
|
-
mal_toolbox-0.
|
|
32
|
-
mal_toolbox-0.
|
|
27
|
+
mal_toolbox-0.2.0.dist-info/AUTHORS,sha256=zxLrLe8EY39WtRKlAY4Oorx4Z2_LHV2ApRvDGZgY7xY,127
|
|
28
|
+
mal_toolbox-0.2.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
29
|
+
mal_toolbox-0.2.0.dist-info/METADATA,sha256=AWtN4435t5ycp5k-dPfRSVRU4rhpyb6e_z1FBE2PYpY,6001
|
|
30
|
+
mal_toolbox-0.2.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
31
|
+
mal_toolbox-0.2.0.dist-info/top_level.txt,sha256=phqRVLRKGdSUgRY03mcpi2cmbbDo5YGjkV4gkqHFFcM,11
|
|
32
|
+
mal_toolbox-0.2.0.dist-info/RECORD,,
|
maltoolbox/__init__.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
|
2
|
-
# MAL Toolbox v0.
|
|
2
|
+
# MAL Toolbox v0.2.0
|
|
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.
|
|
24
|
+
__version__ = '0.2.0'
|
|
25
25
|
__authors__ = ['Andrei Buhaiu',
|
|
26
26
|
'Giuseppe Nebbione',
|
|
27
27
|
'Nikolaos Kakouros',
|
|
@@ -12,6 +12,7 @@ Currently these are:
|
|
|
12
12
|
"""
|
|
13
13
|
|
|
14
14
|
from __future__ import annotations
|
|
15
|
+
from typing import Optional
|
|
15
16
|
import logging
|
|
16
17
|
|
|
17
18
|
from ..attackgraph import AttackGraph
|
|
@@ -41,6 +42,7 @@ def propagate_viability_from_node(node: AttackGraphNode) -> None:
|
|
|
41
42
|
if child.is_viable != original_value:
|
|
42
43
|
propagate_viability_from_node(child)
|
|
43
44
|
|
|
45
|
+
|
|
44
46
|
def propagate_necessity_from_node(node: AttackGraphNode) -> None:
|
|
45
47
|
"""
|
|
46
48
|
Arguments:
|
|
@@ -53,11 +55,13 @@ def propagate_necessity_from_node(node: AttackGraphNode) -> None:
|
|
|
53
55
|
)
|
|
54
56
|
|
|
55
57
|
if node.ttc and 'name' in node.ttc:
|
|
56
|
-
if node.ttc['name'] not in ['Enabled', 'Disabled']:
|
|
58
|
+
if node.ttc['name'] not in ['Enabled', 'Disabled', 'Instant']:
|
|
57
59
|
# Do not propagate unnecessary state from nodes that have a TTC
|
|
58
60
|
# probability distribution associated with them.
|
|
59
61
|
# TODO: Evaluate this more carefully, how do we want to have TTCs
|
|
60
62
|
# impact necessity and viability.
|
|
63
|
+
# TODO: Have this condition be any probability that has a
|
|
64
|
+
# Bernoulli component
|
|
61
65
|
return
|
|
62
66
|
|
|
63
67
|
for child in node.children:
|
|
@@ -108,6 +112,7 @@ def evaluate_viability(node: AttackGraphNode) -> None:
|
|
|
108
112
|
logger.error(msg, node.full_name, node.id, node.type)
|
|
109
113
|
raise ValueError(msg % (node.full_name, node.id, node.type))
|
|
110
114
|
|
|
115
|
+
|
|
111
116
|
def evaluate_necessity(node: AttackGraphNode) -> None:
|
|
112
117
|
"""
|
|
113
118
|
Arguments:
|
|
@@ -141,6 +146,7 @@ def evaluate_necessity(node: AttackGraphNode) -> None:
|
|
|
141
146
|
logger.error(msg, node.full_name, node.id, node.type)
|
|
142
147
|
raise ValueError(msg % (node.full_name, node.id, node.type))
|
|
143
148
|
|
|
149
|
+
|
|
144
150
|
def evaluate_viability_and_necessity(node: AttackGraphNode) -> None:
|
|
145
151
|
"""
|
|
146
152
|
Arguments:
|
|
@@ -149,6 +155,7 @@ def evaluate_viability_and_necessity(node: AttackGraphNode) -> None:
|
|
|
149
155
|
evaluate_viability(node)
|
|
150
156
|
evaluate_necessity(node)
|
|
151
157
|
|
|
158
|
+
|
|
152
159
|
def calculate_viability_and_necessity(graph: AttackGraph) -> None:
|
|
153
160
|
"""
|
|
154
161
|
Arguments:
|
|
@@ -163,13 +170,66 @@ def calculate_viability_and_necessity(graph: AttackGraph) -> None:
|
|
|
163
170
|
if not node.is_necessary:
|
|
164
171
|
propagate_necessity_from_node(node)
|
|
165
172
|
|
|
173
|
+
|
|
166
174
|
def prune_unviable_and_unnecessary_nodes(graph: AttackGraph) -> None:
|
|
167
175
|
"""
|
|
168
176
|
Arguments:
|
|
169
177
|
graph - the attack graph for which we wish to remove the
|
|
170
178
|
the nodes which are not viable or necessary.
|
|
171
179
|
"""
|
|
180
|
+
logger.debug('Prune unviable and unnecessary nodes from the attack graph.')
|
|
172
181
|
for node in graph.nodes:
|
|
173
182
|
if (node.type == 'or' or node.type == 'and') and \
|
|
174
183
|
(not node.is_viable or not node.is_necessary):
|
|
175
184
|
graph.remove_node(node)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def propagate_viability_from_unviable_node(
|
|
188
|
+
unviable_node: AttackGraphNode,
|
|
189
|
+
) -> list[AttackGraphNode]:
|
|
190
|
+
"""
|
|
191
|
+
Update viability of nodes affected by newly enabled defense
|
|
192
|
+
`unviable_node` in the graph and return any attack steps
|
|
193
|
+
that are no longer viable because of it.
|
|
194
|
+
|
|
195
|
+
Propagate recursively via children as long as changes occur.
|
|
196
|
+
|
|
197
|
+
Arguments:
|
|
198
|
+
unviable_node - the node to propagate viability from
|
|
199
|
+
|
|
200
|
+
Returns:
|
|
201
|
+
attack_steps_made_unviable - list of the attack steps that have been
|
|
202
|
+
made unviable by a defense enabled in the
|
|
203
|
+
current step. Builds up recursively.
|
|
204
|
+
"""
|
|
205
|
+
|
|
206
|
+
attack_steps_made_unviable = []
|
|
207
|
+
|
|
208
|
+
logger.debug(
|
|
209
|
+
'Update viability for node "%s"(%d)',
|
|
210
|
+
unviable_node.full_name,
|
|
211
|
+
unviable_node.id
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
assert not unviable_node.is_viable, (
|
|
215
|
+
"propagate_viability_from_unviable_node should not be called"
|
|
216
|
+
f" on viable node {unviable_node.full_name}"
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
if unviable_node.type in ('and', 'or'):
|
|
220
|
+
attack_steps_made_unviable.append(unviable_node)
|
|
221
|
+
|
|
222
|
+
for child in unviable_node.children:
|
|
223
|
+
original_value = child.is_viable
|
|
224
|
+
if child.type == 'or':
|
|
225
|
+
child.is_viable = False
|
|
226
|
+
for parent in child.parents:
|
|
227
|
+
child.is_viable = child.is_viable or parent.is_viable
|
|
228
|
+
if child.type == 'and':
|
|
229
|
+
child.is_viable = False
|
|
230
|
+
|
|
231
|
+
if child.is_viable != original_value:
|
|
232
|
+
attack_steps_made_unviable += \
|
|
233
|
+
propagate_viability_from_unviable_node(child)
|
|
234
|
+
|
|
235
|
+
return attack_steps_made_unviable
|