mal-toolbox 0.1.6__tar.gz → 0.1.8__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 (39) hide show
  1. {mal_toolbox-0.1.6/mal_toolbox.egg-info → mal_toolbox-0.1.8}/PKG-INFO +3 -1
  2. {mal_toolbox-0.1.6 → mal_toolbox-0.1.8}/README.md +3 -1
  3. {mal_toolbox-0.1.6 → mal_toolbox-0.1.8/mal_toolbox.egg-info}/PKG-INFO +3 -1
  4. {mal_toolbox-0.1.6 → mal_toolbox-0.1.8}/maltoolbox/__init__.py +2 -2
  5. {mal_toolbox-0.1.6 → mal_toolbox-0.1.8}/maltoolbox/model.py +103 -2
  6. {mal_toolbox-0.1.6 → mal_toolbox-0.1.8}/pyproject.toml +1 -1
  7. {mal_toolbox-0.1.6 → mal_toolbox-0.1.8}/tests/test_model.py +151 -7
  8. {mal_toolbox-0.1.6 → mal_toolbox-0.1.8}/AUTHORS +0 -0
  9. {mal_toolbox-0.1.6 → mal_toolbox-0.1.8}/LICENSE +0 -0
  10. {mal_toolbox-0.1.6 → mal_toolbox-0.1.8}/mal_toolbox.egg-info/SOURCES.txt +0 -0
  11. {mal_toolbox-0.1.6 → mal_toolbox-0.1.8}/mal_toolbox.egg-info/dependency_links.txt +0 -0
  12. {mal_toolbox-0.1.6 → mal_toolbox-0.1.8}/mal_toolbox.egg-info/requires.txt +0 -0
  13. {mal_toolbox-0.1.6 → mal_toolbox-0.1.8}/mal_toolbox.egg-info/top_level.txt +0 -0
  14. {mal_toolbox-0.1.6 → mal_toolbox-0.1.8}/maltoolbox/__main__.py +0 -0
  15. {mal_toolbox-0.1.6 → mal_toolbox-0.1.8}/maltoolbox/attackgraph/__init__.py +0 -0
  16. {mal_toolbox-0.1.6 → mal_toolbox-0.1.8}/maltoolbox/attackgraph/analyzers/__init__.py +0 -0
  17. {mal_toolbox-0.1.6 → mal_toolbox-0.1.8}/maltoolbox/attackgraph/analyzers/apriori.py +0 -0
  18. {mal_toolbox-0.1.6 → mal_toolbox-0.1.8}/maltoolbox/attackgraph/attacker.py +0 -0
  19. {mal_toolbox-0.1.6 → mal_toolbox-0.1.8}/maltoolbox/attackgraph/attackgraph.py +0 -0
  20. {mal_toolbox-0.1.6 → mal_toolbox-0.1.8}/maltoolbox/attackgraph/node.py +0 -0
  21. {mal_toolbox-0.1.6 → mal_toolbox-0.1.8}/maltoolbox/attackgraph/query.py +0 -0
  22. {mal_toolbox-0.1.6 → mal_toolbox-0.1.8}/maltoolbox/default.conf +0 -0
  23. {mal_toolbox-0.1.6 → mal_toolbox-0.1.8}/maltoolbox/exceptions.py +0 -0
  24. {mal_toolbox-0.1.6 → mal_toolbox-0.1.8}/maltoolbox/file_utils.py +0 -0
  25. {mal_toolbox-0.1.6 → mal_toolbox-0.1.8}/maltoolbox/ingestors/__init__.py +0 -0
  26. {mal_toolbox-0.1.6 → mal_toolbox-0.1.8}/maltoolbox/ingestors/neo4j.py +0 -0
  27. {mal_toolbox-0.1.6 → mal_toolbox-0.1.8}/maltoolbox/language/__init__.py +0 -0
  28. {mal_toolbox-0.1.6 → mal_toolbox-0.1.8}/maltoolbox/language/classes_factory.py +0 -0
  29. {mal_toolbox-0.1.6 → mal_toolbox-0.1.8}/maltoolbox/language/compiler/__init__.py +0 -0
  30. {mal_toolbox-0.1.6 → mal_toolbox-0.1.8}/maltoolbox/language/compiler/mal_lexer.py +0 -0
  31. {mal_toolbox-0.1.6 → mal_toolbox-0.1.8}/maltoolbox/language/compiler/mal_parser.py +0 -0
  32. {mal_toolbox-0.1.6 → mal_toolbox-0.1.8}/maltoolbox/language/compiler/mal_visitor.py +0 -0
  33. {mal_toolbox-0.1.6 → mal_toolbox-0.1.8}/maltoolbox/language/languagegraph.py +0 -0
  34. {mal_toolbox-0.1.6 → mal_toolbox-0.1.8}/maltoolbox/translators/__init__.py +0 -0
  35. {mal_toolbox-0.1.6 → mal_toolbox-0.1.8}/maltoolbox/translators/securicad.py +0 -0
  36. {mal_toolbox-0.1.6 → mal_toolbox-0.1.8}/maltoolbox/translators/updater.py +0 -0
  37. {mal_toolbox-0.1.6 → mal_toolbox-0.1.8}/maltoolbox/wrappers.py +0 -0
  38. {mal_toolbox-0.1.6 → mal_toolbox-0.1.8}/setup.cfg +0 -0
  39. {mal_toolbox-0.1.6 → mal_toolbox-0.1.8}/tests/test_wrappers.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mal-toolbox
