mal-toolbox 0.0.27__py3-none-any.whl → 0.1.12__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 (37) hide show
  1. {mal_toolbox-0.0.27.dist-info → mal_toolbox-0.1.12.dist-info}/METADATA +60 -28
  2. mal_toolbox-0.1.12.dist-info/RECORD +32 -0
  3. {mal_toolbox-0.0.27.dist-info → mal_toolbox-0.1.12.dist-info}/WHEEL +1 -1
  4. maltoolbox/__init__.py +31 -31
  5. maltoolbox/__main__.py +80 -4
  6. maltoolbox/attackgraph/__init__.py +8 -0
  7. maltoolbox/attackgraph/analyzers/__init__.py +0 -0
  8. maltoolbox/attackgraph/analyzers/apriori.py +173 -27
  9. maltoolbox/attackgraph/attacker.py +99 -21
  10. maltoolbox/attackgraph/attackgraph.py +507 -217
  11. maltoolbox/attackgraph/node.py +143 -21
  12. maltoolbox/attackgraph/query.py +128 -26
  13. maltoolbox/default.conf +8 -7
  14. maltoolbox/exceptions.py +45 -0
  15. maltoolbox/file_utils.py +66 -0
  16. maltoolbox/ingestors/__init__.py +0 -0
  17. maltoolbox/ingestors/neo4j.py +95 -84
  18. maltoolbox/language/__init__.py +4 -0
  19. maltoolbox/language/classes_factory.py +145 -64
  20. maltoolbox/language/{lexer_parser/__main__.py → compiler/__init__.py} +5 -12
  21. maltoolbox/language/{lexer_parser → compiler}/mal_lexer.py +1 -1
  22. maltoolbox/language/{lexer_parser → compiler}/mal_parser.py +1 -1
  23. maltoolbox/language/{lexer_parser → compiler}/mal_visitor.py +4 -5
  24. maltoolbox/language/languagegraph.py +569 -168
  25. maltoolbox/model.py +858 -0
  26. maltoolbox/translators/__init__.py +0 -0
  27. maltoolbox/translators/securicad.py +76 -52
  28. maltoolbox/translators/updater.py +132 -0
  29. maltoolbox/wrappers.py +62 -0
  30. mal_toolbox-0.0.27.dist-info/RECORD +0 -26
  31. maltoolbox/cl_parser.py +0 -89
  32. maltoolbox/language/specification.py +0 -265
  33. maltoolbox/main.py +0 -84
  34. maltoolbox/model/model.py +0 -279
  35. {mal_toolbox-0.0.27.dist-info → mal_toolbox-0.1.12.dist-info}/AUTHORS +0 -0
  36. {mal_toolbox-0.0.27.dist-info → mal_toolbox-0.1.12.dist-info}/LICENSE +0 -0
  37. {mal_toolbox-0.0.27.dist-info → mal_toolbox-0.1.12.dist-info}/top_level.txt +0 -0
