mal-toolbox 0.1.9__py3-none-any.whl → 0.1.10__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mal-toolbox
3
- Version: 0.1.9
3
+ Version: 0.1.10
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
@@ -1,4 +1,4 @@
1
- maltoolbox/__init__.py,sha256=tfDCz1FxF71yzZIZyY0uKDOqCV2nThbOBOUCyW72Qvg,2776
1
+ maltoolbox/__init__.py,sha256=LEN08MI3BjFY7M6PRBbqO1gwXvLbqa2z7xBsIQiZO9Y,2778
2
2
  maltoolbox/__main__.py,sha256=1lOOOme_y56VWrEE1jkarTt-WoUo9yilCo8sUrivyns,2680
3
3
  maltoolbox/default.conf,sha256=YLGBSJh2q8hn3RzRRBbib9F6E6pcvquoHeALMRtA0wU,295
4
4
  maltoolbox/exceptions.py,sha256=0YjPx2v1yYumZ2o7pVZ1s_jS-GAb3Ng979KEFhROSNY,1399
@@ -6,12 +6,12 @@ maltoolbox/file_utils.py,sha256=6KFEEZvf9x8yfNAq7hadF7lUGlLimNFMJ0W_DK2rh6Q,2024
6
6
  maltoolbox/model.py,sha256=1HuH58skm1StcKtvMFO-iXkpLOaauB9YSEv1XNdW_-Y,30406
7
7
  maltoolbox/wrappers.py,sha256=BYYNcIdTlyumADQCPcy1xmPEabfmi0P1l9RcbdVWm9w,2002
8
8
  maltoolbox/attackgraph/__init__.py,sha256=Oqqj5iCwnrzjDoJEFZnVI_kebjJPVbPXK-mWHy0lf-8,209
9
- maltoolbox/attackgraph/attacker.py,sha256=hmJtgcwtQgz0r9J_0Pgm7ktfKpFE3dp-HJ2TOz1NiWw,2880
10
- maltoolbox/attackgraph/attackgraph.py,sha256=Gx88-3M1HacMZRE9YgIGg-6cxCFVZg23bj_pkxDeEH4,29182
11
- maltoolbox/attackgraph/node.py,sha256=ZYnXMIucmTIspQvBD-zyB_wnYO6j27HxQahuQGpYbkE,5640
9
+ maltoolbox/attackgraph/attacker.py,sha256=OaBNDYZF8shbFuQctzuNYVkOrpNb_KhxxV19k0SRa50,3541
10
+ maltoolbox/attackgraph/attackgraph.py,sha256=T9snTC8kzgN017leI29CYj2YdlrU8IDxYiV69yDgz7o,30060
11
+ maltoolbox/attackgraph/node.py,sha256=oFaGCz4QPvDcS7xM5lxaG_-GUR-PKT2xtQ1ryzYRWaU,5869
12
12
  maltoolbox/attackgraph/query.py,sha256=JnoNTUEIlLv2VIk3u5Rq3GpleOn9TZVGBVijniRY_44,6802
13
13
  maltoolbox/attackgraph/analyzers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
- maltoolbox/attackgraph/analyzers/apriori.py,sha256=t_QdfetgNoDXFwlOasrmtBE7XaTaKZvnQCo6X9rs5Xg,6481
14
+ maltoolbox/attackgraph/analyzers/apriori.py,sha256=g0inYyyNYG_wXwz7uJHtf0_6hio92RG9U054edMgJEg,6863
15
15
  maltoolbox/ingestors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
16
  maltoolbox/ingestors/neo4j.py,sha256=jdulYsQ2eZT2r0Af_yYjyGkmVx4l5h8viu1Z70NjVAM,8811
17
17
  maltoolbox/language/__init__.py,sha256=0tvCJDayrLwpqKRny7LBkMOrvcDE6JfJj7U7Jd64Okg,140
@@ -24,9 +24,9 @@ maltoolbox/language/compiler/mal_visitor.py,sha256=9gG06D7GZKlBY-62SmbIkRYkGBUBI
24
24
  maltoolbox/translators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
25
  maltoolbox/translators/securicad.py,sha256=FAIHnoqFTmNYbCGxLsK6pX5g1oiNFfPTqkT_3qq3GG8,6692
26
26
  maltoolbox/translators/updater.py,sha256=Ap08-AsU_7or5ESQvZL2i4nWz3B5pvgfftZyc_-Gd8M,4766