3
- Version: 0.1.6
3
+ Version: 0.1.8
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
@@ -33,6 +33,8 @@ MAL ([Meta Attack Language](https://mal-lang.org/)) models and attack graphs.
33
33
  Attack graphs can be used to run simulations (see MAL Simulator) or analysis.
34
34
  MAL Toolbox also gives the ability to view the AttackGraph/Model graphically in neo4j.
35
35
 
36
+ [Documentation](https://mal-lang.org/mal-toolbox/index.html)(Work in progress)
37
+
36
38
  ## The Language Module
37
39
 
38
40
  The language module provides various tools to process MAL languages.
@@ -6,6 +6,8 @@ MAL ([Meta Attack Language](https://mal-lang.org/)) models and attack graphs.
6
6
  Attack graphs can be used to run simulations (see MAL Simulator) or analysis.
7
7
  MAL Toolbox also gives the ability to view the AttackGraph/Model graphically in neo4j.
8
8
 
9
+ [Documentation](https://mal-lang.org/mal-toolbox/index.html)(Work in progress)
10
+
9
11
  ## The Language Module
10
12
 
11
13
  The language module provides various tools to process MAL languages.
@@ -99,4 +101,4 @@ To find code examples and tutorials, visit the
99
101
  There are unit tests inside of ./tests.
100
102
  Before running the tests, make sure to install the requirements in ./tests/requirements.txt with `python -m pip install -r ./tests/requirements.txt`.
101
103
 
102
- To run all tests, use the `pytest` command. To run just a specific file or test function use `pytest tests/<filename>` or `pytest -k <function_name>`.
104
+ To run all tests, use the `pytest` command. To run just a specific file or test function use `pytest tests/<filename>` or `pytest -k <function_name>`.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mal-toolbox
3
- Version: 0.1.6
3
+ Version: 0.1.8
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
@@ -33,6 +33,8 @@ MAL ([Meta Attack Language](https://mal-lang.org/)) models and attack graphs.
33
33
  Attack graphs can be used to run simulations (see MAL Simulator) or analysis.
34
34
  MAL Toolbox also gives the ability to view the AttackGraph/Model graphically in neo4j.
35
35
 
36
+ [Documentation](https://mal-lang.org/mal-toolbox/index.html)(Work in progress)
37
+
36
38
  ## The Language Module
37
39
 
38
40
  The language module provides various tools to process MAL languages.
@@ -1,5 +1,5 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # MAL Toolbox v0.1.6
2
+ # MAL Toolbox v0.1.8
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.6'
24
+ __version__ = '0.1.8'
25
25
  __authors__ = ['Andrei Buhaiu',
26
26
  'Giuseppe Nebbione',
27
27
  'Nikolaos Kakouros',
@@ -28,10 +28,105 @@ logger = logging.getLogger(__name__)
28
28
 
29
29
  @dataclass
30
30
  class AttackerAttachment:
31
- """Used to attach attackers to attack step entrypoints of assets"""
31
+ """Used to attach attackers to attack step entry points of assets"""
32
32
  id: Optional[int] = None
33
33
  name: Optional[str] = None
34
- entry_points: list[tuple] = field(default_factory=lambda: [])
34
+ entry_points: list[tuple[SchemaGeneratedClass, list[str]]] = \
35
+ field(default_factory=lambda: [])
36
+
37
+
38
+ def get_entry_point_tuple(
39
+ self,
40
+ asset: SchemaGeneratedClass
41
+ ) -> Optional[tuple[SchemaGeneratedClass, list[str]]]:
42
+ """Return an entry point tuple of an AttackerAttachment matching the
43
+ asset provided.
44
+
45
+
46
+ Arguments:
47
+ asset - the asset to add entry point to
48
+
49
+ Return:
50
+ The entry point tuple containing the asset and the list of attack
51
+ steps if the asset has any entry points defined for this attacker
52
+ attachemnt.
53
+ None, otherwise.
54
+ """
55
+ return next((ep_tuple for ep_tuple in self.entry_points
56
+ if ep_tuple[0] == asset), None)
57
+
58
+
59
+ def add_entry_point(
60
+ self, asset: SchemaGeneratedClass, attackstep_name: str):
61
+ """Add an entry point to an AttackerAttachment
62
+
63
+ self.entry_points contain tuples, first element of each tuple
64
+ is an asset, second element is a list of attack step names that
65
+ are entry points for the attacker.
66
+
67
+ Arguments:
68
+ asset - the asset to add the entry point to
69
+ attackstep_name - the name of the attack step to add as an entry point
70
+ """
71
+
72
+ logger.debug(
73
+ f'Add entry point "{attackstep_name}" on asset "{asset.name}" '
74
+ f'to AttackerAttachment "{self.name}".'
75
+ )
76
+
77
+ # Get the entry point tuple for the asset if it already exists
78
+ entry_point_tuple = self.get_entry_point_tuple(asset)
79
+
80
+ if entry_point_tuple:
81
+ if attackstep_name not in entry_point_tuple[1]:
82
+ # If it exists and does not already have the attack step,
83
+ # add it
84
+ entry_point_tuple[1].append(attackstep_name)
85
+ else:
86
+ logger.info(
87
+ f'Entry point "{attackstep_name}" on asset "{asset.name}"'
88
+ f' already existed for AttackerAttachment "{self.name}".'
89
+ )
90
+ else:
91
+ # Otherwise, create the entry point tuple and the initial entry
92
+ # point
93
+ self.entry_points.append((asset, [attackstep_name]))
94
+
95
+ def remove_entry_point(
96
+ self, asset: SchemaGeneratedClass, attackstep_name: str):
97
+ """Remove an entry point from an AttackerAttachment if it exists
98
+
99
+ Arguments:
100
+ asset - the asset to remove the entry point from
101
+ """
102
+
103
+ logger.debug(
104
+ f'Remove entry point "{attackstep_name}" on asset "{asset.name}" '
105
+ f'from AttackerAttachment "{self.name}".'
106
+ )
107
+
108
+ # Get the entry point tuple for the asset if it exists
109
+ entry_point_tuple = self.get_entry_point_tuple(asset)
110
+
111
+ if entry_point_tuple:
112
+ if attackstep_name in entry_point_tuple[1]:
113
+ # If it exists and not already has the attack step, add it
114
+ entry_point_tuple[1].remove(attackstep_name)
115
+ else:
116
+ logger.warning(
117
+ f'Failed to find entry point "{attackstep_name}" on '
118
+ f'asset "{asset.name}" for AttackerAttachment '
119
+ f'"{self.name}". Nothing to remove.'
120
+ )
121
+
122
+ if not entry_point_tuple[1]:
123
+ self.entry_points.remove(entry_point_tuple)
124
+ else:
125
+ logger.warning(
126
+ f'Failed to find entry points on asset "{asset.name}" '
127
+ f'for AttackerAttachment "{self.name}". Nothing to remove.'
128
+ )
129
+
35
130
 
36
131
  class Model():
37
132
  """An implementation of a MAL language with assets and associations"""
@@ -133,6 +228,12 @@ class Model():
133
228
  for association in asset.associations:
134
229
  self.remove_asset_from_association(asset, association)
135
230
 
231
+ # Also remove all of the entry points
232
+ for attacker in self.attackers:
233
+ entry_point_tuple = attacker.get_entry_point_tuple(asset)
234
+ if entry_point_tuple:
235
+ attacker.entry_points.remove(entry_point_tuple)
236
+
136
237
  self.assets.remove(asset)
137
238
 
138
239
  def remove_asset_from_association(
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mal-toolbox"
3
- version = "0.1.6"
3
+ version = "0.1.8"
4
4
  authors = [
5
5
  { name="Andrei Buhaiu", email="buhaiu@kth.se" },
6
6
  { name="Giuseppe Nebbione", email="nebbione@kth.se" },
@@ -51,6 +51,150 @@ def create_association(
51
51
 
52
52
  ### Tests
53
53
 
54
+ def test_attacker_attachment_add_entry_point(model: Model):
55
+ """"""
56
+
57
+ asset1 = create_application_asset(model, "Asset1")
58
+ asset2 = create_application_asset(model, "Asset2")
59
+ model.add_asset(asset1)
60
+ model.add_asset(asset2)
61
+
62
+ # Add attacker 1
63
+ attacker1 = AttackerAttachment()
64
+ model.add_attacker(attacker1)
65
+
66
+ attacker1.add_entry_point(asset1, 'read')
67
+ assert len(attacker1.entry_points) == 1
68
+ assert attacker1.entry_points[0][0] == asset1
69
+ assert attacker1.entry_points[0][1] == ['read']
70
+
71
+ attacker1.add_entry_point(asset1, 'access')
72
+ assert len(attacker1.entry_points) == 1
73
+ assert attacker1.entry_points[0][0] == asset1
74
+ assert attacker1.entry_points[0][1] == ['read', 'access']
75
+
76
+ # Try to add already existing entry point
77
+ attacker1.add_entry_point(asset1, 'access')
78
+ assert len(attacker1.entry_points) == 1
79
+ assert attacker1.entry_points[0][0] == asset1
80
+ assert attacker1.entry_points[0][1] == ['read', 'access']
81
+
82
+ attacker1.add_entry_point(asset2, 'access')
83
+ assert len(attacker1.entry_points) == 2
84
+ assert attacker1.entry_points[1][0] == asset2
85
+ assert attacker1.entry_points[1][1] == ['access']
86
+
87
+
88
+ def test_attacker_attachment_remove_entry_point(model: Model):
89
+ """"""
90
+
91
+ asset1 = create_application_asset(model, "Asset1")
92
+ asset2 = create_application_asset(model, "Asset2")
93
+ model.add_asset(asset1)
94
+ model.add_asset(asset2)
95
+
96
+ # Add attacker 1
97
+ attacker1 = AttackerAttachment()
98
+ model.add_attacker(attacker1)
99
+
100
+ attacker1.add_entry_point(asset1, 'read')
101
+ attacker1.add_entry_point(asset1, 'access')
102
+ attacker1.add_entry_point(asset2, 'access')
103
+
104
+ assert len(attacker1.entry_points) == 2
105
+ assert attacker1.entry_points[0][0] == asset1
106
+ assert attacker1.entry_points[0][1] == ['read', 'access']
107
+ assert attacker1.entry_points[1][0] == asset2
108
+ assert attacker1.entry_points[1][1] == ['access']
109
+
110
+ attacker1.remove_entry_point(asset1, 'read')
111
+ assert len(attacker1.entry_points) == 2
112
+ assert attacker1.entry_points[0][0] == asset1
113
+ assert attacker1.entry_points[0][1] == ['access']
114
+ assert attacker1.entry_points[1][0] == asset2
115
+ assert attacker1.entry_points[1][1] == ['access']
116
+
117
+ # Try to remove inexistent entry point, but the asset is still present in
118
+ # the list of entry points
119
+ attacker1.remove_entry_point(asset1, 'read')
120
+ assert len(attacker1.entry_points) == 2
121
+ assert attacker1.entry_points[0][0] == asset1
122
+ assert attacker1.entry_points[0][1] == ['access']
123
+ assert attacker1.entry_points[1][0] == asset2
124
+ assert attacker1.entry_points[1][1] == ['access']
125
+
126
+ attacker1.remove_entry_point(asset1, 'access')
127
+ assert len(attacker1.entry_points) == 1
128
+ assert attacker1.entry_points[0][0] == asset2
129
+ assert attacker1.entry_points[0][1] == ['access']
130
+
131
+ # Try to remove inexistent entry point, where the asset is no longer in
132
+ # the list of entry points
133
+ attacker1.remove_entry_point(asset1, 'access')
134
+ assert len(attacker1.entry_points) == 1
135
+ assert attacker1.entry_points[0][0] == asset2
136
+ assert attacker1.entry_points[0][1] == ['access']
137
+
138
+ attacker1.remove_entry_point(asset2, 'access')
139
+ assert len(attacker1.entry_points) == 0
140
+
141
+
142
+ def test_attacker_attachment_remove_asset(model: Model):
143
+ """"""
144
+
145
+ asset1 = create_application_asset(model, "Asset1")
146
+ asset2 = create_application_asset(model, "Asset2")
147
+ model.add_asset(asset1)
148
+ model.add_asset(asset2)
149
+
150
+ attacker1 = AttackerAttachment()
151
+ model.add_attacker(attacker1)
152
+ attacker2 = AttackerAttachment()
153
+ model.add_attacker(attacker2)
154
+
155
+ attacker1.add_entry_point(asset1, 'read')
156
+ attacker1.add_entry_point(asset1, 'access')
157
+ attacker1.add_entry_point(asset2, 'access')
158
+
159
+ attacker2.add_entry_point(asset1, 'read')
160
+ attacker2.add_entry_point(asset2, 'read')
161
+ attacker2.add_entry_point(asset2, 'access')
162
+
163
+ assert len(attacker1.entry_points) == 2
164
+ assert attacker1.entry_points[0][0] == asset1
165
+ assert attacker1.entry_points[0][1] == ['read', 'access']
166
+ assert attacker1.entry_points[1][0] == asset2
167
+ assert attacker1.entry_points[1][1] == ['access']
168
+
169
+ assert len(attacker2.entry_points) == 2
170
+ assert attacker2.entry_points[0][0] == asset1
171
+ assert attacker2.entry_points[0][1] == ['read']
172
+ assert attacker2.entry_points[1][0] == asset2
173
+ assert attacker2.entry_points[1][1] == ['read', 'access']
174
+
175
+ model.remove_asset(asset2)
176
+ # All of the entry points of the asset removed should be gone, but the
177
+ # other assets should not be impacted.
178
+ assert len(attacker1.entry_points) == 1
179
+ assert attacker1.entry_points[0][0] == asset1
180
+ assert attacker1.entry_points[0][1] == ['read', 'access']
181
+
182
+ assert len(attacker2.entry_points) == 1
183
+ assert attacker2.entry_points[0][0] == asset1
184
+ assert attacker2.entry_points[0][1] == ['read']
185
+
186
+ # Try to remove inexistent entry point, where the asset is no longer in
187
+ # the list of entry points
188
+ attacker1.remove_entry_point(asset2, 'access')
189
+ assert len(attacker1.entry_points) == 1
190
+ assert attacker1.entry_points[0][0] == asset1
191
+ assert attacker1.entry_points[0][1] == ['read', 'access']
192
+
193
+ assert len(attacker2.entry_points) == 1
194
+ assert attacker2.entry_points[0][0] == asset1
195
+ assert attacker2.entry_points[0][1] == ['read']
196
+
197
+
54
198
  def test_model_add_asset(model: Model):
55
199
  """Make sure assets are added correctly"""
56
200
 
@@ -600,17 +744,17 @@ def test_model_attacker_to_dict(model: Model):
600
744
  attacker_dict = ret[1]
601
745
  assert attacker_dict.get('name') == attacker.name
602
746
 
603
- # entrypoints_dict has asset IDs as keys
604
- entrypoints_dict = attacker_dict.get('entry_points')
747
+ # entry_points_dict has asset IDs as keys
748
+ entry_points_dict = attacker_dict.get('entry_points')
605
749
 
606
750
  # attacker should be attached to p1, therefore p1s
607
- # id should be a key in the entrypoints_dict
608
- assert p1.id is not None and entrypoints_dict
609
- assert p1.id in entrypoints_dict
751
+ # id should be a key in the entry_points_dict
752
+ assert p1.id is not None and entry_points_dict
753
+ assert p1.id in entry_points_dict
610
754
 
611
- # The given steps should be inside the entrypoint of
755
+ # The given steps should be inside the entry_point of
612
756
  # the attacker for asset p1
613
- assert entrypoints_dict[p1.id]['attack_steps'] == attack_steps
757
+ assert entry_points_dict[p1.id]['attack_steps'] == attack_steps
614
758
 
615
759
 
616
760
  def test_serialize(model: Model):
File without changes
File without changes
File without changes