mal-toolbox 1.0.0__tar.gz → 1.0.1__tar.gz

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.
Files changed (32) hide show
  1. {mal_toolbox-1.0.0/mal_toolbox.egg-info → mal_toolbox-1.0.1}/PKG-INFO +1 -2
  2. {mal_toolbox-1.0.0 → mal_toolbox-1.0.1/mal_toolbox.egg-info}/PKG-INFO +1 -2
  3. {mal_toolbox-1.0.0 → mal_toolbox-1.0.1}/mal_toolbox.egg-info/requires.txt +0 -1
  4. {mal_toolbox-1.0.0 → mal_toolbox-1.0.1}/maltoolbox/__init__.py +2 -2
  5. {mal_toolbox-1.0.0 → mal_toolbox-1.0.1}/maltoolbox/attackgraph/attackgraph.py +17 -13
  6. {mal_toolbox-1.0.0 → mal_toolbox-1.0.1}/maltoolbox/language/languagegraph.py +52 -36
  7. {mal_toolbox-1.0.0 → mal_toolbox-1.0.1}/maltoolbox/model.py +1 -13
  8. {mal_toolbox-1.0.0 → mal_toolbox-1.0.1}/pyproject.toml +1 -2
  9. {mal_toolbox-1.0.0 → mal_toolbox-1.0.1}/AUTHORS +0 -0
  10. {mal_toolbox-1.0.0 → mal_toolbox-1.0.1}/LICENSE +0 -0
  11. {mal_toolbox-1.0.0 → mal_toolbox-1.0.1}/README.md +0 -0
  12. {mal_toolbox-1.0.0 → mal_toolbox-1.0.1}/mal_toolbox.egg-info/SOURCES.txt +0 -0
  13. {mal_toolbox-1.0.0 → mal_toolbox-1.0.1}/mal_toolbox.egg-info/dependency_links.txt +0 -0
  14. {mal_toolbox-1.0.0 → mal_toolbox-1.0.1}/mal_toolbox.egg-info/entry_points.txt +0 -0
  15. {mal_toolbox-1.0.0 → mal_toolbox-1.0.1}/mal_toolbox.egg-info/top_level.txt +0 -0
  16. {mal_toolbox-1.0.0 → mal_toolbox-1.0.1}/maltoolbox/__main__.py +0 -0
  17. {mal_toolbox-1.0.0 → mal_toolbox-1.0.1}/maltoolbox/attackgraph/__init__.py +0 -0
  18. {mal_toolbox-1.0.0 → mal_toolbox-1.0.1}/maltoolbox/attackgraph/analyzers/__init__.py +0 -0
  19. {mal_toolbox-1.0.0 → mal_toolbox-1.0.1}/maltoolbox/attackgraph/node.py +0 -0
  20. {mal_toolbox-1.0.0 → mal_toolbox-1.0.1}/maltoolbox/exceptions.py +0 -0
  21. {mal_toolbox-1.0.0 → mal_toolbox-1.0.1}/maltoolbox/file_utils.py +0 -0
  22. {mal_toolbox-1.0.0 → mal_toolbox-1.0.1}/maltoolbox/ingestors/__init__.py +0 -0
  23. {mal_toolbox-1.0.0 → mal_toolbox-1.0.1}/maltoolbox/language/__init__.py +0 -0
  24. {mal_toolbox-1.0.0 → mal_toolbox-1.0.1}/maltoolbox/language/compiler/__init__.py +0 -0
  25. {mal_toolbox-1.0.0 → mal_toolbox-1.0.1}/maltoolbox/language/compiler/mal_lexer.py +0 -0
  26. {mal_toolbox-1.0.0 → mal_toolbox-1.0.1}/maltoolbox/language/compiler/mal_parser.py +0 -0
  27. {mal_toolbox-1.0.0 → mal_toolbox-1.0.1}/maltoolbox/py.typed +0 -0
  28. {mal_toolbox-1.0.0 → mal_toolbox-1.0.1}/maltoolbox/translators/__init__.py +0 -0
  29. {mal_toolbox-1.0.0 → mal_toolbox-1.0.1}/maltoolbox/translators/securicad.py +0 -0
  30. {mal_toolbox-1.0.0 → mal_toolbox-1.0.1}/maltoolbox/translators/updater.py +0 -0
  31. {mal_toolbox-1.0.0 → mal_toolbox-1.0.1}/setup.cfg +0 -0
  32. {mal_toolbox-1.0.0 → mal_toolbox-1.0.1}/tests/test_model.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mal-toolbox
3
- Version: 1.0.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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mal-toolbox
3
- Version: 1.0.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,4 +1,3 @@
1
- py2neo>=2021.2.3
2
1
  antlr4-tools
3
2
  antlr4-python3-runtime
4
3
  docopt
@@ -1,5 +1,5 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # MAL Toolbox v1.0.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.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 log_configs['langspec_file']:
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 log_configs['langgraph_file']:
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
- defense_value = float(asset.defenses[attack_step.name])
477
- ttc_dist = {
478
- 'arguments': [defense_value],
479
- 'name': 'Bernoulli',
480
- 'type': 'function'
481
- }
482
- logger.debug(
483
- 'Setting defense \"%s\" to "%s".',
484
- node_name, defense_value
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': [1.0],
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 = 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 = 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
@@ -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
- lg_step = self.lg_asset.attack_steps[defense]
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mal-toolbox"
3
- version = "1.0.0"
3
+ version = "1.0.1"
4
4
  authors = [
5
5
  { name="Andrei Buhaiu", email="buhaiu@kth.se" },
6
6
  { name="Joakim Loxdal", email="loxdal@kth.se" },
@@ -12,7 +12,6 @@ description = "A collection of tools used to create MAL models and attack graphs
12
12
  readme = "README.md"
13
13
  requires-python = ">=3.10"
14
14
  dependencies = [
15
- "py2neo>=2021.2.3",
16
15
  "antlr4-tools",
17
16
  "antlr4-python3-runtime",
18
17
  "docopt",
File without changes
File without changes
File without changes
File without changes