mal-toolbox 1.0.0__tar.gz → 1.0.2__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.2}/PKG-INFO +18 -6
  2. {mal_toolbox-1.0.0 → mal_toolbox-1.0.2}/README.md +17 -4
  3. {mal_toolbox-1.0.0 → mal_toolbox-1.0.2/mal_toolbox.egg-info}/PKG-INFO +18 -6
  4. {mal_toolbox-1.0.0 → mal_toolbox-1.0.2}/mal_toolbox.egg-info/requires.txt +0 -1
  5. {mal_toolbox-1.0.0 → mal_toolbox-1.0.2}/maltoolbox/__init__.py +2 -2
  6. {mal_toolbox-1.0.0 → mal_toolbox-1.0.2}/maltoolbox/attackgraph/attackgraph.py +18 -14
  7. {mal_toolbox-1.0.0 → mal_toolbox-1.0.2}/maltoolbox/language/languagegraph.py +59 -46
  8. {mal_toolbox-1.0.0 → mal_toolbox-1.0.2}/maltoolbox/model.py +1 -13
  9. {mal_toolbox-1.0.0 → mal_toolbox-1.0.2}/pyproject.toml +1 -2
  10. {mal_toolbox-1.0.0 → mal_toolbox-1.0.2}/AUTHORS +0 -0
  11. {mal_toolbox-1.0.0 → mal_toolbox-1.0.2}/LICENSE +0 -0
  12. {mal_toolbox-1.0.0 → mal_toolbox-1.0.2}/mal_toolbox.egg-info/SOURCES.txt +0 -0
  13. {mal_toolbox-1.0.0 → mal_toolbox-1.0.2}/mal_toolbox.egg-info/dependency_links.txt +0 -0
  14. {mal_toolbox-1.0.0 → mal_toolbox-1.0.2}/mal_toolbox.egg-info/entry_points.txt +0 -0
  15. {mal_toolbox-1.0.0 → mal_toolbox-1.0.2}/mal_toolbox.egg-info/top_level.txt +0 -0
  16. {mal_toolbox-1.0.0 → mal_toolbox-1.0.2}/maltoolbox/__main__.py +0 -0
  17. {mal_toolbox-1.0.0 → mal_toolbox-1.0.2}/maltoolbox/attackgraph/__init__.py +0 -0
  18. {mal_toolbox-1.0.0 → mal_toolbox-1.0.2}/maltoolbox/attackgraph/analyzers/__init__.py +0 -0
  19. {mal_toolbox-1.0.0 → mal_toolbox-1.0.2}/maltoolbox/attackgraph/node.py +0 -0
  20. {mal_toolbox-1.0.0 → mal_toolbox-1.0.2}/maltoolbox/exceptions.py +0 -0
  21. {mal_toolbox-1.0.0 → mal_toolbox-1.0.2}/maltoolbox/file_utils.py +0 -0
  22. {mal_toolbox-1.0.0 → mal_toolbox-1.0.2}/maltoolbox/ingestors/__init__.py +0 -0
  23. {mal_toolbox-1.0.0 → mal_toolbox-1.0.2}/maltoolbox/language/__init__.py +0 -0
  24. {mal_toolbox-1.0.0 → mal_toolbox-1.0.2}/maltoolbox/language/compiler/__init__.py +0 -0
  25. {mal_toolbox-1.0.0 → mal_toolbox-1.0.2}/maltoolbox/language/compiler/mal_lexer.py +0 -0
  26. {mal_toolbox-1.0.0 → mal_toolbox-1.0.2}/maltoolbox/language/compiler/mal_parser.py +0 -0
  27. {mal_toolbox-1.0.0 → mal_toolbox-1.0.2}/maltoolbox/py.typed +0 -0
  28. {mal_toolbox-1.0.0 → mal_toolbox-1.0.2}/maltoolbox/translators/__init__.py +0 -0
  29. {mal_toolbox-1.0.0 → mal_toolbox-1.0.2}/maltoolbox/translators/securicad.py +0 -0
  30. {mal_toolbox-1.0.0 → mal_toolbox-1.0.2}/maltoolbox/translators/updater.py +0 -0
  31. {mal_toolbox-1.0.0 → mal_toolbox-1.0.2}/setup.cfg +0 -0
  32. {mal_toolbox-1.0.0 → mal_toolbox-1.0.2}/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.2
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
@@ -87,13 +86,26 @@ pip install mal-toolbox
87
86
 
88
87
  ## Configuration
89
88
  You can use a `maltoolbox.yml` file in the current working directory to
90
- configure the toolbox. Alternatively, you can use the `MALTOOLBOX_CONFIG`
91
- environment variable to set a custom config file location:
89
+ configure the toolbox.
90
+
91
+ The config should look like this:
92
+ ```yml
93
+ logging:
94
+ log_level: INFO
95
+ log_file: "logs/log.txt"
96
+ attackgraph_file: "logs/attackgraph.json"
97
+ model_file: "logs/model.yml"
98
+ langspec_file: "logs/langspec_file.yml"
99
+ langgraph_file: "logs/langspec_file.yml"
100
+ ```
101
+
102
+ Alternatively, you can use the `MALTOOLBOX_CONFIG`
103
+ environment variable to set a custom config file location.
92
104
 
