mal-toolbox 1.1.1__py3-none-any.whl → 1.1.3__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.
Files changed (31) hide show
  1. {mal_toolbox-1.1.1.dist-info → mal_toolbox-1.1.3.dist-info}/METADATA +25 -2
  2. mal_toolbox-1.1.3.dist-info/RECORD +32 -0
  3. maltoolbox/__init__.py +6 -7
  4. maltoolbox/__main__.py +17 -9
  5. maltoolbox/attackgraph/__init__.py +2 -3
  6. maltoolbox/attackgraph/attackgraph.py +379 -362
  7. maltoolbox/attackgraph/node.py +14 -19
  8. maltoolbox/exceptions.py +7 -10
  9. maltoolbox/file_utils.py +10 -4
  10. maltoolbox/language/__init__.py +1 -1
  11. maltoolbox/language/compiler/__init__.py +4 -4
  12. maltoolbox/language/compiler/mal_lexer.py +154 -154
  13. maltoolbox/language/compiler/mal_parser.py +784 -1136
  14. maltoolbox/language/languagegraph.py +487 -639
  15. maltoolbox/model.py +64 -77
  16. maltoolbox/patternfinder/attackgraph_patterns.py +17 -8
  17. maltoolbox/translators/__init__.py +8 -0
  18. maltoolbox/translators/networkx.py +42 -0
  19. maltoolbox/translators/updater.py +18 -25
  20. maltoolbox/visualization/__init__.py +4 -4
  21. maltoolbox/visualization/draw_io_utils.py +6 -5
  22. maltoolbox/visualization/graphviz_utils.py +4 -2
  23. maltoolbox/visualization/neo4j_utils.py +13 -14
  24. maltoolbox/visualization/utils.py +2 -3
  25. mal_toolbox-1.1.1.dist-info/RECORD +0 -32
  26. maltoolbox/translators/securicad.py +0 -179
  27. {mal_toolbox-1.1.1.dist-info → mal_toolbox-1.1.3.dist-info}/WHEEL +0 -0
  28. {mal_toolbox-1.1.1.dist-info → mal_toolbox-1.1.3.dist-info}/entry_points.txt +0 -0
  29. {mal_toolbox-1.1.1.dist-info → mal_toolbox-1.1.3.dist-info}/licenses/AUTHORS +0 -0
  30. {mal_toolbox-1.1.1.dist-info → mal_toolbox-1.1.3.dist-info}/licenses/LICENSE +0 -0
  31. {mal_toolbox-1.1.1.dist-info → mal_toolbox-1.1.3.dist-info}/top_level.txt +0 -0
@@ -1,17 +1,18 @@
1
- """
2
- MAL-Toolbox Attack Graph Node
1
+ """MAL-Toolbox Attack Graph Node
3
2
  """
4
3
 
5
4
  from __future__ import annotations
5
+
6
6
  import copy
7
7
  from functools import cached_property
8
8
  from typing import TYPE_CHECKING
9
9
 
10
10
  if TYPE_CHECKING:
11
- from typing import Any, Optional
12
- from ..language import LanguageGraphAttackStep, Detector
11
+
12
+ from ..language import LanguageGraphAttackStep
13
13
  from ..model import ModelAsset
14
14
 
15
+
15
16
  class AttackGraphNode:
16
17
  """Node part of AttackGraph"""
17
18
 
@@ -19,10 +20,10 @@ class AttackGraphNode:
19
20
  self,
20
21
  node_id: int,
21
22
  lg_attack_step: LanguageGraphAttackStep,
22
- model_asset: Optional[ModelAsset] = None,
23
- ttc_dist: Optional[dict] = None,
24
- existence_status: Optional[bool] = None,
25
- full_name: Optional[str] = None
23
+ model_asset: ModelAsset | None = None,
24
+ ttc_dist: dict | None = None,
25
+ existence_status: bool | None = None,
26
+ full_name: str | None = None
26
27
  ):
27
28
  self.lg_attack_step = lg_attack_step
28
29
  self.name = lg_attack_step.name
@@ -68,12 +69,10 @@ class AttackGraphNode:
68
69
 
69
70
  return node_dict
70
71
 
71
-
72
72
  def __repr__(self) -> str:
73
73
  return (f'AttackGraphNode(name: "{self.full_name}", id: {self.id}, '
74
74
  f'type: {self.type})')
75
75
 
76
-
77
76
  def __deepcopy__(self, memo) -> AttackGraphNode:
78
77
  """Deep copy an attackgraph node
