1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.mal_lang.lib;
18
19 import java.util.ArrayList;
20 import java.util.LinkedHashMap;
21 import java.util.List;
22 import java.util.Locale;
23 import java.util.Map;
24 import org.mal_lang.langspec.AttackStepType;
25 import org.mal_lang.langspec.Lang;
26 import org.mal_lang.langspec.Multiplicity;
27 import org.mal_lang.langspec.Risk;
28 import org.mal_lang.langspec.builders.AssetBuilder;
29 import org.mal_lang.langspec.builders.AssociationBuilder;
30 import org.mal_lang.langspec.builders.AttackStepBuilder;
31 import org.mal_lang.langspec.builders.CategoryBuilder;
32 import org.mal_lang.langspec.builders.LangBuilder;
33 import org.mal_lang.langspec.builders.StepsBuilder;
34 import org.mal_lang.langspec.builders.VariableBuilder;
35 import org.mal_lang.langspec.builders.step.StepAttackStepBuilder;
36 import org.mal_lang.langspec.builders.step.StepCollectBuilder;
37 import org.mal_lang.langspec.builders.step.StepDifferenceBuilder;
38 import org.mal_lang.langspec.builders.step.StepExpressionBuilder;
39 import org.mal_lang.langspec.builders.step.StepFieldBuilder;
40 import org.mal_lang.langspec.builders.step.StepIntersectionBuilder;
41 import org.mal_lang.langspec.builders.step.StepSubTypeBuilder;
42 import org.mal_lang.langspec.builders.step.StepTransitiveBuilder;
43 import org.mal_lang.langspec.builders.step.StepUnionBuilder;
44 import org.mal_lang.langspec.builders.step.StepVariableBuilder;
45 import org.mal_lang.langspec.ttc.TtcAddition;
46 import org.mal_lang.langspec.ttc.TtcDistribution;
47 import org.mal_lang.langspec.ttc.TtcDivision;
48 import org.mal_lang.langspec.ttc.TtcExponentiation;
49 import org.mal_lang.langspec.ttc.TtcExpression;
50 import org.mal_lang.langspec.ttc.TtcFunction;
51 import org.mal_lang.langspec.ttc.TtcMultiplication;
52 import org.mal_lang.langspec.ttc.TtcNumber;
53 import org.mal_lang.langspec.ttc.TtcSubtraction;
54
55
56
57
58
59
60
61 public class LangConverter {
62 private final MalLogger LOGGER;
63 private final Map<String, List<AST.Category>> astCategories = new LinkedHashMap<>();
64 private final List<AST.Association> astAssociations = new ArrayList<>();
65 private final Map<String, String> astDefines = new LinkedHashMap<>();
66 private final Map<String, byte[]> svgIcons;
67 private final Map<String, byte[]> pngIcons;
68 private final String license;
69 private final String notice;
70
71 private LangConverter(
72 AST ast,
73 boolean verbose,
74 boolean debug,
75 Map<String, byte[]> svgIcons,
76 Map<String, byte[]> pngIcons,
77 String license,
78 String notice) {
79 Locale.setDefault(Locale.ROOT);
80 this.LOGGER = new MalLogger("LANG_CONVERTER", verbose, debug);
81
82
83 for (var astDefine : ast.getDefines()) {
84 this.astDefines.put(astDefine.key.id, astDefine.value);
85 }
86
87
88 var allAstCategories = ast.getCategories();
89 for (var astCategory : allAstCategories) {
90 if (!this.astCategories.containsKey(astCategory.name.id)) {
91 this.astCategories.put(astCategory.name.id, new ArrayList<>());
92 }
93 this.astCategories.get(astCategory.name.id).add(astCategory);
94 }
95
96
97 for (var astAssociation : ast.getAssociations()) {
98 this.astAssociations.add(astAssociation);
99 }
100
101 this.svgIcons = svgIcons == null ? Map.of() : Map.copyOf(svgIcons);
102 this.pngIcons = pngIcons == null ? Map.of() : Map.copyOf(pngIcons);
103 this.license = license;
104 this.notice = notice;
105 }
106
107
108
109
110
111
112
113
114
115 public static Lang convert(AST ast) {
116 return convert(ast, false, false, null, null, null, null);
117 }
118
119
120
121
122
123
124
125
126
127
128
129 public static Lang convert(AST ast, boolean verbose, boolean debug) {
130 return convert(ast, verbose, debug, null, null, null, null);
131 }
132
133
134
135
136
137
138
139
140
141
142
143
144
145 public static Lang convert(
146 AST ast,
147 Map<String, byte[]> svgIcons,
148 Map<String, byte[]> pngIcons,
149 String license,
150 String notice) {
151 return convert(ast, false, false, svgIcons, pngIcons, license, notice);
152 }
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168 public static Lang convert(
169 AST ast,
170 boolean verbose,
171 boolean debug,
172 Map<String, byte[]> svgIcons,
173 Map<String, byte[]> pngIcons,
174 String license,
175 String notice) {
176 return new LangConverter(ast, verbose, debug, svgIcons, pngIcons, license, notice).convertLog();
177 }
178
179 private Lang convertLog() {
180 var lang = this.convertLang();
181 LOGGER.print();
182 return lang;
183 }
184
185 private Lang convertLang() {
186 var langBuilder = new LangBuilder();
187
188 for (var entry : this.astDefines.entrySet()) {
189 langBuilder.addDefine(entry.getKey(), entry.getValue());
190 }
191
192 for (var entry : this.astCategories.entrySet()) {
193 var categoryBuilder = new CategoryBuilder(entry.getKey());
194 for (var astCategory : entry.getValue()) {
195 for (var meta : astCategory.meta) {
196 categoryBuilder.getMeta().addEntry(meta.type.id, meta.string);
197 }
198 }
199 langBuilder.addCategory(categoryBuilder);
200 }
201
202 for (var astCategories : this.astCategories.values()) {
203 for (var astCategory : astCategories) {
204 for (var astAsset : astCategory.assets) {
205 var assetBuilder =
206 new AssetBuilder(
207 astAsset.name.id,
208 astCategory.name.id,
209 astAsset.isAbstract,
210 astAsset.parent.isPresent() ? astAsset.parent.get().id : null);
211 for (var meta : astAsset.meta) {
212 assetBuilder.getMeta().addEntry(meta.type.id, meta.string);
213 }
214 for (var astVariable : astAsset.variables) {
215 var variableBuilder =
216 new VariableBuilder(
217 astVariable.name.id, this.convertStepExpression(astVariable.expr, false));
218 assetBuilder.addVariable(variableBuilder);
219 }
220 for (var astAttackStep : astAsset.attackSteps) {
221 var name = astAttackStep.name.id;
222 var type = this.convertAttackStepType(astAttackStep.type);
223 var risk =
224 astAttackStep.cia.isPresent() ? this.convertRisk(astAttackStep.cia.get()) : null;
225 var ttc =
226 astAttackStep.ttc.isPresent()
227 ? this.convertTtcExpression(astAttackStep.ttc.get())
228 : null;
229 var requires =
230 astAttackStep.requires.isPresent()
231 ? this.convertRequires(astAttackStep.requires.get())
232 : null;
233 var reaches =
234 astAttackStep.reaches.isPresent()
235 ? this.convertReaches(astAttackStep.reaches.get())
236 : null;
237 var attackStepBuilder = new AttackStepBuilder(name, type, risk, ttc, requires, reaches);
238 for (var meta : astAttackStep.meta) {
239 attackStepBuilder.getMeta().addEntry(meta.type.id, meta.string);
240 }
241 for (var tag : astAttackStep.tags) {
242 attackStepBuilder.addTag(tag.id);
243 }
244 assetBuilder.addAttackStep(attackStepBuilder);
245 }
246 if (this.svgIcons.containsKey(assetBuilder.getName())) {
247 assetBuilder.setSvgIcon(this.svgIcons.get(assetBuilder.getName()));
248 }
249 if (this.pngIcons.containsKey(assetBuilder.getName())) {
250 assetBuilder.setPngIcon(this.pngIcons.get(assetBuilder.getName()));
251 }
252 langBuilder.addAsset(assetBuilder);
253 }
254 }
255 }
256
257 if (!this.svgIcons.isEmpty() || !this.pngIcons.isEmpty()) {
258 for (var assetBuilder : langBuilder.getAssets()) {
259 if (!assetBuilder.isAbstract() && !LangConverter.assetHasIcon(langBuilder, assetBuilder)) {
260 LOGGER.warning(String.format("No icon found for asset '%s'", assetBuilder.getName()));
261 }
262 }
263 }
264
265 for (var astAssociation : this.astAssociations) {
266 var associationBuilder =
267 new AssociationBuilder(
268 astAssociation.linkName.id,
269 astAssociation.leftAsset.id,
270 astAssociation.leftField.id,
271 this.convertMultiplicity(astAssociation.leftMult),
272 astAssociation.rightAsset.id,
273 astAssociation.rightField.id,
274 this.convertMultiplicity(astAssociation.rightMult));
275 for (var meta : astAssociation.meta) {
276 associationBuilder.getMeta().addEntry(meta.type.id, meta.string);
277 }
278 langBuilder.addAssociation(associationBuilder);
279 }
280
281 if (this.license != null) {
282 langBuilder.setLicense(this.license);
283 }
284
285 if (this.notice != null) {
286 langBuilder.setNotice(this.notice);
287 }
288
289 return Lang.fromBuilder(langBuilder);
290 }
291
292 private AttackStepType convertAttackStepType(AST.AttackStepType astType) {
293 switch (astType) {
294 case ANY:
295 return AttackStepType.OR;
296 case ALL:
297 return AttackStepType.AND;
298 case DEFENSE:
299 return AttackStepType.DEFENSE;
300 case EXIST:
301 return AttackStepType.EXIST;
302 case NOTEXIST:
303 return AttackStepType.NOT_EXIST;
304 default:
305 throw new RuntimeException(String.format("Invalid attack step type %s", astType));
306 }
307 }
308
309 private Risk convertRisk(List<AST.CIA> astCiaList) {
310 boolean isConfidentiality = false;
311 boolean isIntegrity = false;
312 boolean isAvailability = false;
313 for (var astCia : astCiaList) {
314 switch (astCia) {
315 case C:
316 isConfidentiality = true;
317 break;
318 case I:
319 isIntegrity = true;
320 break;
321 case A:
322 isAvailability = true;
323 break;
324 }
325 }
326 return new Risk(isConfidentiality, isIntegrity, isAvailability);
327 }
328
329 private TtcExpression convertTtcExpression(AST.TTCExpr astTtcExpression) {
330 if (astTtcExpression instanceof AST.TTCAddExpr) {
331 var astTtcAddition = (AST.TTCAddExpr) astTtcExpression;
332 return new TtcAddition(
333 this.convertTtcExpression(astTtcAddition.lhs),
334 this.convertTtcExpression(astTtcAddition.rhs));
335 } else if (astTtcExpression instanceof AST.TTCSubExpr) {
336 var astTtcSubtraction = (AST.TTCSubExpr) astTtcExpression;
337 return new TtcSubtraction(
338 this.convertTtcExpression(astTtcSubtraction.lhs),
339 this.convertTtcExpression(astTtcSubtraction.rhs));
340 } else if (astTtcExpression instanceof AST.TTCMulExpr) {
341 var astTtcMultiplication = (AST.TTCMulExpr) astTtcExpression;
342 return new TtcMultiplication(
343 this.convertTtcExpression(astTtcMultiplication.lhs),
344 this.convertTtcExpression(astTtcMultiplication.rhs));
345 } else if (astTtcExpression instanceof AST.TTCDivExpr) {
346 var astTtcDivision = (AST.TTCDivExpr) astTtcExpression;
347 return new TtcDivision(
348 this.convertTtcExpression(astTtcDivision.lhs),
349 this.convertTtcExpression(astTtcDivision.rhs));
350 } else if (astTtcExpression instanceof AST.TTCPowExpr) {
351 var astTtcExponentiation = (AST.TTCPowExpr) astTtcExpression;
352 return new TtcExponentiation(
353 this.convertTtcExpression(astTtcExponentiation.lhs),
354 this.convertTtcExpression(astTtcExponentiation.rhs));
355 } else if (astTtcExpression instanceof AST.TTCFuncExpr) {
356 var astTtcFunction = (AST.TTCFuncExpr) astTtcExpression;
357 return new TtcFunction(
358 TtcDistribution.fromString(astTtcFunction.name.id),
359 astTtcFunction.params.stream().mapToDouble(x -> x).toArray());
360 } else if (astTtcExpression instanceof AST.TTCNumExpr) {
361 var astTtcNumber = (AST.TTCNumExpr) astTtcExpression;
362 return new TtcNumber(astTtcNumber.value);
363 } else {
364 throw new RuntimeException(
365 String.format("Invalid TTC expression type %s", astTtcExpression.getClass().getName()));
366 }
367 }
368
369 private StepsBuilder convertRequires(AST.Requires astRequires) {
370 var stepsBuilder = new StepsBuilder(true);
371 for (var astStepExpression : astRequires.requires) {
372 stepsBuilder.addStepExpression(this.convertStepExpression(astStepExpression, false));
373 }
374 return stepsBuilder;
375 }
376
377 private StepsBuilder convertReaches(AST.Reaches astReaches) {
378 var stepsBuilder = new StepsBuilder(!astReaches.inherits);
379 for (var astStepExpression : astReaches.reaches) {
380 stepsBuilder.addStepExpression(this.convertStepExpression(astStepExpression, true));
381 }
382 return stepsBuilder;
383 }
384
385 private StepExpressionBuilder convertStepExpression(AST.Expr astExpr, boolean isAttackStep) {
386 if (astExpr instanceof AST.UnionExpr) {
387 var astStepUnion = (AST.UnionExpr) astExpr;
388 return new StepUnionBuilder(
389 this.convertStepExpression(astStepUnion.lhs, false),
390 this.convertStepExpression(astStepUnion.rhs, isAttackStep));
391 } else if (astExpr instanceof AST.IntersectionExpr) {
392 var astStepIntersection = (AST.IntersectionExpr) astExpr;
393 return new StepIntersectionBuilder(
394 this.convertStepExpression(astStepIntersection.lhs, false),
395 this.convertStepExpression(astStepIntersection.rhs, isAttackStep));
396 } else if (astExpr instanceof AST.DifferenceExpr) {
397 var astStepDifference = (AST.DifferenceExpr) astExpr;
398 return new StepDifferenceBuilder(
399 this.convertStepExpression(astStepDifference.lhs, false),
400 this.convertStepExpression(astStepDifference.rhs, isAttackStep));
401 } else if (astExpr instanceof AST.StepExpr) {
402 var astStepCollect = (AST.StepExpr) astExpr;
403 return new StepCollectBuilder(
404 this.convertStepExpression(astStepCollect.lhs, false),
405 this.convertStepExpression(astStepCollect.rhs, isAttackStep));
406 } else if (astExpr instanceof AST.TransitiveExpr) {
407 var astStepTransitive = (AST.TransitiveExpr) astExpr;
408 return new StepTransitiveBuilder(this.convertStepExpression(astStepTransitive.e, false));
409 } else if (astExpr instanceof AST.SubTypeExpr) {
410 var astStepSubType = (AST.SubTypeExpr) astExpr;
411 return new StepSubTypeBuilder(
412 astStepSubType.subType.id, this.convertStepExpression(astStepSubType.e, false));
413 } else if (astExpr instanceof AST.IDExpr) {
414 var astStepId = (AST.IDExpr) astExpr;
415 if (isAttackStep) {
416 return new StepAttackStepBuilder(astStepId.id.id);
417 } else {
418 return new StepFieldBuilder(astStepId.id.id);
419 }
420 } else if (astExpr instanceof AST.CallExpr) {
421 var astStepVariable = (AST.CallExpr) astExpr;
422 return new StepVariableBuilder(astStepVariable.id.id);
423 } else {
424 throw new RuntimeException(
425 String.format("Invalid step expression type %s", astExpr.getClass().getName()));
426 }
427 }
428
429 private Multiplicity convertMultiplicity(AST.Multiplicity astMultiplicity) {
430 switch (astMultiplicity) {
431 case ZERO_OR_ONE:
432 return Multiplicity.ZERO_OR_ONE;
433 case ZERO_OR_MORE:
434 return Multiplicity.ZERO_OR_MORE;
435 case ONE:
436 return Multiplicity.ONE;
437 case ONE_OR_MORE:
438 return Multiplicity.ONE_OR_MORE;
439 default:
440 throw new RuntimeException(String.format("Invalid multiplicity %s", astMultiplicity));
441 }
442 }
443
444 private static boolean assetHasIcon(LangBuilder langBuilder, AssetBuilder assetBuilder) {
445 if (assetBuilder.getSvgIcon() != null || assetBuilder.getPngIcon() != null) {
446 return true;
447 }
448 if (assetBuilder.getSuperAsset() == null) {
449 return false;
450 }
451 for (var superAssetBuilder : langBuilder.getAssets()) {
452 if (superAssetBuilder.getName().equals(assetBuilder.getSuperAsset())) {
453 return LangConverter.assetHasIcon(langBuilder, superAssetBuilder);
454 }
455 }
456 throw new IllegalStateException();
457 }
458 }