93
- """bash
105
+ ```bash
94
106
  # in your shell, e.g. bash do:
95
107
  export MALTOOLBOX_CONFIG=path/to/yml/config/file
96
- """
108
+ ```
97
109
 
98
110
  The default configuration can be found here:
99
111
 
@@ -60,13 +60,26 @@ pip install mal-toolbox
60
60
 
61
61
  ## Configuration
62
62
  You can use a `maltoolbox.yml` file in the current working directory to
63
- configure the toolbox. Alternatively, you can use the `MALTOOLBOX_CONFIG`
64
- environment variable to set a custom config file location:
63
+ configure the toolbox.
64
+
65
+ The config should look like this:
66
+ ```yml
67
+ logging:
68
+ log_level: INFO
69
+ log_file: "logs/log.txt"
70
+ attackgraph_file: "logs/attackgraph.json"
71
+ model_file: "logs/model.yml"
72
+ langspec_file: "logs/langspec_file.yml"
73
+ langgraph_file: "logs/langspec_file.yml"
74
+ ```
75
+
76
+ Alternatively, you can use the `MALTOOLBOX_CONFIG`
77
+ environment variable to set a custom config file location.
65
78
 
66
- """bash
79
+ ```bash
67
80
  # in your shell, e.g. bash do:
68
81
  export MALTOOLBOX_CONFIG=path/to/yml/config/file
69
- """
82
+ ```
70
83
 
71
84
  The default configuration can be found here:
72
85
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mal-toolbox
3
- Version: 1.0.0
3
+ Version: 1.0.2
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
@@ -87,13 +86,26 @@ pip install mal-toolbox
87
86
 
88
87
  ## Configuration
89
88
  You can use a `maltoolbox.yml` file in the current working directory to
90
- configure the toolbox. Alternatively, you can use the `MALTOOLBOX_CONFIG`
91
- environment variable to set a custom config file location:
89
+ configure the toolbox.
90
+
91
+ The config should look like this:
92
+ ```yml
93
+ logging:
94
+ log_level: INFO
95
+ log_file: "logs/log.txt"
96
+ attackgraph_file: "logs/attackgraph.json"
97
+ model_file: "logs/model.yml"
98
+ langspec_file: "logs/langspec_file.yml"
99
+ langgraph_file: "logs/langspec_file.yml"
100
+ ```
101
+
102
+ Alternatively, you can use the `MALTOOLBOX_CONFIG`
103
+ environment variable to set a custom config file location.
92
104
 
93
- """bash
105
+ ```bash
94
106
  # in your shell, e.g. bash do:
95
107
  export MALTOOLBOX_CONFIG=path/to/yml/config/file
96
- """
108
+ ```
97
109
 
98
110
  The default configuration can be found here:
99
111
 
@@ -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.2
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.2"
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
@@ -194,7 +194,7 @@ class AttackGraph():
194
194
  ttc_dist = node_dict['ttc'],
195
195
  existence_status = node_dict.get('existence_status', None)
196
196
  )
197
- ag_node.tags = set(node_dict.get('tags', []))
197
+ ag_node.tags = list(node_dict.get('tags', []))
198
198
  ag_node.extras = node_dict.get('extras', {})
199
199
 
200
200
  if node_asset:
@@ -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,14 +483,14 @@ 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)
442
490
  info: dict = field(default_factory = dict)
443
491
  inherits: Optional[LanguageGraphAttackStep] = None
444
492
  own_requires: list[ExpressionsChain] = field(default_factory=list)
445
- tags: set = field(default_factory = set)
493
+ tags: list = field(default_factory = list)
446
494
  detectors: dict = field(default_factory = lambda: {})
447
495
 
448
496
 
@@ -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,18 +959,16 @@ 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 = {},
953
970
  info = attack_step_dict['info'],
954
- tags = set(attack_step_dict['tags'])
971
+ tags = list(attack_step_dict['tags'])
955
972
  )
956
973
  asset.attack_steps[attack_step_dict['name']] = \
957
974
  attack_step_node
@@ -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
@@ -1676,7 +1692,7 @@ class LanguageGraph():
1676
1692
  children = {},
1677
1693
  parents = {},
1678
1694
  info = attack_step_attribs['meta'],
1679
- tags = set(attack_step_attribs['tags'])
1695
+ tags = list(attack_step_attribs['tags'])
1680
1696
  )
1681
1697
  langspec_dict[attack_step_node.full_name] = \
1682
1698
  attack_step_attribs
@@ -1719,7 +1735,7 @@ class LanguageGraph():
1719
1735
  children = {},
1720
1736
  parents = {},
1721
1737
  info = attack_step.info,
1722
- tags = set(attack_step.tags)
1738
+ tags = list(attack_step.tags)
1723
1739
  )
1724
1740
  attack_step_node.inherits = attack_step
1725
1741
  asset.attack_steps[attack_step.name] = attack_step_node
@@ -1727,12 +1743,9 @@ class LanguageGraph():
1727
1743
  # The inherited attack step was already overridden.
1728
1744
  continue
1729
1745
  else:
1730
- asset.attack_steps[attack_step.name].inherits = \
1731
- attack_step
1732
- asset.attack_steps[attack_step.name].tags |= \
1733
- attack_step.tags
1734
- asset.attack_steps[attack_step.name].info |= \
1735
- attack_step.info
1746
+ asset.attack_steps[attack_step.name].inherits = attack_step
1747
+ asset.attack_steps[attack_step.name].tags += attack_step.tags
1748
+ asset.attack_steps[attack_step.name].info |= attack_step.info
1736
1749
 
1737
1750
  # Then, link all of the attack step nodes according to their
1738
1751
  # associations.
@@ -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.2"
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