@@ -1,265 +0,0 @@
1
- """
2
- MAL-Toolbox Language Specification Module
3
-
4
- """
5
-
6
- import logging
7
- import json
8
- import zipfile
9
- import copy
10
-
11
- logger = logging.getLogger(__name__)
12
-
13
- def load_language_specification_from_mar(mar_archive: str) -> dict:
14
- """
15
- Read a ".mar" archive provided by malc (https://github.com/mal-lang/malc)
16
- and return a dictionary representing a MAL language structure
17
-
18
- Arguments:
19
- mar_archive - the path to a ".mar" archive
20
-
21
- Return:
22
- A dictionary representing the language specification
23
- """
24
-
25
- logger.info(f'Load language specfication from \'{mar_archive}\' mar archive.')
26
- with zipfile.ZipFile(mar_archive, 'r') as archive:
27
- langspec = archive.read('langspec.json')
28
- return json.loads(langspec)
29
-
30
- def load_language_specification_from_json(json_file: str) -> dict:
31
- """
32
- Read a MAL language JSON specification file
33
-
34
- Arguments:
35
- file_spec - a language specification file that can be for example
36
- provided by malc (https://github.com/mal-lang/malc)
37
-
38
- Return:
39
- A dictionary representing the language specification
40
- """
41
-
42
- logger.info(f'Load language specfication from \'{json_file}\'.')
43
- with open(json_file, 'r', encoding='utf-8') as spec:
44
- data = spec.read()
45
- return json.loads(data)
46
-
47
-
48
- def save_language_specification_to_json(lang_spec: dict, filename: str) -> dict:
49
- """
50
- Save a MAL language specification dictionary to a JSON file
51
-
52
- Arguments:
53
- lang_spec - a dictionary containing the MAL language specification
54
- filename - the JSON filename where the language specification will
55
- be written
56
- """
57
-
58
- logger.info(f'Save language specfication to {filename}.')
59
-
60
- with open(filename, 'w', encoding='utf-8') as file:
61
- json.dump(lang_spec, file, indent=4)
62
-
63
-
64
- def get_attacks_for_class(lang_spec: dict, asset_type: str) -> dict:
65
- """
66
- Get all Attack Steps for a specific Class
67
-
68
- Arguments:
69
- lang_spec - a dictionary containing the MAL language specification
70
- asset_type - a string representing the class for which we want to list
71
- the possible attack steps
72
-
73
- Return:
74
- A dictionary representing the set of possible attacks for the specified
75
- class. Each key in the dictionary is an attack name and is associated
76
- with a dictionary containing other characteristics of the attack such as
77
- type of attack, TTC distribution, child attack steps and other information
78
- """
79
- attacks = {}
80
- asset = next((asset for asset in lang_spec['assets'] if asset['name'] == \
81
- asset_type), None)
82
- if not asset:
83
- logger.error(f'Failed to find asset type {asset_type} when '\
84
- 'looking for attack steps.')
85
- return None
86
-
87
- logger.debug(f'Get attack steps for {asset["name"]} asset from '\
88
- 'language specification.')
89
- if asset['superAsset']:
90
- logger.debug(f'Asset extends another one, fetch the superclass '\
91
- 'attack steps for it.')
92
- attacks = get_attacks_for_class(lang_spec, asset['superAsset'])
93
-
94
- for attack in asset['attackSteps']:
95
- if attack['name'] not in attacks:
96
- attacks[attack['name']] = copy.deepcopy(attack)
97
- else:
98
- if not attack['reaches']:
99
- # This attack step does not lead to any attack steps
100
- continue
101
- if attack['reaches']['overrides'] == True:
102
- attacks[attack['name']] = copy.deepcopy(attack)
103
- else:
104
- attacks[attack['name']]['reaches']['stepExpressions'].\
105
- extend(attack['reaches']['stepExpressions'])
106
-
107
- return attacks
108
-
109
- def get_associations_for_class(lang_spec: dict, asset_type: str) -> dict:
110
- """
111
- Get all Associations for a specific Class
112
-
113
- Arguments:
114
- lang_spec - a dictionary containing the MAL language specification
115
- asset_type - a string representing the class for which we want to list
116
- the associations
117
-
118
- Return:
119
- A dictionary representing the set of associations for the specified
120
- class. Each key in the dictionary is an attack name and is associated
121
- with a dictionary containing other characteristics of the attack such as
122
- type of attack, TTC distribution, child attack steps and other information
123
- """
124
- logger.debug(f'Get associations for {asset_type} asset from '\
125
- 'language specification.')
126
- associations = []
127
-
128
- asset = next((asset for asset in lang_spec['assets'] if asset['name'] == \
129
- asset_type), None)
130
- if not asset:
131
- logger.error(f'Failed to find asset type {asset_type} when '\
132
- 'looking for associations.')
133
- return None
134
-
135
- if asset['superAsset']:
136
- logger.debug(f'Asset extends another one, fetch the superclass '\
137
- 'associations for it.')
138
- associations.extend(get_associations_for_class(lang_spec,
139
- asset['superAsset']))
140
- assoc_iter = (assoc for assoc in lang_spec['associations'] \
141
- if assoc['leftAsset'] == asset_type or \
142
- assoc['rightAsset'] == asset_type)
143
- assoc = next(assoc_iter, None)
144
- while (assoc):
145
- associations.append(assoc)
146
- assoc = next(assoc_iter, None)
147
-
148
- return associations
149
-
150
- def get_association_by_fields_and_assets(lang_spec: dict,
151
- first_field: str,
152
- second_field: str,
153
- first_asset: str,
154
- second_asset: str) -> str:
155
- """
156
- Get an association based on its field names and asset types
157
-
158
- Arguments:
159
- lang_spec - a dictionary containing the MAL language specification
160
- first_field - a string containing the first field
161
- second_field - a string containing the second field
162
- first_asset - a string representing the first asset type
163
- second_asset - a string representing the second asset type
164
-
165
- Return:
166
- The name of the association matching the fieldnames and asset types.
167
- None if there is no match.
168
- """
169
- for assoc in lang_spec['associations']:
170
- logger.debug(f'Compare (\"{first_asset}\",'
171
- f'\"{first_field}\",'
172
- f'\"{second_asset}\",'
173
- f'\"{second_field}\") to '
174
- f'(\"{assoc["leftAsset"]}\",'
175
- f'\"{assoc["leftField"]}\",'
176
- f'\"{assoc["rightAsset"]}\",'
177
- f'\"{assoc["rightField"]}\").')
178
-
179
- # If the asset and fields match either way we accept it as a match.
180
- if assoc['leftField'] == first_field and \
181
- assoc['rightField'] == second_field and \
182
- extends_asset(lang_spec, first_asset, assoc['leftAsset']) and \
183
- extends_asset(lang_spec, second_asset, assoc['rightAsset']):
184
- return assoc['name']
185
- if assoc['leftField'] == second_field and \
186
- assoc['rightField'] == first_field and \
187
- extends_asset(lang_spec, second_asset, assoc['leftAsset']) and \
188
- extends_asset(lang_spec, first_asset, assoc['rightAsset']):
189
- return assoc['name']
190
-
191
- return None
192
-
193
- def get_variable_for_class_by_name(lang_spec: dict, asset_type: str,
194
- variable_name:str) -> dict:
195
- """
196
- Get a variables for a specific asset type by name.
197
- NOTE: Variables are the ones specified in MAL through `let` statements
198
-
199
- Arguments:
200
- lang_spec - a dictionary containing the MAL language specification
201
- asset_type - a string representing the type of asset which contains
202
- the variable
203
- variable_name - the name of the variable to search for
204
-
205
- Return:
206
- A dictionary representing the step expressions for the specified variable.
207
- """
208
-
209
- asset = next((asset for asset in lang_spec['assets'] if asset['name'] == \
210
- asset_type), None)
211
- if not asset:
212
- logger.error(f'Failed to find asset type {asset_type} when '\
213
- 'looking for variable.')
214
- return None
215
-
216
- variable_dict = next((variable for variable in \
217
- asset['variables'] if variable['name'] == variable_name), None)
218
- if not variable_dict:
219
- if asset['superAsset']:
220
- variable_dict = get_variable_for_class_by_name(lang_spec,
221
- asset['superAsset'], variable_name)
222
- if variable_dict:
223
- return variable_dict
224
- else:
225
- logger.error(f'Failed to find variable {variable_name} in '\
226
- f'{asset_type}\'s language specification.')
227
- return None
228
-
229
- return variable_dict['stepExpression']
230
-
231
- def extends_asset(lang_spec: dict, asset, target_asset):
232
- """
233
- Check if an asset extends the target asset through inheritance.
234
-
235
- Arguments:
236
- lang_spec - a dictionary containing the MAL language specification
237
- asset - the asset name we wish to evaluate
238
- target_asset - the target asset name we wish to evaluate if it
239
- is extended
240
-
241
- Return:
242
- True if this asset extends the target_asset via inheritance.
243
- False otherwise.
244
- """
245
-
246
- logger.debug(f'Check if {asset} extends {target_asset} via inheritance.')
247
-
248
- if asset == target_asset:
249
- return True
250
-
251
- asset_dict = next((asset_info for asset_info in lang_spec['assets'] \
252
- if asset_info['name'] == asset), None)
253
- if not asset_dict:
254
- logger.error(f'Failed to find asset type {asset} when '\
255
- 'looking for variable.')
256
- return False
257
- if asset_dict['superAsset']:
258
- if asset_dict['superAsset'] == target_asset:
259
- return True
260
- else:
261
- return extends_asset(lang_spec, asset_dict['superAsset'],
262
- target_asset)
263
-
264
- return False
265
-
maltoolbox/main.py DELETED
@@ -1,84 +0,0 @@
1
- """
2
- MAL-Toolbox Main Module
3
- """
4
-
5
- import sys
6
-
7
- import logging
8
- import json
9
-
10
- import maltoolbox
11
- import maltoolbox.cl_parser
12
- from maltoolbox.language import classes_factory
13
- from maltoolbox.language import specification
14
- from maltoolbox.model import model
15
- from maltoolbox.attackgraph import attackgraph
16
- from maltoolbox.attackgraph.analyzers import apriori
17
- from maltoolbox.ingestors import neo4j
18
-
19
- logger = logging.getLogger(__name__)
20
-
21
- def main():
22
- """Main routine of maltoolbox command line interface."""
23
- args = maltoolbox.cl_parser.parse_args(sys.argv[1:])
24
- cmd_params = vars(args)
25
- logger.info('Received the following command line parameters:\n' +
26
- json.dumps(cmd_params, indent = 2))
27
-
28
- match(cmd_params['command']):
29
- case 'gen_ag':
30
- model_filename = cmd_params['model']
31
- langspec_filename = cmd_params['language']
32
-
33
- # Load language specification and generate classes based on it.
34
- lang_spec = specification. \
35
- load_language_specification_from_mar(langspec_filename)
36
- if maltoolbox.log_configs['langspec_file']:
37
- specification.save_language_specification_to_json(lang_spec,
38
- maltoolbox.log_configs['langspec_file'])
39
- lang_classes_factory = \
40
- classes_factory.LanguageClassesFactory(lang_spec)
41
- lang_classes_factory.create_classes()
42
-
43
- # Load instance model.
44
- instance_model = model.Model('Test model', lang_spec,
45
- lang_classes_factory)
46
- instance_model.load_from_file(model_filename)
47
- if maltoolbox.log_configs['model_file']:
48
- instance_model.save_to_file( \
49
- maltoolbox.log_configs['model_file'])
50
-
51
- graph = attackgraph.AttackGraph()
52
- result = graph.generate_graph(lang_spec, instance_model)
53
- if result > 0:
54
- logger.error('Attack graph generation failed!')
55
- print('Attack graph generation failed!')
56
- exit(result)
57
-
58
- apriori.calculate_viability_and_necessity(graph)
59
-
60
- graph.attach_attackers(instance_model)
61
-
62
- if maltoolbox.log_configs['attackgraph_file']:
63
- graph.save_to_file(
64
- maltoolbox.log_configs['attackgraph_file'])
65
-
66
- if cmd_params['neo4j']:
67
- # Injest instance model and attack graph into Neo4J.
68
- logger.debug('Ingest model graph into Neo4J database.')
69
- neo4j.ingest_model(instance_model,
70
- maltoolbox.neo4j_configs['uri'],
71
- maltoolbox.neo4j_configs['username'],
72
- maltoolbox.neo4j_configs['password'],
73
- maltoolbox.neo4j_configs['dbname'],
74
- delete=True)
75
- logger.debug('Ingest attack graph into Neo4J database.')
76
- neo4j.ingest_attack_graph(graph,
77
- maltoolbox.neo4j_configs['uri'],
78
- maltoolbox.neo4j_configs['username'],
79
- maltoolbox.neo4j_configs['password'],
80
- maltoolbox.neo4j_configs['dbname'],
81
- delete=False)
82
-
83
- case _:
84
- logger.error(f'Received unknown command: {cmd_params["command"]}.')
maltoolbox/model/model.py DELETED
@@ -1,279 +0,0 @@
1
- """
2
- MAL-Toolbox Model Module
3
- """
4
-
5
- import json
6
- import logging
7
-
8
- from dataclasses import dataclass
9
- from typing import List
10
-
11
- logger = logging.getLogger(__name__)
12
-
13
- @dataclass
14
- class Attacker:
15
- id: str = None
16
- name: str = None
17
- entry_points: List[tuple] = None
18
-
19
- class Model:
20
- latestId = 0
21
-
22
- def __init__(self, name, lang_spec, lang_classes_factory):
23
- self.name = name
24
- self.assets = []
25
- self.associations = []
26
- self.attackers = []
27
- self.lang_spec = lang_spec
28
- self.lang_classes_factory = lang_classes_factory
29
-
30
- def add_asset(self, asset, asset_id: int = None):
31
- """
32
- Add an asset to the model.
33
- """
34
- if asset_id is not None:
35
- for existing_asset in self.assets:
36
- if asset_id == existing_asset.id:
37
- raise ValueError(f'Asset index {asset_id} already in use.')
38
- asset.id = asset_id
39
- else:
40
- asset.id = self.latestId
41
- self.latestId = max(asset.id + 1, self.latestId)
42
-
43
- asset.associations = []
44
- if not hasattr(asset, 'name'):
45
- asset.name = asset.metaconcept + ':' + str(asset.id)
46
- self.assets.append(asset)
47
-
48
- def add_association(self, association):
49
- """
50
- Add an association to the model.
51
- """
52
- for prop in range(0, 2):
53
- for asset in getattr(association,
54
- list(vars(association)['_properties'])[prop]):
55
- assocs = list(asset.associations)
56
- assocs.append(association)
57
- asset.associations = assocs
58
- self.associations.append(association)
59
-
60
- def add_attacker(self, attacker, attacker_id: int = None):
61
- """
62
- Add an attacker to the model.
63
- """
64
- if attacker_id:
65
- attacker.id = attacker_id
66
- else:
67
- attacker.id = self.latestId
68
- self.latestId = max(attacker.id + 1, self.latestId)
69
-
70
- if not hasattr(attacker, 'name') or not attacker.name:
71
- attacker.name = 'Attacker:' + str(attacker.id)
72
- self.attackers.append(attacker)
73
-
74
- def get_asset_by_id(self, asset_id):
75
- """
76
- Find an asset in the model based on its id.
77
-
78
- Arguments:
79
- asset_id - the id of the asset we are looking for
80
-
81
- Return:
82
- An asset matching the id if it exists in the model.
83
- """
84
- return next((asset for asset in self.assets if asset.id == asset_id),
85
- None)
86
-
87
- def get_attacker_by_id(self, attacker_id):
88
- """
89
- Find an attacker in the model based on its id.
90
-
91
- Arguments:
92
- attacker_id - the id of the attacker we are looking for
93
-
94
- Return:
95
- An attacker matching the id if it exists in the model.
96
- """
97
- return next((attacker for attacker in self.attackers \
98
- if attacker.id == attacker_id), None)
99
-
100
- def get_associated_assets_by_field_name(self, asset, field_name):
101
- """
102
- Get a list of associated assets for an asset given a fieldname.
103
-
104
- Arguments:
105
- asset - the asset whose fields we are interested in
106
- fieldname - the field name we are looking for
107
-
108
- Return:
109
- A list of assets associated with the asset given that match the
110
- fieldname.
111
- """
112
- logger.debug(f'Get associated assets for asset '
113
- f'{asset.name}(id:{asset.id}) by field name {field_name}.')
114
- associated_assets = []
115
- for association in asset.associations:
116
- # Determine which two of the end points leads away from the asset.
117
- # This is particularly relevant for associations between two
118
- # assets of the same type.
119
- if asset in getattr(association,
120
- list(vars(association)['_properties'])[0]):
121
- elementName = list(vars(association)['_properties'])[1]
122
- else:
123
- elementName = list(vars(association)['_properties'])[0]
124
-
125
- if elementName == field_name:
126
- associated_assets.extend(getattr(association, elementName))
127
-
128
- return associated_assets
129
-
130
- def asset_to_json(self, asset):
131
- """
132
- Convert an asset to its JSON format.
133
- """
134
- defenses = {}
135
- logger.debug(f'Translating {asset.name} to json.')
136
- for defense in list(vars(asset)['_properties'])[1:]:
137
- defenseValue = getattr(asset, defense)
138
- logger.debug(f'Translating {defense}: {defenseValue} defense '\
139
- 'to json.')
140
- if defenseValue != getattr(asset, defense).default():
141
- # Save the default values that are not the default ones.
142
- defenses[str(defense)] = str(defenseValue)
143
- return (str(asset.id), {
144
- 'name': str(asset.name),
145
- 'metaconcept': str(asset.metaconcept),
146
- 'eid': str(asset.id),
147
- 'defenses': defenses
148
- }
149
- )
150
-
151
- def association_to_json(self, association):
152
- """
153
- Convert an association to its JSON format.
154
- """
155
- firstElementName = list(vars(association)['_properties'])[0]
156
- secondElementName = list(vars(association)['_properties'])[1]
157
- firstElements = getattr(association, firstElementName)
158
- secondElements = getattr(association, secondElementName)
159
- json_association = {
160
- 'metaconcept': type(association).__name__,
161
- 'association': {
162
- str(firstElementName): [str(asset.id) for asset in firstElements],
163
- str(secondElementName): [str(asset.id) for asset in secondElements]
164
- }
165
- }
166
- return json_association
167
-
168
- def attacker_to_json(self, attacker):
169
- """
170
- Convert an attacker to its JSON format.
171
- """
172
- logger.debug(f'Translating {attacker.name} to json.')
173
- json_attacker = {
174
- 'name': str(attacker.name),
175
- 'entry_points': {},
176
- }
177
- for (asset, attack_steps) in attacker.entry_points:
178
- json_attacker['entry_points'][str(asset.id)] = {
179
- 'attack_steps' : attack_steps
180
- }
181
- return (str(attacker.id), json_attacker)
182
-
183
- def model_to_json(self):
184
- """
185
- Convert the model to its JSON format.
186
- """
187
- logger.debug(f'Converting model to JSON format.')
188
- contents = {
189
- 'metadata': {},
190
- 'assets': {},
191
- 'associations': [],
192
- 'attackers' : {}
193
- }
194
- contents['metadata'] = {
195
- 'name': self.name,
196
- 'langVersion': self.lang_spec['defines']['version'],
197
- 'langID': self.lang_spec['defines']['id'],
198
- 'malVersion': '0.1.0-SNAPSHOT',
199
- 'info': 'Created by the mal-toolbox model python module.'
200
- }
201
-
202
- logger.debug('Translating assets to json.')
203
- for asset in self.assets:
204
- (asset_id, asset_json) = self.asset_to_json(asset)
205
- contents['assets'][asset_id] = asset_json
206
-
207
- logger.debug('Translating associations to json.')
208
- for association in self.associations:
209
- assoc_json = self.association_to_json(association)
210
- contents['associations'].append(assoc_json)
211
-
212
- logger.debug('Translating attackers to json.')
213
- for attacker in self.attackers:
214
- (attacker_id, attacker_json) = self.attacker_to_json(attacker)
215
- contents['attackers'][attacker_id] = attacker_json
216
- return contents
217
-
218
- def save_to_file(self, filename):
219
- """
220
- Save model to a json file.
221
-
222
- Arguments:
223
- filename - the name of the output file
224
- """
225
-
226
- logger.info(f'Saving model to {filename} file.')
227
- contents = self.model_to_json()
228
- fp = open(filename, 'w')
229
- json.dump(contents, fp, indent = 2)
230
-
231
- def load_from_file(self, filename):
232
- """
233
- Load model from a json file.
234
-
235
- Arguments:
236
- filename - the name of the input file
237
- """
238
- logger.info(f'Loading model from {filename} file.')
239
- with open(filename, 'r', encoding='utf-8') as model_file:
240
- json_model = json.loads(model_file.read())
241
-
242
- self.name = json_model['metadata']['name']
243
-
244
- # Reconstruct the assets
245
- for asset_id in json_model['assets']:
246
- asset_object = json_model['assets'][asset_id]
247
- logger.debug(f'Loading asset from {filename}:\n' \
248
- + json.dumps(asset_object, indent = 2))
249
- asset = getattr(self.lang_classes_factory.ns,
250
- asset_object['metaconcept'])(name = asset_object['name'])
251
- for defense in asset_object['defenses']:
252
- setattr(asset, defense,
253
- float(asset_object['defenses'][defense]))
254
- self.add_asset(asset, asset_id = int(asset_id))
255
-
256
- # Reconstruct the associations
257
- if 'associations' in json_model:
258
- for association_object in json_model['associations']:
259
- association = getattr(self.lang_classes_factory.ns,
260
- association_object['metaconcept'])()
261
- for field in association_object['association']:
262
- asset_list = []
263
- for asset_id in association_object['association'][field]:
264
- asset_list.append(self.get_asset_by_id(int(asset_id)))
265
- setattr(association, field, asset_list)
266
- self.add_association(association)
267
-
268
- # Reconstruct the attackers
269
- if 'attackers' in json_model:
270
- attackers_info = json_model['attackers']
271
- for attacker_id in attackers_info:
272
- attacker = Attacker(name = attackers_info[attacker_id]['name'])
273
- attacker.entry_points = []
274
- for asset_id in attackers_info[attacker_id]['entry_points']:
275
- attacker.entry_points.append(
276
- (self.get_asset_by_id(int(asset_id)),
277
- attackers_info[attacker_id]['entry_points']\
278
- [asset_id]['attack_steps']))
279
- self.add_attacker(attacker, attacker_id = int(attacker_id))