79
78
 
@@ -83,15 +82,14 @@ class AttackGraphNode:
83
82
  should be recreated when deepcopying the attack graph itself.
84
83
 
85
84
  """
86
-
87
85
  # Check if the object is already in the memo dictionary
88
86
  if id(self) in memo:
89
87
  return memo[id(self)]
90
88
 
91
89
  copied_node = AttackGraphNode(
92
- node_id = self.id,
93
- model_asset = self.model_asset,
94
- lg_attack_step = self.lg_attack_step
90
+ node_id=self.id,
91
+ model_asset=self.model_asset,
92
+ lg_attack_step=self.lg_attack_step
95
93
  )
96
94
 
97
95
  copied_node.tags = copy.deepcopy(self.tags, memo)
@@ -105,11 +103,9 @@ class AttackGraphNode:
105
103
 
106
104
  return copied_node
107
105
 
108
-
109
106
  @property
110
107
  def full_name(self) -> str:
111
- """
112
- Return the full name of the attack step. This is normally a
108
+ """Return the full name of the attack step. This is normally a
113
109
  combination of the asset name to which the attack step
114
110
  belongs and attack step name itself, but can also be
115
111
  explicitly set or a combination of the step id and step name.
@@ -117,7 +113,7 @@ class AttackGraphNode:
117
113
  if self._full_name:
118
114
  # Explicitly set
119
115
  return self._full_name
120
- elif self.model_asset:
116
+ if self.model_asset:
121
117
  # Inherited from model asset
122
118
  full_name = self.model_asset.name + ':' + self.name
123
119
  else:
@@ -125,7 +121,6 @@ class AttackGraphNode:
125
121
  full_name = str(self.id) + ':' + self.name
126
122
  return full_name
127
123
 
128
-
129
124
  @cached_property
130
125
  def info(self) -> dict[str, str]:
131
126
  return self.lg_attack_step.info
maltoolbox/exceptions.py CHANGED
@@ -1,14 +1,14 @@
1
1
  class MalToolboxException(Exception):
2
2
  """Base exception for all other maltoolbox exceptions to inherit from."""
3
- pass
3
+
4
4
 
5
5
  class LanguageGraphException(MalToolboxException):
6
6
  """Base exception for all language-graph related exceptions."""
7
- pass
7
+
8
8
 
9
9
  class LanguageGraphSuperAssetNotFoundError(LanguageGraphException):
10
10
  """Asset's super asset not found in language graph during attack graph construction."""
11
- pass
11
+
12
12
 
13
13
  class LanguageGraphAssociationError(LanguageGraphException):
14
14
  """Error in building an association.
@@ -16,30 +16,27 @@ class LanguageGraphAssociationError(LanguageGraphException):
16
16
  For example, right or left-hand side asset of association missing in
17
17
  language graph.
18
18
  """
19
- pass
19
+
20
20
 
21
21
  class LanguageGraphStepExpressionError(LanguageGraphException):
22
22
  """A target asset cannot be linked with for a step expression."""
23
- pass
23
+
24
24
 
25
25
  class AttackGraphException(MalToolboxException):
26
26
  """Base exception for all attack-graph related exceptions."""
27
- pass
27
+
28
28
 
29
29
  class AttackGraphStepExpressionError(AttackGraphException):
30
30
  """A target attack step cannot be linked with for a step expression."""
31
- pass
32
31
 
33
32
 
34
33
  class ModelException(MalToolboxException):
35
34
  """Base Exception for all Model related exceptions"""
36
- pass
37
35
 
38
36
 
39
37
  class ModelAssociationException(ModelException):
40
38
  """Exception related to associations in Model"""
41
- pass
39
+
42
40
 
43
41
  class DuplicateModelAssociationError(ModelException):
44
42
  """Associations should be unique as part of Model"""
45
- pass
maltoolbox/file_utils.py CHANGED
@@ -1,16 +1,19 @@
1
1
  """Utilty functions for file handling"""
2
2
 
3
3
  import json
4
+
4
5
  import yaml
5
6
 
7
+
6
8
  def save_dict_to_json_file(filename: str, serialized_object: dict) -> None:
7
9
  """Save serialized object to a json file.
8
10
 
9
11
  Arguments:
12
+ ---------
10
13
  filename - the name of the output file
11
14
  data - dict to output as json
12
- """
13
15
 
16
+ """
14
17
  with open(filename, 'w', encoding='utf-8') as f:
15
18
  json.dump(serialized_object, f, indent=4)
16
19
 
@@ -19,8 +22,10 @@ def save_dict_to_yaml_file(filename: str, serialized_object: dict) -> None:
19
22
  """Save serialized object to a yaml file.
20
23
 
21
24
  Arguments:
25
+ ---------
22
26
  filename - the name of the output file
23
27
  data - dict to output as yaml
28
+
24
29
  """
25
30
 
26
31
  class NoAliasSafeDumper(yaml.SafeDumper):
@@ -33,14 +38,14 @@ def save_dict_to_yaml_file(filename: str, serialized_object: dict) -> None:
33
38
 
34
39
  def load_dict_from_yaml_file(filename: str) -> dict:
35
40
  """Open json file and read as dict"""
36
- with open(filename, 'r', encoding='utf-8') as file:
41
+ with open(filename, encoding='utf-8') as file:
37
42
  object_dict = yaml.safe_load(file)
38
43
  return object_dict
39
44
 
40
45
 
41
46
  def load_dict_from_json_file(filename: str) -> dict:
42
47
  """Open yaml file and read as dict"""
43
- with open(filename, 'r', encoding='utf-8') as file:
48
+ with open(filename, encoding='utf-8') as file:
44
49
  object_dict = json.loads(file.read())
45
50
  return object_dict
46
51
 
@@ -50,10 +55,11 @@ def save_dict_to_file(filename: str, dictionary: dict) -> None:
50
55
  depending on file extension.
51
56
 
52
57
  Arguments:
58
+ ---------
53
59
  filename - the name of the output file
54
60
  dictionary - the dict to save to the file
55
- """
56
61
 
62
+ """
57
63
  if filename.endswith(('.yml', '.yaml')):
58
64
  save_dict_to_yaml_file(filename, dictionary)
59
65
  elif filename.endswith('.json'):
@@ -20,4 +20,4 @@ __all__ = [
20
20
  "LanguageGraphAssociation",
21
21
  "LanguageGraphAttackStep",
22
22
  "disaggregate_attack_step_full_name"
23
- ]
23
+ ]
@@ -3,7 +3,7 @@ import sys
3
3
  from collections.abc import MutableMapping, MutableSequence
4
4
  from pathlib import Path
5
5
 
6
- from antlr4 import FileStream, CommonTokenStream, ParseTreeVisitor
6
+ from antlr4 import CommonTokenStream, FileStream, ParseTreeVisitor
7
7
  from antlr4.error.ErrorListener import ConsoleErrorListener
8
8
 
9
9
  from .mal_lexer import malLexer
@@ -16,7 +16,7 @@ from .mal_parser import malParser
16
16
 
17
17
  def patched_antrl_syntax_error(self, recognizer, offendingSymbol, line, column, msg, e):
18
18
  file = patched_antrl_syntax_error.file
19
- print(f"{file}:{str(line)}:{str(column)}: {msg}", file=sys.stderr)
19
+ print(f"{file}:{line!s}:{column!s}: {msg}", file=sys.stderr)
20
20
 
21
21
 
22
22
  ConsoleErrorListener.syntaxError = patched_antrl_syntax_error
@@ -88,9 +88,9 @@ class MalCompiler(ParseTreeVisitor):
88
88
  included_file = self.compile(value)
89
89
  for k, v in langspec.items():
90
90
  if isinstance(v, MutableMapping):
91
- langspec[k].update(included_file.get(k, {}))
91
+ v.update(included_file.get(k, {}))
92
92
  if isinstance(v, MutableSequence) and k in included_file:
93
- langspec[k].extend(included_file[k])
93
+ v.extend(included_file[k])
94
94
 
95
95
  for key in ("categories", "assets", "associations"):
96
96
  unique = []