mal-toolbox 1.0.0__py3-none-any.whl → 1.0.1__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-1.0.0.dist-info → mal_toolbox-1.0.1.dist-info}/METADATA +1 -2
- {mal_toolbox-1.0.0.dist-info → mal_toolbox-1.0.1.dist-info}/RECORD +11 -11
- maltoolbox/__init__.py +2 -2
- maltoolbox/attackgraph/attackgraph.py +17 -13
- maltoolbox/language/languagegraph.py +52 -36
- maltoolbox/model.py +1 -13
- {mal_toolbox-1.0.0.dist-info → mal_toolbox-1.0.1.dist-info}/WHEEL +0 -0
- {mal_toolbox-1.0.0.dist-info → mal_toolbox-1.0.1.dist-info}/entry_points.txt +0 -0
- {mal_toolbox-1.0.0.dist-info → mal_toolbox-1.0.1.dist-info}/licenses/AUTHORS +0 -0
- {mal_toolbox-1.0.0.dist-info → mal_toolbox-1.0.1.dist-info}/licenses/LICENSE +0 -0
- {mal_toolbox-1.0.0.dist-info → mal_toolbox-1.0.1.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mal-toolbox
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.1
|
|
4
4
|
Summary: A collection of tools used to create MAL models and attack graphs.
|
|
5
5
|
Author-email: Andrei Buhaiu <buhaiu@kth.se>, Joakim Loxdal <loxdal@kth.se>, Nikolaos Kakouros <nkak@kth.se>, Jakob Nyberg <jaknyb@kth.se>, Giuseppe Nebbione <nebbione@kth.se>
|
|
6
6
|
License: Apache Software License
|
|
@@ -18,7 +18,6 @@ Requires-Python: >=3.10
|
|
|
18
18
|
Description-Content-Type: text/markdown
|
|
19
19
|
License-File: LICENSE
|
|
20
20
|
License-File: AUTHORS
|
|
21
|
-
Requires-Dist: py2neo>=2021.2.3
|
|
22
21
|
Requires-Dist: antlr4-tools
|
|
23
22
|
Requires-Dist: antlr4-python3-runtime
|
|
24
23
|
Requires-Dist: docopt
|
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
mal_toolbox-1.0.
|
|
2
|
-
mal_toolbox-1.0.
|
|
3
|
-
maltoolbox/__init__.py,sha256=
|
|
1
|
+
mal_toolbox-1.0.1.dist-info/licenses/AUTHORS,sha256=zxLrLe8EY39WtRKlAY4Oorx4Z2_LHV2ApRvDGZgY7xY,127
|
|
2
|
+
mal_toolbox-1.0.1.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
3
|
+
maltoolbox/__init__.py,sha256=AO8CtBtHZ2gaI_kkyQW51Wn44s9xb0ErllNbR_AB0Do,2043
|
|
4
4
|
maltoolbox/__main__.py,sha256=QBloKCJ_RMsFPZ8qiWZQnoP2gnnRyECIJBfA1zTAYJM,2394
|
|
5
5
|
maltoolbox/exceptions.py,sha256=0YjPx2v1yYumZ2o7pVZ1s_jS-GAb3Ng979KEFhROSNY,1399
|
|
6
6
|
maltoolbox/file_utils.py,sha256=fYG3UsvPQcU0ES_WI3nLfuzSZgc0jtE4IAxdMGgs9aA,1876
|
|
7
|
-
maltoolbox/model.py,sha256=
|
|
7
|
+
maltoolbox/model.py,sha256=xTK2jUr0Gz5pPVhdjh78zO5G46nV8N2ciIt6M5SwcGU,16058
|
|
8
8
|
maltoolbox/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
9
|
maltoolbox/attackgraph/__init__.py,sha256=m_81AjzwXONdclcW_R7mF2f8p-4DvoSRVfQ3Nyh7fak,298
|
|
10
|
-
maltoolbox/attackgraph/attackgraph.py,sha256=
|
|
10
|
+
maltoolbox/attackgraph/attackgraph.py,sha256=fuhpfD3YnQEdDdG7QPdYSylOX3XPL2mbx4cX0_bmX3c,26870
|
|
11
11
|
maltoolbox/attackgraph/node.py,sha256=Z2sdzXhPel9h7ySxP9fjgd1exVmpRbvRySVtLpI1_BM,3904
|
|
12
12
|
maltoolbox/attackgraph/analyzers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
13
|
maltoolbox/ingestors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
14
|
maltoolbox/language/__init__.py,sha256=TsTTryEyjChwHN1o5F2BSUlFsAss2N6J0H0-nzvXiD8,489
|
|
15
|
-
maltoolbox/language/languagegraph.py,sha256=
|
|
15
|
+
maltoolbox/language/languagegraph.py,sha256=WLV0-CvfrB4yqaDW3NTLyVOVDduNeD1By3Yqmjzn00Y,73810
|
|
16
16
|
maltoolbox/language/compiler/__init__.py,sha256=JQyAgDwJh1pU7AmuOhd1-d2b2PYXpgMVPtxnav8QHVc,15872
|
|
17
17
|
maltoolbox/language/compiler/mal_lexer.py,sha256=BeifykDAt4PloRASOaLzBgWF35ev_zgD8lXMIsSHykc,12063
|
|
18
18
|
maltoolbox/language/compiler/mal_parser.py,sha256=sUoaE43l2VKg-Dou30mk2wlVS1FvdOREwHNIyFe4IkY,114699
|
|
19
19
|
maltoolbox/translators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
20
|
maltoolbox/translators/securicad.py,sha256=F_rndv2JyKxfHAXPwf2RrdiFPnemJVArYUpVsFP6QQk,6997
|
|
21
21
|
maltoolbox/translators/updater.py,sha256=UZPnx22udROiocCcSmtrgUJUupkjktkxl-M7rhBxUPc,8660
|
|
22
|
-
mal_toolbox-1.0.
|
|
23
|
-
mal_toolbox-1.0.
|
|
24
|
-
mal_toolbox-1.0.
|
|
25
|
-
mal_toolbox-1.0.
|
|
26
|
-
mal_toolbox-1.0.
|
|
22
|
+
mal_toolbox-1.0.1.dist-info/METADATA,sha256=LSJ7hnJewGb32qVSdpv-3KBinzJ_ZHUbsWxWhHEebII,5097
|
|
23
|
+
mal_toolbox-1.0.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
24
|
+
mal_toolbox-1.0.1.dist-info/entry_points.txt,sha256=oqby5O6cUP_OHCm70k_iYPA6UlbTBf7se1i3XwdK3uU,56
|
|
25
|
+
mal_toolbox-1.0.1.dist-info/top_level.txt,sha256=phqRVLRKGdSUgRY03mcpi2cmbbDo5YGjkV4gkqHFFcM,11
|
|
26
|
+
mal_toolbox-1.0.1.dist-info/RECORD,,
|
maltoolbox/__init__.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
|
2
|
-
# MAL Toolbox v1.0.
|
|
2
|
+
# MAL Toolbox v1.0.1
|
|
3
3
|
# Copyright 2025, 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__ = "1.0.
|
|
24
|
+
__version__ = "1.0.1"
|
|
25
25
|
__authors__ = [
|
|
26
26
|
"Andrei Buhaiu",
|
|
27
27
|
"Giuseppe Nebbione",
|
|
@@ -55,12 +55,12 @@ def create_attack_graph(
|
|
|
55
55
|
else:
|
|
56
56
|
raise TypeError("`lang` must be either string or LanguageGraph")
|
|
57
57
|
|
|
58
|
-
if
|
|
58
|
+
if 'langspec_file' in log_configs:
|
|
59
59
|
lang_graph.save_language_specification_to_json(
|
|
60
60
|
log_configs['langspec_file']
|
|
61
61
|
)
|
|
62
62
|
|
|
63
|
-
if
|
|
63
|
+
if 'langgraph_file' in log_configs:
|
|
64
64
|
lang_graph.save_to_file(log_configs['langgraph_file'])
|
|
65
65
|
|
|
66
66
|
# Load model
|
|
@@ -469,20 +469,24 @@ class AttackGraph():
|
|
|
469
469
|
existence_status = None
|
|
470
470
|
node_name = asset.name + ':' + attack_step.name
|
|
471
471
|
|
|
472
|
-
ttc_dist = attack_step.ttc
|
|
472
|
+
ttc_dist = copy.deepcopy(attack_step.ttc)
|
|
473
473
|
match (attack_step.type):
|
|
474
474
|
case 'defense':
|
|
475
475
|
# Set the TTC probability for defenses
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
476
|
+
# that were explicitly set in model
|
|
477
|
+
if attack_step.name in asset.defenses:
|
|
478
|
+
defense_value = float(
|
|
479
|
+
asset.defenses[attack_step.name]
|
|
480
|
+
)
|
|
481
|
+
ttc_dist = {
|
|
482
|
+
'arguments': [defense_value],
|
|
483
|
+
'name': 'Bernoulli',
|
|
484
|
+
'type': 'function'
|
|
485
|
+
}
|
|
486
|
+
logger.debug(
|
|
487
|
+
'Setting defense \"%s\" to "%s".',
|
|
488
|
+
node_name, defense_value
|
|
489
|
+
)
|
|
486
490
|
|
|
487
491
|
case 'exist' | 'notExist':
|
|
488
492
|
# Resolve step expression associated with
|
|
@@ -97,12 +97,60 @@ predef_ttcs: dict[str, dict] = {
|
|
|
97
97
|
},
|
|
98
98
|
'Disabled':
|
|
99
99
|
{
|
|
100
|
-
'arguments': [
|
|
100
|
+
'arguments': [0.0],
|
|
101
101
|
'name': 'Bernoulli',
|
|
102
102
|
'type': 'function'
|
|
103
103
|
},
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
+
def get_ttc_distribution(
|
|
107
|
+
step_dict: dict,
|
|
108
|
+
defense_default_ttc = None,
|
|
109
|
+
attack_default_ttc = None
|
|
110
|
+
) -> Optional[dict]:
|
|
111
|
+
"""Convert step TTC to a TTC distribution if needed
|
|
112
|
+
|
|
113
|
+
- If no TTC is set, set return default TTC.
|
|
114
|
+
- If the TTC provided is a predefined name replace it with the
|
|
115
|
+
probability distribution it corresponds to.
|
|
116
|
+
- Otherwise return the TTC distribution as is.
|
|
117
|
+
|
|
118
|
+
Arguments:
|
|
119
|
+
step_dict - A dict with the attack step data
|
|
120
|
+
defense_default_ttc - the value to give a defense ttc if none is set
|
|
121
|
+
attack_default_ttc - the value to give an attack ttc if none is set
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
A dict with the steps TTC distribution, or None if the step is not
|
|
125
|
+
a defense or attack step
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
if defense_default_ttc is None:
|
|
129
|
+
defense_default_ttc = predef_ttcs['Disabled'].copy()
|
|
130
|
+
if attack_default_ttc is None:
|
|
131
|
+
attack_default_ttc = predef_ttcs['Instant'].copy()
|
|
132
|
+
|
|
133
|
+
step_ttc = step_dict['ttc']
|
|
134
|
+
|
|
135
|
+
if step_dict['type'] == 'defense':
|
|
136
|
+
if step_ttc is None:
|
|
137
|
+
# No step ttc set in language for defense
|
|
138
|
+
step_ttc = defense_default_ttc
|
|
139
|
+
elif step_dict['type'] in ('or', 'and'):
|
|
140
|
+
if step_ttc is None:
|
|
141
|
+
# No step ttc set in language for attack
|
|
142
|
+
step_ttc = attack_default_ttc
|
|
143
|
+
else:
|
|
144
|
+
# No TTC for other step types
|
|
145
|
+
return None
|
|
146
|
+
|
|
147
|
+
if 'name' in step_ttc and step_ttc['name'] in predef_ttcs:
|
|
148
|
+
# Predefined step ttc set in language, fetch from dict
|
|
149
|
+
step_ttc = predef_ttcs[step_ttc['name']].copy()
|
|
150
|
+
|
|
151
|
+
return step_ttc
|
|
152
|
+
|
|
153
|
+
|
|
106
154
|
|
|
107
155
|
def disaggregate_attack_step_full_name(
|
|
108
156
|
attack_step_full_name: str) -> list[str]:
|
|
@@ -435,7 +483,7 @@ class LanguageGraphAttackStep:
|
|
|
435
483
|
name: str
|
|
436
484
|
type: str
|
|
437
485
|
asset: LanguageGraphAsset
|
|
438
|
-
ttc: dict = field(default_factory = dict)
|
|
486
|
+
ttc: Optional[dict] = field(default_factory = dict)
|
|
439
487
|
overrides: bool = False
|
|
440
488
|
children: dict = field(default_factory = dict)
|
|
441
489
|
parents: dict = field(default_factory = dict)
|
|
@@ -728,7 +776,6 @@ class LanguageGraph():
|
|
|
728
776
|
"""Graph representation of a MAL language"""
|
|
729
777
|
def __init__(self, lang: Optional[dict] = None):
|
|
730
778
|
self.assets: dict[str, LanguageGraphAsset] = {}
|
|
731
|
-
self.predef_ttcs: dict[str, dict] = predef_ttcs
|
|
732
779
|
if lang is not None:
|
|
733
780
|
self._lang_spec: dict = lang
|
|
734
781
|
self.metadata = {
|
|
@@ -769,34 +816,6 @@ class LanguageGraph():
|
|
|
769
816
|
langspec = archive.read('langspec.json')
|
|
770
817
|
return LanguageGraph(json.loads(langspec))
|
|
771
818
|
|
|
772
|
-
|
|
773
|
-
def replace_if_predef_ttc(
|
|
774
|
-
self,
|
|
775
|
-
ttc_entry: Optional[dict]
|
|
776
|
-
) -> dict:
|
|
777
|
-
"""
|
|
778
|
-
If the TTC provided is a predefined name replace it with the
|
|
779
|
-
probability distribution it corresponds to. Otherwise, simply return
|
|
780
|
-
the TTC distribution provided as is.
|
|
781
|
-
|
|
782
|
-
Arguments:
|
|
783
|
-
ttc_entry - the TTC entry to check for predefined names
|
|
784
|
-
|
|
785
|
-
Returns:
|
|
786
|
-
If the TTC entry provided contained a predefined name the TTC
|
|
787
|
-
probability distrubtion corresponding to it. Otherwise, the TTC
|
|
788
|
-
distribution provided as a parameter as is.
|
|
789
|
-
"""
|
|
790
|
-
if ttc_entry is None:
|
|
791
|
-
return {}
|
|
792
|
-
|
|
793
|
-
ttc = self.predef_ttcs.get(str(ttc_entry.get('name')))
|
|
794
|
-
if ttc is not None:
|
|
795
|
-
return ttc
|
|
796
|
-
else:
|
|
797
|
-
return ttc_entry
|
|
798
|
-
|
|
799
|
-
|
|
800
819
|
def _to_dict(self):
|
|
801
820
|
"""Converts LanguageGraph into a dict"""
|
|
802
821
|
|
|
@@ -940,13 +959,11 @@ class LanguageGraph():
|
|
|
940
959
|
asset_dict['name']
|
|
941
960
|
)
|
|
942
961
|
for attack_step_dict in asset_dict['attack_steps'].values():
|
|
943
|
-
ttc = lang_graph.replace_if_predef_ttc(
|
|
944
|
-
attack_step_dict['ttc'])
|
|
945
962
|
attack_step_node = LanguageGraphAttackStep(
|
|
946
963
|
name = attack_step_dict['name'],
|
|
947
964
|
type = attack_step_dict['type'],
|
|
948
965
|
asset = asset,
|
|
949
|
-
ttc =
|
|
966
|
+
ttc = get_ttc_distribution(attack_step_dict),
|
|
950
967
|
overrides = attack_step_dict['overrides'],
|
|
951
968
|
children = {},
|
|
952
969
|
parents = {},
|
|
@@ -1663,12 +1680,11 @@ class LanguageGraph():
|
|
|
1663
1680
|
attack_step_attribs['name']
|
|
1664
1681
|
)
|
|
1665
1682
|
|
|
1666
|
-
ttc = self.replace_if_predef_ttc(attack_step_attribs['ttc'])
|
|
1667
1683
|
attack_step_node = LanguageGraphAttackStep(
|
|
1668
1684
|
name = attack_step_attribs['name'],
|
|
1669
1685
|
type = attack_step_attribs['type'],
|
|
1670
1686
|
asset = asset,
|
|
1671
|
-
ttc =
|
|
1687
|
+
ttc = get_ttc_distribution(attack_step_attribs),
|
|
1672
1688
|
overrides = (
|
|
1673
1689
|
attack_step_attribs['reaches']['overrides']
|
|
1674
1690
|
if attack_step_attribs['reaches'] else False
|
maltoolbox/model.py
CHANGED
|
@@ -326,12 +326,6 @@ class ModelAsset:
|
|
|
326
326
|
self._associated_assets: dict[str, set[ModelAsset]] = {}
|
|
327
327
|
self.attack_step_nodes: list = []
|
|
328
328
|
|
|
329
|
-
for step in self.lg_asset.attack_steps.values():
|
|
330
|
-
if step.type == 'defense' and step.name not in self.defenses:
|
|
331
|
-
self.defenses[step.name] = 1.0 if step.ttc and \
|
|
332
|
-
step.ttc['name'] == 'Enabled' else 0.0
|
|
333
|
-
|
|
334
|
-
|
|
335
329
|
def _to_dict(self):
|
|
336
330
|
"""Get dictionary representation of the asset."""
|
|
337
331
|
|
|
@@ -345,14 +339,8 @@ class ModelAsset:
|
|
|
345
339
|
'associated_assets': {}
|
|
346
340
|
}
|
|
347
341
|
|
|
348
|
-
# Only add non-default values for defenses to improve legibility of
|
|
349
|
-
# the model format
|
|
350
342
|
for defense, defense_value in self.defenses.items():
|
|
351
|
-
|
|
352
|
-
default_defval = 1.0 if lg_step.ttc and \
|
|
353
|
-
lg_step.ttc['name'] == 'Enabled' else 0.0
|
|
354
|
-
if defense_value != default_defval:
|
|
355
|
-
asset_dict['defenses'][defense] = defense_value
|
|
343
|
+
asset_dict['defenses'][defense] = defense_value
|
|
356
344
|
|
|
357
345
|
for fieldname, assets in self.associated_assets.items():
|
|
358
346
|
asset_dict['associated_assets'][fieldname] = {asset.id: asset.name
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|