27
- mal_toolbox-0.1.9.dist-info/AUTHORS,sha256=zxLrLe8EY39WtRKlAY4Oorx4Z2_LHV2ApRvDGZgY7xY,127
28
- mal_toolbox-0.1.9.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
29
- mal_toolbox-0.1.9.dist-info/METADATA,sha256=ceylz74EpI9pdyIeJQCzeU_HcqpEB4-riD12UjpLyv8,6003
30
- mal_toolbox-0.1.9.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
31
- mal_toolbox-0.1.9.dist-info/top_level.txt,sha256=phqRVLRKGdSUgRY03mcpi2cmbbDo5YGjkV4gkqHFFcM,11
32
- mal_toolbox-0.1.9.dist-info/RECORD,,
27
+ mal_toolbox-0.1.10.dist-info/AUTHORS,sha256=zxLrLe8EY39WtRKlAY4Oorx4Z2_LHV2ApRvDGZgY7xY,127
28
+ mal_toolbox-0.1.10.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
29
+ mal_toolbox-0.1.10.dist-info/METADATA,sha256=TUH60j4aZ0spLc2BstK_9bOAUst58qXEXiBVIg4dEcA,6004
30
+ mal_toolbox-0.1.10.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
31
+ mal_toolbox-0.1.10.dist-info/top_level.txt,sha256=phqRVLRKGdSUgRY03mcpi2cmbbDo5YGjkV4gkqHFFcM,11
32
+ mal_toolbox-0.1.10.dist-info/RECORD,,
maltoolbox/__init__.py CHANGED
@@ -1,5 +1,5 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # MAL Toolbox v0.1.9
2
+ # MAL Toolbox v0.1.10
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.9'
24
+ __version__ = '0.1.10'
25
25
  __authors__ = ['Andrei Buhaiu',
26
26
  'Giuseppe Nebbione',
27
27
  'Nikolaos Kakouros',
@@ -51,6 +51,15 @@ def propagate_necessity_from_node(node: AttackGraphNode) -> None:
51
51
  'Propagate necessity from "%s"(%d) with necessity status %s.',
52
52
  node.full_name, node.id, node.is_necessary
53
53
  )
54
+
55
+ if node.ttc and 'name' in node.ttc:
56
+ if node.ttc['name'] not in ['Enabled', 'Disabled']:
57
+ # Do not propagate unnecessary state from nodes that have a TTC
58
+ # probability distribution associated with them.
59
+ # TODO: Evaluate this more carefully, how do we want to have TTCs
60
+ # impact necessity and viability.
61
+ return
62
+
54
63
  for child in node.children:
55
64
  original_value = child.is_necessary
56
65
  if child.type == 'or':
@@ -4,6 +4,7 @@ MAL-Toolbox Attack Graph Attacker Class
4
4
 
5
5
  from __future__ import annotations
6
6
  from dataclasses import dataclass, field
7
+ import copy
7
8
  import logging
8
9
 
9
10
  from typing import Optional
@@ -41,6 +42,28 @@ class Attacker:
41
42
  def __repr__(self) -> str:
42
43
  return str(self.to_dict())
43
44
 
45
+ def __deepcopy__(self, memo) -> Attacker:
46
+ """Deep copy an Attacker"""
47
+
48
+ # Check if the object is already in the memo dictionary
49
+ if id(self) in memo:
50
+ return memo[id(self)]
51
+
52
+ copied_attacker = Attacker(
53
+ id = self.id,
54
+ name = self.name,
55
+ )
56
+
57
+ # Remember that self was already copied
58
+ memo[id(self)] = copied_attacker
59
+
60
+ copied_attacker.entry_points = copy.deepcopy(
61
+ self.entry_points, memo = memo)
62
+ copied_attacker.reached_attack_steps = copy.deepcopy(
63
+ self.reached_attack_steps, memo = memo)
64
+
65
+ return copied_attacker
66
+
44
67
  def compromise(self, node: AttackGraphNode) -> None:
45
68
  """
46
69
  Have the attacke compromise the node given as a parameter.
@@ -226,15 +226,37 @@ class AttackGraph():
226
226
  }
227
227
 
228
228
  def __deepcopy__(self, memo):
229
+
230
+ # Check if the object is already in the memo dictionary
231
+ if id(self) in memo:
232
+ return memo[id(self)]
233
+
229
234
  copied_attackgraph = AttackGraph(self.lang_graph)
230
235
  copied_attackgraph.model = self.model
231
236
 
232
- # Deep copy nodes and add references to them
233
- copied_attackgraph.nodes = copy.deepcopy(self.nodes, memo)
237
+ copied_attackgraph.nodes = []
238
+
239
+ # Deep copy nodes
240
+ for node in self.nodes:
241
+ copied_node = copy.deepcopy(node, memo)
242
+ copied_attackgraph.nodes.append(copied_node)
243
+
244
+ # Re-link node references
245
+ for node in self.nodes:
246
+ if node.parents:
247
+ memo[id(node)].parents = copy.deepcopy(node.parents, memo)
248
+ if node.children:
249
+ memo[id(node)].children = copy.deepcopy(node.children, memo)
234
250
 
235
251
  # Deep copy attackers and references to them
236
252
  copied_attackgraph.attackers = copy.deepcopy(self.attackers, memo)
237
253
 
254
+ # Re-link attacker references
255
+ for node in self.nodes:
256
+ if node.compromised_by:
257
+ memo[id(node)].compromised_by = copy.deepcopy(
258
+ node.compromised_by, memo)
259
+
238
260
  # Copy lookup dicts
239
261
  copied_attackgraph._id_to_attacker = \
240
262
  copy.deepcopy(self._id_to_attacker, memo)
@@ -354,7 +376,10 @@ class AttackGraph():
354
376
  attacker = ag_attacker,
355
377
  attacker_id = int(attacker['id']),
356
378
  entry_points = attacker['entry_points'].keys(),
357
- reached_attack_steps = attacker['reached_attack_steps'].keys()
379
+ reached_attack_steps = [
380
+ int(node_id) # Convert to int since they can be strings
381
+ for node_id in attacker['reached_attack_steps'].keys()
382
+ ]
358
383
  )
359
384
 
360
385
  return attack_graph
@@ -466,7 +491,7 @@ class AttackGraph():
466
491
  continue
467
492
  attacker.compromise(ag_node)
468
493
 
469
- attacker.entry_points = attacker.reached_attack_steps
494
+ attacker.entry_points = list(attacker.reached_attack_steps)
470
495
 
471
496
  def _generate_graph(self) -> None:
472
497
  """
@@ -612,7 +637,7 @@ class AttackGraph():
612
637
  if logger.isEnabledFor(logging.DEBUG):
613
638
  # Avoid running json.dumps when not in debug
614
639
  logger.debug(f'Add node \"{node.full_name}\" '
615
- 'with id:{node_id}:\n' \
640
+ f'with id:{node_id}:\n' \
616
641
  + json.dumps(node.to_dict(), indent = 2))
617
642
 
618
643
  if node.id in self._id_to_node:
@@ -669,7 +694,8 @@ class AttackGraph():
669
694
  attacker.name,
670
695
  attacker_id)
671
696
  else:
672
- logger.debug('Add attacker "%s" without id.')
697
+ logger.debug('Add attacker "%s" without id.',
698
+ attacker.name)
673
699
 
674
700
 
675
701
  attacker.id = attacker_id or self.next_attacker_id
@@ -678,7 +704,7 @@ class AttackGraph():
678
704
 
679
705
  self.next_attacker_id = max(attacker.id + 1, self.next_attacker_id)
680
706
  for node_id in reached_attack_steps:
681
- node = self.get_node_by_id(int(node_id))
707
+ node = self.get_node_by_id(node_id)
682
708
  if node:
683
709
  attacker.compromise(node)
684
710
  else:
@@ -71,7 +71,18 @@ class AttackGraphNode:
71
71
  return str(self.to_dict())
72
72
 
73
73
  def __deepcopy__(self, memo) -> AttackGraphNode:
74
- """Deep copy an attackgraph node"""
74
+ """Deep copy an attackgraph node
75
+
76
+ The deepcopy will copy over node specific information, such as type,
77
+ name, etc., but it will not copy attack graph relations such as
78
+ parents, children, or attackers it is compromised by. These references
79
+ should be recreated when deepcopying the attack graph itself.
80
+
81
+ """
82
+
83
+ # Check if the object is already in the memo dictionary
84
+ if id(self) in memo:
85
+ return memo[id(self)]
75
86
 
76
87
  copied_node = AttackGraphNode(
77
88
  self.type,
@@ -85,22 +96,20 @@ class AttackGraphNode:
85
96
  self.existence_status,
86
97
  self.is_viable,
87
98
  self.is_necessary,
88
- copy.deepcopy(self.compromised_by, memo),
99
+ [],
89
100
  self.mitre_info,
90
- copy.deepcopy(self.tags, memo),
91
- copy.deepcopy(self.attributes, memo),
92
- copy.deepcopy(self.extras, memo)
101
+ [],
102
+ {},
103
+ {}
93
104
  )
94
105
 
106
+ copied_node.tags = copy.deepcopy(self.tags, memo)
107
+ copied_node.attributes = copy.deepcopy(self.attributes, memo)
108
+ copied_node.extras = copy.deepcopy(self.extras, memo)
109
+
95
110
  # Remember that self was already copied
96
111
  memo[id(self)] = copied_node
97
112
 
98
- # Deep copy children and parents, send memo (avoid infinite recursion)
99
- if self.parents:
100
- copied_node.parents = copy.deepcopy(self.parents, memo)
101
- if self.children:
102
- copied_node.children = copy.deepcopy(self.children, memo)
103
-
104
113
  return copied_node
105
114
 
106
115
  def is_compromised(self) -> bool: