mal-toolbox 0.3.6__tar.gz → 0.3.7__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 (35) hide show
  1. {mal_toolbox-0.3.6/mal_toolbox.egg-info → mal_toolbox-0.3.7}/PKG-INFO +1 -1
  2. {mal_toolbox-0.3.6 → mal_toolbox-0.3.7/mal_toolbox.egg-info}/PKG-INFO +1 -1
  3. {mal_toolbox-0.3.6 → mal_toolbox-0.3.7}/maltoolbox/__init__.py +2 -2
  4. {mal_toolbox-0.3.6 → mal_toolbox-0.3.7}/maltoolbox/attackgraph/analyzers/apriori.py +5 -5
  5. {mal_toolbox-0.3.6 → mal_toolbox-0.3.7}/maltoolbox/attackgraph/query.py +31 -51
  6. {mal_toolbox-0.3.6 → mal_toolbox-0.3.7}/pyproject.toml +1 -1
  7. {mal_toolbox-0.3.6 → mal_toolbox-0.3.7}/AUTHORS +0 -0
  8. {mal_toolbox-0.3.6 → mal_toolbox-0.3.7}/LICENSE +0 -0
  9. {mal_toolbox-0.3.6 → mal_toolbox-0.3.7}/README.md +0 -0
  10. {mal_toolbox-0.3.6 → mal_toolbox-0.3.7}/mal_toolbox.egg-info/SOURCES.txt +0 -0
  11. {mal_toolbox-0.3.6 → mal_toolbox-0.3.7}/mal_toolbox.egg-info/dependency_links.txt +0 -0
  12. {mal_toolbox-0.3.6 → mal_toolbox-0.3.7}/mal_toolbox.egg-info/entry_points.txt +0 -0
  13. {mal_toolbox-0.3.6 → mal_toolbox-0.3.7}/mal_toolbox.egg-info/requires.txt +0 -0
  14. {mal_toolbox-0.3.6 → mal_toolbox-0.3.7}/mal_toolbox.egg-info/top_level.txt +0 -0
  15. {mal_toolbox-0.3.6 → mal_toolbox-0.3.7}/maltoolbox/__main__.py +0 -0
  16. {mal_toolbox-0.3.6 → mal_toolbox-0.3.7}/maltoolbox/attackgraph/__init__.py +0 -0
  17. {mal_toolbox-0.3.6 → mal_toolbox-0.3.7}/maltoolbox/attackgraph/analyzers/__init__.py +0 -0
  18. {mal_toolbox-0.3.6 → mal_toolbox-0.3.7}/maltoolbox/attackgraph/attacker.py +0 -0
  19. {mal_toolbox-0.3.6 → mal_toolbox-0.3.7}/maltoolbox/attackgraph/attackgraph.py +0 -0
  20. {mal_toolbox-0.3.6 → mal_toolbox-0.3.7}/maltoolbox/attackgraph/node.py +0 -0
  21. {mal_toolbox-0.3.6 → mal_toolbox-0.3.7}/maltoolbox/exceptions.py +0 -0
  22. {mal_toolbox-0.3.6 → mal_toolbox-0.3.7}/maltoolbox/file_utils.py +0 -0
  23. {mal_toolbox-0.3.6 → mal_toolbox-0.3.7}/maltoolbox/ingestors/__init__.py +0 -0
  24. {mal_toolbox-0.3.6 → mal_toolbox-0.3.7}/maltoolbox/ingestors/neo4j.py +0 -0
  25. {mal_toolbox-0.3.6 → mal_toolbox-0.3.7}/maltoolbox/language/__init__.py +0 -0
  26. {mal_toolbox-0.3.6 → mal_toolbox-0.3.7}/maltoolbox/language/compiler/__init__.py +0 -0
  27. {mal_toolbox-0.3.6 → mal_toolbox-0.3.7}/maltoolbox/language/compiler/mal_lexer.py +0 -0
  28. {mal_toolbox-0.3.6 → mal_toolbox-0.3.7}/maltoolbox/language/compiler/mal_parser.py +0 -0
  29. {mal_toolbox-0.3.6 → mal_toolbox-0.3.7}/maltoolbox/language/languagegraph.py +0 -0
  30. {mal_toolbox-0.3.6 → mal_toolbox-0.3.7}/maltoolbox/model.py +0 -0
  31. {mal_toolbox-0.3.6 → mal_toolbox-0.3.7}/maltoolbox/translators/__init__.py +0 -0
  32. {mal_toolbox-0.3.6 → mal_toolbox-0.3.7}/maltoolbox/translators/securicad.py +0 -0
  33. {mal_toolbox-0.3.6 → mal_toolbox-0.3.7}/maltoolbox/translators/updater.py +0 -0
  34. {mal_toolbox-0.3.6 → mal_toolbox-0.3.7}/setup.cfg +0 -0
  35. {mal_toolbox-0.3.6 → mal_toolbox-0.3.7}/tests/test_model.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: mal-toolbox
3
- Version: 0.3.6
3
+ Version: 0.3.7
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: mal-toolbox
3
- Version: 0.3.6
3
+ Version: 0.3.7
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
@@ -1,5 +1,5 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # MAL Toolbox v0.3.6
2
+ # MAL Toolbox v0.3.7
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__ = "0.3.6"
24
+ __version__ = "0.3.7"
25
25
  __authors__ = [
26
26
  "Andrei Buhaiu",
27
27
  "Giuseppe Nebbione",
@@ -195,7 +195,7 @@ def prune_unviable_and_unnecessary_nodes(graph: AttackGraph) -> None:
195
195
 
196
196
  def propagate_viability_from_unviable_node(
197
197
  unviable_node: AttackGraphNode,
198
- ) -> list[AttackGraphNode]:
198
+ ) -> set[AttackGraphNode]:
199
199
  """
200
200
  Update viability of nodes affected by newly enabled defense
201
201
  `unviable_node` in the graph and return any attack steps
@@ -207,12 +207,12 @@ def propagate_viability_from_unviable_node(
207
207
  unviable_node - the node to propagate viability from
208
208
 
209
209
  Returns:
210
- attack_steps_made_unviable - list of the attack steps that have been
210
+ attack_steps_made_unviable - set of the attack steps that have been
211
211
  made unviable by a defense enabled in the
212
212
  current step. Builds up recursively.
213
213
  """
214
214
 
215
- attack_steps_made_unviable = []
215
+ attack_steps_made_unviable = set()
216
216
 
