mal-toolbox 0.2.0__py3-none-any.whl → 0.3.1__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.
- {mal_toolbox-0.2.0.dist-info → mal_toolbox-0.3.1.dist-info}/METADATA +43 -25
- mal_toolbox-0.3.1.dist-info/RECORD +29 -0
- mal_toolbox-0.3.1.dist-info/entry_points.txt +2 -0
- maltoolbox/__init__.py +38 -57
- maltoolbox/__main__.py +43 -14
- maltoolbox/attackgraph/__init__.py +1 -1
- maltoolbox/attackgraph/analyzers/apriori.py +6 -5
- maltoolbox/attackgraph/attacker.py +26 -13
- maltoolbox/attackgraph/attackgraph.py +185 -153
- maltoolbox/attackgraph/node.py +56 -54
- maltoolbox/attackgraph/query.py +4 -2
- maltoolbox/file_utils.py +0 -8
- maltoolbox/ingestors/neo4j.py +146 -157
- maltoolbox/language/__init__.py +7 -3
- maltoolbox/language/compiler/__init__.py +485 -17
- maltoolbox/language/compiler/mal_lexer.py +172 -152
- maltoolbox/language/compiler/mal_parser.py +1370 -663
- maltoolbox/language/languagegraph.py +103 -99
- maltoolbox/model.py +306 -488
- maltoolbox/translators/securicad.py +164 -163
- maltoolbox/translators/updater.py +231 -108
- mal_toolbox-0.2.0.dist-info/RECORD +0 -32
- maltoolbox/default.conf +0 -17
- maltoolbox/language/classes_factory.py +0 -259
- maltoolbox/language/compiler/mal_visitor.py +0 -416
- maltoolbox/wrappers.py +0 -62
- {mal_toolbox-0.2.0.dist-info → mal_toolbox-0.3.1.dist-info}/AUTHORS +0 -0
- {mal_toolbox-0.2.0.dist-info → mal_toolbox-0.3.1.dist-info}/LICENSE +0 -0
- {mal_toolbox-0.2.0.dist-info → mal_toolbox-0.3.1.dist-info}/WHEEL +0 -0
- {mal_toolbox-0.2.0.dist-info → mal_toolbox-0.3.1.dist-info}/top_level.txt +0 -0
|
@@ -1,416 +0,0 @@
|
|
|
1
|
-
# mypy: ignore-errors
|
|
2
|
-
from collections.abc import MutableMapping, MutableSequence
|
|
3
|
-
|
|
4
|
-
from antlr4 import ParseTreeVisitor
|
|
5
|
-
from .mal_parser import malParser
|
|
6
|
-
|
|
7
|
-
# In a rule like `rule: one? two* three`:
|
|
8
|
-
# - ctx.one() would be None if the token was not found on a matching line
|
|
9
|
-
# - ctx.two() would be []
|
|
10
|
-
|
|
11
|
-
class malVisitor(ParseTreeVisitor):
|
|
12
|
-
def __init__(self, compiler, *args, **kwargs):
|
|
13
|
-
self.compiler = compiler
|
|
14
|
-
self.current_file = compiler.current_file # for debug purposes
|
|
15
|
-
|
|
16
|
-
super().__init__(*args, **kwargs)
|
|
17
|
-
|
|
18
|
-
def visitMal(self, ctx):
|
|
19
|
-
langspec = {
|
|
20
|
-
"formatVersion": "1.0.0",
|
|
21
|
-
"defines": {},
|
|
22
|
-
"categories": [],
|
|
23
|
-
"assets": [],
|
|
24
|
-
"associations": [],
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
# no visitDeclaration method needed, `declaration` is a thin rule
|
|
28
|
-
for declaration in (d.getChild(0) for d in ctx.declaration()):
|
|
29
|
-
if result := self.visit(declaration) or True:
|
|
30
|
-
key, value = result
|
|
31
|
-
|
|
32
|
-
if key == "categories":
|
|
33
|
-
category, assets = value
|
|
34
|
-
langspec["categories"].extend(category)
|
|
35
|
-
langspec["assets"].extend(assets)
|
|
36
|
-
continue
|
|
37
|
-
|
|
38
|
-
if key == "defines":
|
|
39
|
-
langspec[key].update(value)
|
|
40
|
-
|
|
41
|
-
if key == "associations":
|
|
42
|
-
langspec[key].extend(value)
|
|
43
|
-
|
|
44
|
-
if key == "include":
|
|
45
|
-
included_file = self.compiler.compile(value)
|
|
46
|
-
for k, v in langspec.items():
|
|
47
|
-
if isinstance(v, MutableMapping):
|
|
48
|
-
langspec[k].update(included_file.get(k, {}))
|
|
49
|
-
if isinstance(v, MutableSequence) and k in included_file:
|
|
50
|
-
langspec[k].extend(included_file[k])
|
|
51
|
-
|
|
52
|
-
for key in ("categories", "assets", "associations"):
|
|
53
|
-
unique = []
|
|
54
|
-
for item in langspec[key]:
|
|
55
|
-
if item not in unique:
|
|
56
|
-
unique.append(item)
|
|
57
|
-
langspec[key] = unique
|
|
58
|
-
|
|
59
|
-
return langspec
|
|
60
|
-
|
|
61
|
-
def visitInclude(self, ctx):
|
|
62
|
-
return ("include", ctx.STRING().getText().strip('"'))
|
|
63
|
-
|
|
64
|
-
def visitDefine(self, ctx):
|
|
65
|
-
return ("defines", {ctx.ID().getText(): ctx.STRING().getText().strip('"')})
|
|
66
|
-
|
|
67
|
-
def visitCategory(self, ctx):
|
|
68
|
-
category = {}
|
|
69
|
-
category["name"] = ctx.ID().getText()
|
|
70
|
-
category["meta"] = {k: v for meta in ctx.meta() for k, v in self.visit(meta)}
|
|
71
|
-
|
|
72
|
-
assets = [self.visit(asset) for asset in ctx.asset()]
|
|
73
|
-
|
|
74
|
-
return ("categories", ([category], assets))
|
|
75
|
-
|
|
76
|
-
def visitMeta(self, ctx):
|
|
77
|
-
return ((ctx.ID().getText(), ctx.STRING().getText().strip('"')),)
|
|
78
|
-
|
|
79
|
-
def visitAsset(self, ctx):
|
|
80
|
-
asset = {}
|
|
81
|
-
asset["name"] = ctx.ID()[0].getText()
|
|
82
|
-
asset["meta"] = {k: v for meta in ctx.meta() for k, v in self.visit(meta)}
|
|
83
|
-
asset["category"] = ctx.parentCtx.ID().getText()
|
|
84
|
-
asset["isAbstract"] = ctx.ABSTRACT() is not None
|
|
85
|
-
|
|
86
|
-
asset["superAsset"] = None
|
|
87
|
-
if len(ctx.ID()) > 1 and ctx.ID()[1]:
|
|
88
|
-
asset["superAsset"] = ctx.ID()[1].getText()
|
|
89
|
-
|
|
90
|
-
asset["variables"] = [self.visit(variable) for variable in ctx.variable()]
|
|
91
|
-
asset["attackSteps"] = [self.visit(step) for step in ctx.step()]
|
|
92
|
-
|
|
93
|
-
return asset
|
|
94
|
-
|
|
95
|
-
def visitStep(self, ctx):
|
|
96
|
-
step = {}
|
|
97
|
-
step["name"] = ctx.ID().getText()
|
|
98
|
-
step["meta"] = {k: v for meta in ctx.meta() for k, v in self.visit(meta)}
|
|
99
|
-
step["type"] = self.visit(ctx.steptype())
|
|
100
|
-
step["tags"] = [self.visit(tag) for tag in ctx.tag()]
|
|
101
|
-
step["risk"] = self.visit(ctx.cias()) if ctx.cias() else None
|
|
102
|
-
step["ttc"] = self.visit(ctx.ttc()) if ctx.ttc() else None
|
|
103
|
-
step["requires"] = (
|
|
104
|
-
self.visit(ctx.precondition()) if ctx.precondition() else None
|
|
105
|
-
)
|
|
106
|
-
step["reaches"] = self.visit(ctx.reaches()) if ctx.reaches() else None
|
|
107
|
-
|
|
108
|
-
return step
|
|
109
|
-
|
|
110
|
-
def visitSteptype(self, ctx):
|
|
111
|
-
return (
|
|
112
|
-
"or"
|
|
113
|
-
if ctx.OR()
|
|
114
|
-
else "and"
|
|
115
|
-
if ctx.AND()
|
|
116
|
-
else "defense"
|
|
117
|
-
if ctx.HASH()
|
|
118
|
-
else "exist"
|
|
119
|
-
if ctx.EXISTS()
|
|
120
|
-
else "notExist"
|
|
121
|
-
if ctx.NOTEXISTS()
|
|
122
|
-
else None # should never happen, the grammar limits it
|
|
123
|
-
)
|
|
124
|
-
|
|
125
|
-
def visitTag(self, ctx):
|
|
126
|
-
return ctx.ID().getText()
|
|
127
|
-
|
|
128
|
-
def visitCias(self, ctx):
|
|
129
|
-
risk = {
|
|
130
|
-
"isConfidentiality": False,
|
|
131
|
-
"isIntegrity": False,
|
|
132
|
-
"isAvailability": False,
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
for cia in ctx.cia():
|
|
136
|
-
risk.update(self.visit(cia))
|
|
137
|
-
|
|
138
|
-
return risk
|
|
139
|
-
|
|
140
|
-
def visitCia(self, ctx):
|
|
141
|
-
key = (
|
|
142
|
-
"isConfidentiality"
|
|
143
|
-
if ctx.C()
|
|
144
|
-
else "isIntegrity"
|
|
145
|
-
if ctx.I()
|
|
146
|
-
else "isAvailability"
|
|
147
|
-
if ctx.A()
|
|
148
|
-
else None
|
|
149
|
-
)
|
|
150
|
-
|
|
151
|
-
return {key: True}
|
|
152
|
-
|
|
153
|
-
def visitTtc(self, ctx):
|
|
154
|
-
ret = self.visit(ctx.ttcexpr())
|
|
155
|
-
|
|
156
|
-
return ret
|
|
157
|
-
|
|
158
|
-
def visitTtcexpr(self, ctx):
|
|
159
|
-
if len(terms := ctx.ttcterm()) == 1:
|
|
160
|
-
return self.visit(terms[0])
|
|
161
|
-
|
|
162
|
-
ret = {}
|
|
163
|
-
|
|
164
|
-
lhs = self.visit(terms[0])
|
|
165
|
-
for i in range(1, len(terms)):
|
|
166
|
-
ret["type"] = (
|
|
167
|
-
"addition"
|
|
168
|
-
if ctx.children[2 * i - 1].getText() == "+"
|
|
169
|
-
else "subtraction"
|
|
170
|
-
)
|
|
171
|
-
ret["lhs"] = lhs
|
|
172
|
-
ret["rhs"] = self.visit(terms[i])
|
|
173
|
-
|
|
174
|
-
lhs = ret.copy()
|
|
175
|
-
|
|
176
|
-
return ret
|
|
177
|
-
|
|
178
|
-
def visitTtcterm(self, ctx):
|
|
179
|
-
if len(factors := ctx.ttcfact()) == 1:
|
|
180
|
-
ret = self.visit(factors[0])
|
|
181
|
-
else:
|
|
182
|
-
ret = {}
|
|
183
|
-
ret["type"] = "multiplication" if ctx.STAR() else "division"
|
|
184
|
-
ret["lhs"] = self.visit(factors[0])
|
|
185
|
-
ret["rhs"] = self.visit(factors[1])
|
|
186
|
-
|
|
187
|
-
return ret
|
|
188
|
-
|
|
189
|
-
def visitTtcfact(self, ctx):
|
|
190
|
-
if len(atoms := ctx.ttcatom()) == 1:
|
|
191
|
-
ret = self.visit(atoms[0])
|
|
192
|
-
else:
|
|
193
|
-
ret = {}
|
|
194
|
-
ret["type"] = "exponentiation"
|
|
195
|
-
ret["lhs"] = self.visit(atoms[0])
|
|
196
|
-
ret["rhs"] = self.visit(atoms[1])
|
|
197
|
-
|
|
198
|
-
return ret
|
|
199
|
-
|
|
200
|
-
def visitTtcatom(self, ctx):
|
|
201
|
-
if ctx.ttcdist():
|
|
202
|
-
ret = self.visit(ctx.ttcdist())
|
|
203
|
-
elif ctx.ttcexpr():
|
|
204
|
-
ret = self.visit(ctx.ttcexpr())
|
|
205
|
-
elif ctx.number():
|
|
206
|
-
ret = self.visit(ctx.number())
|
|
207
|
-
|
|
208
|
-
return ret
|
|
209
|
-
|
|
210
|
-
def visitTtcdist(self, ctx):
|
|
211
|
-
ret = {"type": "function"}
|
|
212
|
-
ret["name"] = ctx.ID().getText()
|
|
213
|
-
ret["arguments"] = []
|
|
214
|
-
|
|
215
|
-
if ctx.LPAREN():
|
|
216
|
-
ret["arguments"] = [self.visit(number)["value"] for number in ctx.number()]
|
|
217
|
-
|
|
218
|
-
return ret
|
|
219
|
-
|
|
220
|
-
def visitPrecondition(self, ctx):
|
|
221
|
-
ret = {}
|
|
222
|
-
ret["overrides"] = True
|
|
223
|
-
ret["stepExpressions"] = [self.visit(expr) for expr in ctx.expr()]
|
|
224
|
-
return ret
|
|
225
|
-
|
|
226
|
-
def visitReaches(self, ctx):
|
|
227
|
-
ret = {}
|
|
228
|
-
ret["overrides"] = ctx.INHERITS() is None
|
|
229
|
-
ret["stepExpressions"] = [self.visit(expr) for expr in ctx.expr()]
|
|
230
|
-
|
|
231
|
-
return ret
|
|
232
|
-
|
|
233
|
-
def visitNumber(self, ctx):
|
|
234
|
-
ret = {"type": "number"}
|
|
235
|
-
ret["value"] = float(ctx.getText())
|
|
236
|
-
|
|
237
|
-
return ret
|
|
238
|
-
|
|
239
|
-
def visitVariable(self, ctx):
|
|
240
|
-
ret = {}
|
|
241
|
-
ret["name"] = ctx.ID().getText()
|
|
242
|
-
ret["stepExpression"] = self.visit(ctx.expr())
|
|
243
|
-
|
|
244
|
-
return ret
|
|
245
|
-
|
|
246
|
-
def visitExpr(self, ctx):
|
|
247
|
-
if len(ctx.parts()) == 1:
|
|
248
|
-
return self.visit(ctx.parts()[0])
|
|
249
|
-
|
|
250
|
-
ret = {}
|
|
251
|
-
lhs = self.visit(ctx.parts()[0])
|
|
252
|
-
for i in range(1, len(ctx.parts())):
|
|
253
|
-
ret["type"] = self.visit(ctx.children[2 * i - 1])
|
|
254
|
-
ret["lhs"] = lhs
|
|
255
|
-
ret["rhs"] = self.visit(ctx.parts()[i])
|
|
256
|
-
lhs = ret.copy()
|
|
257
|
-
|
|
258
|
-
return ret
|
|
259
|
-
|
|
260
|
-
def visitParts(self, ctx):
|
|
261
|
-
if len(ctx.part()) == 1:
|
|
262
|
-
return self.visit(ctx.part()[0])
|
|
263
|
-
|
|
264
|
-
ret = {}
|
|
265
|
-
|
|
266
|
-
lhs = self.visit(ctx.part()[0])
|
|
267
|
-
|
|
268
|
-
for i in range(1, len(ctx.part())):
|
|
269
|
-
ret["type"] = "collect"
|
|
270
|
-
ret["lhs"] = lhs
|
|
271
|
-
ret["rhs"] = self.visit(ctx.part()[i])
|
|
272
|
-
|
|
273
|
-
lhs = ret.copy()
|
|
274
|
-
|
|
275
|
-
return ret
|
|
276
|
-
|
|
277
|
-
def visitPart(self, ctx):
|
|
278
|
-
ret = {}
|
|
279
|
-
if ctx.varsubst():
|
|
280
|
-
ret["type"] = "variable"
|
|
281
|
-
ret["name"] = self.visit(ctx.varsubst())
|
|
282
|
-
elif ctx.LPAREN():
|
|
283
|
-
ret = self.visit(ctx.expr())
|
|
284
|
-
else: # ctx.ID()
|
|
285
|
-
# Resolve type: field or attackStep?
|
|
286
|
-
ret["type"] = self._resolve_part_ID_type(ctx)
|
|
287
|
-
|
|
288
|
-
ret["name"] = ctx.ID().getText()
|
|
289
|
-
|
|
290
|
-
if ctx.STAR():
|
|
291
|
-
ret = {"type": "transitive", "stepExpression": ret}
|
|
292
|
-
|
|
293
|
-
for type_ in ctx.type_(): # mind the trailing underscore
|
|
294
|
-
ret = {
|
|
295
|
-
"type": "subType",
|
|
296
|
-
"subType": self.visit(type_),
|
|
297
|
-
"stepExpression": ret,
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
return ret
|
|
301
|
-
|
|
302
|
-
def _resolve_part_ID_type(self, ctx):
|
|
303
|
-
pctx = ctx.parentCtx
|
|
304
|
-
|
|
305
|
-
# Traverse up the tree until we find the parent of the topmost expr
|
|
306
|
-
# (saying "topmost" as expr can be nested) or the root of the tree.
|
|
307
|
-
while pctx and not isinstance(
|
|
308
|
-
pctx,
|
|
309
|
-
malParser.ReachesContext
|
|
310
|
-
# Expressions are also valid in `let` variable assignments, but
|
|
311
|
-
# there every lexical component of expr is considered a "field",
|
|
312
|
-
# no need to resolve the type in that case. Similarly, preconditions
|
|
313
|
-
# (`<-`) only accept fields.
|
|
314
|
-
):
|
|
315
|
-
pctx = pctx.parentCtx
|
|
316
|
-
|
|
317
|
-
if pctx is None:
|
|
318
|
-
# ctx (the `part`) belongs to a "let" assignment or a precondition.
|
|
319
|
-
return "field"
|
|
320
|
-
|
|
321
|
-
# scan for a dot to the right of `ctx`
|
|
322
|
-
file_tokens = ctx.parser.getTokenStream().tokens
|
|
323
|
-
for i in range(ctx.start.tokenIndex, pctx.stop.tokenIndex + 1):
|
|
324
|
-
if file_tokens[i].type == malParser.DOT:
|
|
325
|
-
return "field"
|
|
326
|
-
|
|
327
|
-
# We are looping until the end of pctx (which is a `reaches` or
|
|
328
|
-
# `precondition` context). This could include multiple comma
|
|
329
|
-
# separated `expr`s, we only care for the current one.
|
|
330
|
-
if file_tokens[i].type == malParser.COMMA: # end of current `expr`
|
|
331
|
-
return "attackStep"
|
|
332
|
-
|
|
333
|
-
return "attackStep"
|
|
334
|
-
|
|
335
|
-
def visitVarsubst(self, ctx):
|
|
336
|
-
return ctx.ID().getText()
|
|
337
|
-
|
|
338
|
-
def visitType(self, ctx):
|
|
339
|
-
return ctx.ID().getText()
|
|
340
|
-
|
|
341
|
-
def visitSetop(self, ctx):
|
|
342
|
-
return (
|
|
343
|
-
"union"
|
|
344
|
-
if ctx.UNION()
|
|
345
|
-
else "intersection"
|
|
346
|
-
if ctx.INTERSECT()
|
|
347
|
-
else "difference"
|
|
348
|
-
if ctx.INTERSECT
|
|
349
|
-
else None
|
|
350
|
-
)
|
|
351
|
-
|
|
352
|
-
def visitAssociations(self, ctx):
|
|
353
|
-
associations = []
|
|
354
|
-
for assoc in ctx.association():
|
|
355
|
-
associations.append(self.visit(assoc))
|
|
356
|
-
|
|
357
|
-
return ("associations", associations)
|
|
358
|
-
|
|
359
|
-
def visitAssociation(self, ctx):
|
|
360
|
-
association = {}
|
|
361
|
-
association["name"] = self.visit(ctx.linkname())
|
|
362
|
-
association["meta"] = {k: v for meta in ctx.meta() for k, v in self.visit(meta)}
|
|
363
|
-
association["leftAsset"] = ctx.ID()[0].getText()
|
|
364
|
-
association["leftField"] = self.visit(ctx.field()[0])
|
|
365
|
-
|
|
366
|
-
# no self.visitMult or self.visitMultatom methods, reading them here
|
|
367
|
-
# directly
|
|
368
|
-
association["leftMultiplicity"] = {
|
|
369
|
-
"min": (multatoms := ctx.mult()[0].multatom()).pop(0).getText(),
|
|
370
|
-
"max": multatoms.pop().getText() if multatoms else None,
|
|
371
|
-
}
|
|
372
|
-
association["rightAsset"] = ctx.ID()[1].getText()
|
|
373
|
-
association["rightField"] = self.visit(ctx.field()[1])
|
|
374
|
-
association["rightMultiplicity"] = {
|
|
375
|
-
"min": (multatoms := ctx.mult()[1].multatom()).pop(0).getText(),
|
|
376
|
-
"max": multatoms.pop().getText() if multatoms else None,
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
self._post_process_multitudes(association)
|
|
380
|
-
return association
|
|
381
|
-
|
|
382
|
-
def _post_process_multitudes(self, association):
|
|
383
|
-
mult_keys = [
|
|
384
|
-
# start the multatoms from right to left to make sure the rules
|
|
385
|
-
# below get applied cleanly
|
|
386
|
-
"rightMultiplicity.max",
|
|
387
|
-
"rightMultiplicity.min",
|
|
388
|
-
"leftMultiplicity.max",
|
|
389
|
-
"leftMultiplicity.min",
|
|
390
|
-
]
|
|
391
|
-
|
|
392
|
-
for mult_key in mult_keys:
|
|
393
|
-
key, subkey = mult_key.split(".")
|
|
394
|
-
|
|
395
|
-
# upper limit equals lower limit if not given
|
|
396
|
-
if subkey == "max" and association[key][subkey] is None:
|
|
397
|
-
association[key][subkey] = association[key]["min"]
|
|
398
|
-
|
|
399
|
-
if association[key][subkey] == "*":
|
|
400
|
-
# 'any' as lower limit means start from 0
|
|
401
|
-
if subkey == "min":
|
|
402
|
-
association[key][subkey] = 0
|
|
403
|
-
|
|
404
|
-
# 'any' as upper limit means not limit
|
|
405
|
-
else:
|
|
406
|
-
association[key][subkey] = None
|
|
407
|
-
|
|
408
|
-
# cast numerical strings to integers
|
|
409
|
-
if (multatom := association[key][subkey]) and multatom.isdigit():
|
|
410
|
-
association[key][subkey] = int(association[key][subkey])
|
|
411
|
-
|
|
412
|
-
def visitField(self, ctx):
|
|
413
|
-
return ctx.ID().getText()
|
|
414
|
-
|
|
415
|
-
def visitLinkname(self, ctx):
|
|
416
|
-
return ctx.ID().getText()
|
maltoolbox/wrappers.py
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
"""Contains wrappers combining more than one of the maltoolbox submodules"""
|
|
2
|
-
|
|
3
|
-
import logging
|
|
4
|
-
import sys
|
|
5
|
-
import zipfile
|
|
6
|
-
|
|
7
|
-
from maltoolbox.model import Model
|
|
8
|
-
from maltoolbox.language import LanguageGraph, LanguageClassesFactory
|
|
9
|
-
from maltoolbox.attackgraph import AttackGraph
|
|
10
|
-
from maltoolbox.attackgraph.analyzers.apriori import (
|
|
11
|
-
calculate_viability_and_necessity
|
|
12
|
-
)
|
|
13
|
-
from maltoolbox.exceptions import AttackGraphStepExpressionError
|
|
14
|
-
from maltoolbox import log_configs
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
logger = logging.getLogger(__name__)
|
|
18
|
-
|
|
19
|
-
def create_attack_graph(
|
|
20
|
-
lang_file: str,
|
|
21
|
-
model_file: str,
|
|
22
|
-
attach_attackers=True,
|
|
23
|
-
calc_viability_and_necessity=True
|
|
24
|
-
) -> AttackGraph:
|
|
25
|
-
"""Create and return an attack graph
|
|
26
|
-
|
|
27
|
-
Args:
|
|
28
|
-
lang_file - path to language file (.mar or .mal)
|
|
29
|
-
model_file - path to model file (yaml or json)
|
|
30
|
-
attach_attackers - whether to run attach_attackers or not
|
|
31
|
-
calc_viability_and_necessity - whether run apriori calculations or not
|
|
32
|
-
"""
|
|
33
|
-
try:
|
|
34
|
-
lang_graph = LanguageGraph.from_mar_archive(lang_file)
|
|
35
|
-
except zipfile.BadZipFile:
|
|
36
|
-
lang_graph = LanguageGraph.from_mal_spec(lang_file)
|
|
37
|
-
|
|
38
|
-
if log_configs['langspec_file']:
|
|
39
|
-
lang_graph.save_to_file(log_configs['langspec_file'])
|
|
40
|
-
|
|
41
|
-
lang_classes_factory = LanguageClassesFactory(lang_graph)
|
|
42
|
-
instance_model = Model.load_from_file(model_file, lang_classes_factory)
|
|
43
|
-
|
|
44
|
-
if log_configs['model_file']:
|
|
45
|
-
instance_model.save_to_file(log_configs['model_file'])
|
|
46
|
-
|
|
47
|
-
try:
|
|
48
|
-
attack_graph = AttackGraph(lang_graph, instance_model)
|
|
49
|
-
except AttackGraphStepExpressionError:
|
|
50
|
-
logger.error(
|
|
51
|
-
'Attack graph generation failed when attempting '
|
|
52
|
-
'to resolve attack step expression!'
|
|
53
|
-
)
|
|
54
|
-
sys.exit(1)
|
|
55
|
-
|
|
56
|
-
if attach_attackers:
|
|
57
|
-
attack_graph.attach_attackers()
|
|
58
|
-
|
|
59
|
-
if calc_viability_and_necessity:
|
|
60
|
-
calculate_viability_and_necessity(attack_graph)
|
|
61
|
-
|
|
62
|
-
return attack_graph
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|