217
217
  logger.debug(
218
218
  'Update viability for node "%s"(%d)',
@@ -226,7 +226,7 @@ def propagate_viability_from_unviable_node(
226
226
  )
227
227
 
228
228
  if unviable_node.type in ('and', 'or'):
229
- attack_steps_made_unviable.append(unviable_node)
229
+ attack_steps_made_unviable.add(unviable_node)
230
230
 
231
231
  for child in unviable_node.children:
232
232
  original_value = child.is_viable
@@ -238,7 +238,7 @@ def propagate_viability_from_unviable_node(
238
238
  child.is_viable = False
239
239
 
240
240
  if child.is_viable != original_value:
241
- attack_steps_made_unviable += \
241
+ attack_steps_made_unviable |= \
242
242
  propagate_viability_from_unviable_node(child)
243
243
 
244
244
  return attack_steps_made_unviable
@@ -5,8 +5,9 @@ This submodule contains functions that analyze the information present in the
5
5
  attack graph, but do not alter the structure or nodes in any way.
6
6
  """
7
7
  from __future__ import annotations
8
+ from collections.abc import Iterable
8
9
  import logging
9
- from typing import TYPE_CHECKING
10
+ from typing import TYPE_CHECKING, Optional
10
11
 
11
12
  from .attackgraph import AttackGraph, Attacker
12
13
 
@@ -113,25 +114,37 @@ def is_node_traversable_by_attacker(
113
114
  )
114
115
  return False
115
116
 
116
- def get_attack_surface(
117
- attacker: Attacker
118
- ) -> list[AttackGraphNode]:
117
+
118
+ def calculate_attack_surface(
119
+ attacker: Attacker,
120
+ *,
121
+ from_nodes: Optional[Iterable[AttackGraphNode]] = None,
122
+ skip_compromised: bool = False,
123
+ ) -> set[AttackGraphNode]:
119
124
  """
120
- Get the current attack surface of an attacker. This includes all of the
121
- viable children nodes of already reached attack steps that are of 'or'
122
- type and the 'and' type children nodes which have all of their necessary
123
- parents in the attack steps reached.
125
+ Calculate the attack surface of the attacker. If from_nodes are provided
126
+ only calculate the attack surface stemming from those nodes, otherwise use
127
+ all nodes the attacker has compromised. If skip_compromised is true,
128
+ exclude already compromised nodes from the returned attack surface.
129
+
130
+ The attack surface includes all of the viable children nodes that are of
131
+ 'or' type and the 'and' type children nodes which have all of their
132
+ necessary parents compromised by the attacker.
124
133
 
125
134
  Arguments:
126
- attacker - the Attacker whose attack surface is sought
135
+ attacker - the Attacker whose attack surface is sought
136
+ from_nodes - the nodes to calculate the attack surface from; defaults
137
+ to the attackers compromised nodes list if omitted
138
+ skip_compromised - if true do not add already compromised nodes to the
139
+ attack surface
127
140
  """
128
141
  logger.debug(
129
142
  'Get the attack surface for Attacker "%s"(%d).',
130
143
  attacker.name,
131
144
  attacker.id
132
145
  )
133
- attack_surface = []
134
- for attack_step in attacker.reached_attack_steps:
146
+ attack_surface = set()
147
+ for attack_step in from_nodes or attacker.reached_attack_steps:
135
148
  logger.debug(
136
149
  'Determine attack surface stemming from '
137
150
  '"%s"(%d) for Attacker "%s"(%d).',
@@ -141,44 +154,10 @@ def get_attack_surface(
141
154
  attacker.id
142
155
  )
143
156
  for child in attack_step.children:
157
+ if skip_compromised and child.is_compromised_by(attacker):
158
+ continue
144
159
  if is_node_traversable_by_attacker(child, attacker) and \
145
160
  child not in attack_surface:
146
- attack_surface.append(child)
147
- return attack_surface
148
-
149
- def update_attack_surface_add_nodes(
150
- attacker: Attacker,
151
- current_attack_surface: list[AttackGraphNode],
152
- nodes: list[AttackGraphNode]
153
- ) -> list[AttackGraphNode]:
154
- """
155
- Update the attack surface of an attacker with the new attack step nodes
156
- provided to see if any of their children can be added.
157
-
158
- Arguments:
159
- attacker - the Attacker whose attack surface is sought
160
- current_attack_surface - the current attack surface that we wish to
161
- expand
162
- nodes - the newly compromised attack step nodes that we
163
- wish to see if any of their children should be
164
- added to the attack surface
165
- """
166
- logger.debug('Update the attack surface for Attacker "%s"(%d).',
167
- attacker.name,
168
- attacker.id)
169
- attack_surface = current_attack_surface
170
- for attack_step in nodes:
171
- logger.debug(
172
- 'Determine attack surface stemming from "%s"(%d) '
173
- 'for Attacker "%s"(%d).',
174
- attack_step.full_name,
175
- attack_step.id,
176
- attacker.name,
177
- attacker.id
178
- )
179
- for child in attack_step.children:
180
- is_traversable = is_node_traversable_by_attacker(child, attacker)
181
- if is_traversable and child not in attack_surface:
182
161
  logger.debug(
183
162
  'Add node "%s"(%d) to the attack surface of '
184
163
  'Attacker "%s"(%d).',
@@ -187,10 +166,11 @@ def update_attack_surface_add_nodes(
187
166
  attacker.name,
188
167
  attacker.id
189
168
  )
190
- attack_surface.append(child)
169
+ attack_surface.add(child)
170
+
191
171
  return attack_surface
192
172
 
193
- def get_defense_surface(graph: AttackGraph) -> list[AttackGraphNode]:
173
+ def get_defense_surface(graph: AttackGraph) -> set[AttackGraphNode]:
194
174
  """
195
175
  Get the defense surface. All non-suppressed defense steps that are not
196
176
  already fully enabled.
@@ -199,8 +179,8 @@ def get_defense_surface(graph: AttackGraph) -> list[AttackGraphNode]:
199
179
  graph - the attack graph
200
180
  """
201
181
  logger.debug('Get the defense surface.')
202
- return [node for node in graph.nodes.values()
203
- if node.is_available_defense()]
182
+ return {node for node in graph.nodes.values()
183
+ if node.is_available_defense()}
204
184
 
205
185
  def get_enabled_defenses(graph: AttackGraph) -> list[AttackGraphNode]:
206
186
  """
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mal-toolbox"
3
- version = "0.3.6"
3
+ version = "0.3.7"
4
4
  authors = [
5
5
  { name="Andrei Buhaiu", email="buhaiu@kth.se" },
6
6
  { name="Joakim Loxdal", email="loxdal@kth.se" },
File without changes
File without changes
File without changes
File without changes