args)
26 | throws CompilerException, FileNotFoundException {
27 | super(false, false);
28 | Locale.setDefault(Locale.ROOT);
29 | if (!args.containsKey("path") || args.get("path").isBlank()) {
30 | throw error("D3 generator requires argument 'path'");
31 | }
32 | var outputDir = new File(args.get("path"));
33 | if (!outputDir.isDirectory()) {
34 | throw error("Argument 'path' must be a directory");
35 | }
36 |
37 | var json = Json.createObjectBuilder();
38 | var assets = Json.createArrayBuilder();
39 | for (var asset : lang.getAssets().values()) {
40 | var jsonAsset = Json.createObjectBuilder();
41 | jsonAsset.add("name", asset.getName());
42 | if (!asset.getAttackSteps().isEmpty()) {
43 | var attackSteps = Json.createArrayBuilder();
44 | for (var attackStep : asset.getAttackSteps().values()) {
45 | var jsonAttackStep = Json.createObjectBuilder();
46 | jsonAttackStep.add("name", attackStep.getName());
47 | switch (attackStep.getType()) {
48 | case ANY:
49 | jsonAttackStep.add("type", "or");
50 | break;
51 | case ALL:
52 | jsonAttackStep.add("type", "and");
53 | break;
54 | case DEFENSE:
55 | case EXIST:
56 | case NOTEXIST:
57 | jsonAttackStep.add("type", "defense");
58 | break;
59 | default:
60 | throw new RuntimeException("Invalid attack step type " + attackStep.getType());
61 | }
62 |
63 | var targets = Json.createArrayBuilder();
64 |
65 | if (asset.hasSuperAsset()) {
66 | // getAttackStep will traverse all parents
67 | var as = asset.getSuperAsset().getAttackStep(attackStep.getName());
68 | if (as != null) {
69 | JsonObjectBuilder jsonStep = Json.createObjectBuilder();
70 | jsonStep.add("name", as.getName());
71 | jsonStep.add("entity_name", as.getAsset().getName());
72 | jsonStep.add("size", 4000);
73 | targets.add(jsonStep);
74 | }
75 | }
76 |
77 | for (var expr : attackStep.getReaches()) {
78 | var as = getAttackStep(expr);
79 | JsonObjectBuilder jsonStep = Json.createObjectBuilder();
80 | jsonStep.add("name", as.attackStep.getName());
81 | jsonStep.add("entity_name", as.attackStep.getAsset().getName());
82 | jsonStep.add("size", 4000);
83 | targets.add(jsonStep);
84 | }
85 | jsonAttackStep.add("targets", targets);
86 | attackSteps.add(jsonAttackStep);
87 | }
88 | jsonAsset.add("children", attackSteps);
89 | }
90 | assets.add(jsonAsset);
91 | }
92 | json.add("children", assets);
93 | var jsonString = json.build().toString();
94 |
95 | var name = lang.getDefine("id");
96 | var output = new File(outputDir, "visualization." + name + ".html");
97 | var is = getClass().getResourceAsStream("/d3/visualization.html");
98 | var reader = new BufferedReader(new InputStreamReader(is));
99 | String content = reader.lines().collect(Collectors.joining(System.lineSeparator()));
100 | content = content.replace("{{NAME}}", name).replace("{{JSON}}", jsonString);
101 |
102 | try (var pw = new PrintWriter(output)) {
103 | pw.write(content);
104 | }
105 | }
106 |
107 | private StepAttackStep getAttackStep(StepExpr expr) {
108 | if (expr instanceof StepAttackStep) {
109 | return (StepAttackStep) expr;
110 | } else if (expr instanceof StepBinOp) {
111 | return getAttackStep(((StepBinOp) expr).rhs);
112 | } else {
113 | throw new RuntimeException("Unexpected expression " + expr);
114 | }
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/java/org/mal_lang/compiler/lib/securicad/ExpressionGenerator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019-2022 Foreseeti AB
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.mal_lang.compiler.lib.securicad;
17 |
18 | import com.squareup.javapoet.ClassName;
19 | import com.squareup.javapoet.MethodSpec;
20 | import com.squareup.javapoet.ParameterizedTypeName;
21 | import com.squareup.javapoet.TypeName;
22 | import com.squareup.javapoet.TypeSpec;
23 | import java.util.HashSet;
24 | import java.util.Set;
25 | import javax.lang.model.element.Modifier;
26 | import org.mal_lang.compiler.lib.JavaGenerator;
27 | import org.mal_lang.compiler.lib.Lang.AttackStep;
28 | import org.mal_lang.compiler.lib.Lang.StepExpr;
29 | import org.mal_lang.compiler.lib.MalLogger;
30 |
31 | public class ExpressionGenerator extends JavaGenerator {
32 |
33 | protected ExpressionGenerator(MalLogger LOGGER, String pkg) {
34 | super(LOGGER, pkg);
35 | }
36 |
37 | protected void createGetAttackStepChildren(
38 | TypeSpec.Builder parentBuilder, AttackStep attackStep, String cacheName) {
39 | Name.reset();
40 | MethodSpec.Builder builder = MethodSpec.methodBuilder("getAttackStepChildren");
41 | builder.addAnnotation(Override.class);
42 | builder.addModifiers(Modifier.PUBLIC);
43 | ClassName set = ClassName.get(Set.class);
44 | ClassName as = ClassName.get("com.foreseeti.simulator", "AttackStep");
45 | ClassName hashSet = ClassName.get(HashSet.class);
46 | TypeName asSet = ParameterizedTypeName.get(set, as);
47 | builder.returns(asSet);
48 |
49 | builder.beginControlFlow("if ($N == null)", cacheName);
50 | if (attackStep.inheritsReaches()) {
51 | builder.addStatement("$T tmpCache = new $T<>(super.getAttackStepChildren())", asSet, hashSet);
52 | } else {
53 | builder.addStatement("$T tmpCache = new $T<>()", asSet, hashSet);
54 | }
55 | for (StepExpr expr : attackStep.getReaches()) {
56 | AutoFlow af = new AutoFlow();
57 | AutoFlow end = generateExpr(af, expr, attackStep.getAsset());
58 | end.addStatement("tmpCache.add($L)", end.prefix);
59 | af.build(builder);
60 | }
61 | // copyOf returns an immutable set
62 | builder.addStatement("$N = $T.copyOf(tmpCache)", cacheName, set);
63 | builder.endControlFlow();
64 |
65 | builder.addStatement("return $N", cacheName);
66 | parentBuilder.addMethod(builder.build());
67 | }
68 |
69 | protected void createSetExpectedParents(
70 | TypeSpec.Builder parentBuilder, AttackStep attackStep, String cacheName) {
71 | Name.reset();
72 | MethodSpec.Builder builder = MethodSpec.methodBuilder("setExpectedParents");
73 | builder.addAnnotation(Override.class);
74 | builder.addModifiers(Modifier.PUBLIC);
75 | ClassName concreteSample = ClassName.get("com.foreseeti.simulator", "ConcreteSample");
76 | builder.addParameter(concreteSample, "sample");
77 |
78 | builder.addStatement("super.setExpectedParents(sample)");
79 | builder.beginControlFlow("if ($N == null)", cacheName);
80 | builder.addStatement("$N = new $T<>()", cacheName, HashSet.class);
81 | for (StepExpr expr : attackStep.getParentSteps()) {
82 | AutoFlow af = new AutoFlow();
83 | AutoFlow end = generateExpr(af, expr, attackStep.getAsset());
84 | end.addStatement("$N.add($N)", cacheName, end.prefix);
85 | af.build(builder);
86 | }
87 | builder.endControlFlow();
88 |
89 | builder.beginControlFlow("if (sample != null)");
90 | ClassName as = ClassName.get("com.foreseeti.simulator", "AttackStep");
91 | builder.beginControlFlow("for ($T attackStep : $N)", as, cacheName);
92 | builder.addStatement("sample.addExpectedParent(this, attackStep)");
93 | builder.endControlFlow(); // for
94 | builder.endControlFlow(); // if
95 |
96 | parentBuilder.addMethod(builder.build());
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/java/org/mal_lang/compiler/lib/securicad/VariableGenerator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019-2022 Foreseeti AB
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.mal_lang.compiler.lib.securicad;
17 |
18 | import com.squareup.javapoet.ClassName;
19 | import com.squareup.javapoet.MethodSpec;
20 | import com.squareup.javapoet.ParameterizedTypeName;
21 | import com.squareup.javapoet.TypeName;
22 | import com.squareup.javapoet.TypeSpec;
23 | import java.util.HashSet;
24 | import java.util.Set;
25 | import javax.lang.model.element.Modifier;
26 | import org.mal_lang.compiler.lib.JavaGenerator;
27 | import org.mal_lang.compiler.lib.Lang.Asset;
28 | import org.mal_lang.compiler.lib.Lang.StepExpr;
29 | import org.mal_lang.compiler.lib.MalLogger;
30 |
31 | public class VariableGenerator extends JavaGenerator {
32 |
33 | private final ExpressionGenerator exprGen;
34 |
35 | protected VariableGenerator(MalLogger LOGGER, String pkg) {
36 | super(LOGGER, pkg);
37 | this.exprGen = new ExpressionGenerator(LOGGER, pkg);
38 | }
39 |
40 | protected void generate(TypeSpec.Builder parentBuilder, String name, StepExpr expr, Asset asset) {
41 | String setName = String.format("_cache%s", name);
42 | String methodName = String.format("_%s", name);
43 |
44 | MethodSpec.Builder builder = MethodSpec.methodBuilder(methodName);
45 | builder.addModifiers(Modifier.PROTECTED);
46 | ClassName targetType = ClassName.get(pkg, expr.subTarget.getName());
47 | ClassName set = ClassName.get(Set.class);
48 | TypeName targetSet = ParameterizedTypeName.get(set, targetType);
49 | ClassName hashSet = ClassName.get(HashSet.class);
50 | builder.returns(targetSet);
51 |
52 | parentBuilder.addField(targetSet, setName, Modifier.PRIVATE);
53 |
54 | builder.beginControlFlow("if ($N == null)", setName);
55 | builder.addStatement("$T tmpCache = new $T<>()", targetSet, hashSet);
56 | AutoFlow varFlow = new AutoFlow();
57 | AutoFlow end = this.exprGen.generateExpr(varFlow, expr, asset);
58 | end.addStatement("tmpCache.add($N)", end.prefix);
59 | varFlow.build(builder);
60 | // copyOf returns an immutable set
61 | builder.addStatement("$N = $T.copyOf(tmpCache)", setName, set);
62 | builder.endControlFlow();
63 |
64 | builder.addStatement("return $N", setName);
65 |
66 | parentBuilder.addMethod(builder.build());
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/java/org/mal_lang/formatter/Formatter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020-2022 Foreseeti AB
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.mal_lang.formatter;
17 |
18 | import java.io.File;
19 | import java.io.FileOutputStream;
20 | import java.io.IOException;
21 | import java.util.Locale;
22 | import java.util.Map;
23 | import org.mal_lang.compiler.lib.CompilerException;
24 | import org.mal_lang.compiler.lib.Lexer;
25 | import org.mal_lang.compiler.lib.MalLogger;
26 |
27 | /**
28 | * Code formatter for MAL. The layout algorithm is based on three simple combinators; stacking,
29 | * juxtaposing, and choice. The three combinators can be concatenated to produce virtually any
30 | * layout for the lowest cost.
31 | *
32 | * Yelland, P. (2016). A New Approach to Optimal Code Formatting.
33 | */
34 | public class Formatter {
35 | private static MalLogger LOGGER;
36 |
37 | public static String format(File file, Map opts)
38 | throws IOException, CompilerException {
39 | boolean inplace = false;
40 | if (opts.containsKey("inplace")) {
41 | switch (opts.get("inplace").toLowerCase().strip()) {
42 | case "true":
43 | inplace = true;
44 | break;
45 | case "false":
46 | inplace = false;
47 | break;
48 | default:
49 | throw new CompilerException(
50 | "Optional argument 'inplace' must be either 'true' or 'false'");
51 | }
52 | }
53 | int margin = opts.containsKey("margin") ? Integer.parseInt(opts.get("margin")) : 100;
54 | if (margin < 0) {
55 | throw new CompilerException("Optional argument 'margin' must be a positive integer");
56 | }
57 | return format(file, margin, inplace);
58 | }
59 |
60 | public static String format(File file, int margin, boolean inplace)
61 | throws IOException, CompilerException {
62 | Locale.setDefault(Locale.ROOT);
63 | LOGGER = new MalLogger("FORMATTER", false, false);
64 | try {
65 | org.mal_lang.compiler.lib.Parser.parse(file);
66 | } catch (IOException e) {
67 | throw e;
68 | } catch (CompilerException e) {
69 | LOGGER.error("Code to be formatted must be syntactically valid");
70 | LOGGER.print();
71 | throw e;
72 | }
73 | var p = new Parser(file);
74 | p.parse();
75 | var output = p.getOutput(margin);
76 | output = output.replaceAll("(?m) +$", "");
77 | var bytes = output.getBytes();
78 | var tempFile = File.createTempFile("formatted", ".tmp");
79 | tempFile.deleteOnExit();
80 | try (var fos = new FileOutputStream(tempFile)) {
81 | fos.write(bytes);
82 | if (!Lexer.syntacticallyEqual(new Lexer(file), new Lexer(tempFile))) {
83 | throw new CompilerException(
84 | "The formatter has produced an AST that differs from the input.");
85 | }
86 | } catch (Exception e) {
87 | LOGGER.error("The formatter has produced an invalid AST. Please report this as a bug.");
88 | LOGGER.print();
89 | throw e;
90 | }
91 | if (inplace) {
92 | try (var fos = new FileOutputStream(file, false)) {
93 | fos.write(bytes);
94 | }
95 | } else {
96 | System.out.print(output);
97 | }
98 | return output;
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/java/org/mal_lang/formatter/blocks/AbstractBlock.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020-2022 Foreseeti AB
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.mal_lang.formatter.blocks;
17 |
18 | public abstract class AbstractBlock implements Block {
19 | protected static final int COST_PER_CHARACTER_OVER_MARGIN = 50;
20 | protected static final int COST_PER_NEWLINE = 5;
21 | protected int cost;
22 | protected int index;
23 | protected String output;
24 |
25 | @Override
26 | public int getCost() {
27 | return cost;
28 | }
29 |
30 | @Override
31 | public int getIndex() {
32 | return index;
33 | }
34 |
35 | @Override
36 | public String getOutput() {
37 | return output;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/java/org/mal_lang/formatter/blocks/Block.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020-2022 Foreseeti AB
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.mal_lang.formatter.blocks;
17 |
18 | public interface Block {
19 | void update(int index, int margin);
20 |
21 | int getCost();
22 |
23 | int getIndex();
24 |
25 | String getOutput();
26 | }
27 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/java/org/mal_lang/formatter/blocks/ChoiceBlock.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020-2022 Foreseeti AB
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.mal_lang.formatter.blocks;
17 |
18 | import java.util.List;
19 |
20 | public class ChoiceBlock extends MultiBlock {
21 | public ChoiceBlock(Block... blocks) {
22 | super(blocks);
23 | }
24 |
25 | public ChoiceBlock(List blocks) {
26 | super(blocks);
27 | }
28 |
29 | @Override
30 | public void update(int index, int margin) {
31 | cost = Integer.MAX_VALUE;
32 | for (var block : blocks) {
33 | block.update(index, margin);
34 | if (block.getCost() < cost) {
35 | this.index = block.getIndex();
36 | cost = block.getCost();
37 | output = block.getOutput();
38 | }
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/java/org/mal_lang/formatter/blocks/IndentBlock.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020-2022 Foreseeti AB
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.mal_lang.formatter.blocks;
17 |
18 | public class IndentBlock extends LineBlock {
19 | public IndentBlock(int indent, Block block) {
20 | super(new TextBlock(" ".repeat(indent)), block);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/java/org/mal_lang/formatter/blocks/LineBlock.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020-2022 Foreseeti AB
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.mal_lang.formatter.blocks;
17 |
18 | import java.util.List;
19 |
20 | public class LineBlock extends MultiBlock {
21 | public LineBlock(Block... blocks) {
22 | super(blocks);
23 | }
24 |
25 | public LineBlock(List blocks) {
26 | super(blocks);
27 | }
28 |
29 | @Override
30 | public void update(int index, int margin) {
31 | this.index = index;
32 | cost = 0;
33 | output = "";
34 | for (var block : blocks) {
35 | block.update(this.index, margin);
36 | this.index = block.getIndex();
37 | cost += block.getCost();
38 | output += block.getOutput();
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/java/org/mal_lang/formatter/blocks/MultiBlock.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020-2022 Foreseeti AB
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.mal_lang.formatter.blocks;
17 |
18 | import java.util.ArrayList;
19 | import java.util.Arrays;
20 | import java.util.List;
21 |
22 | public abstract class MultiBlock extends AbstractBlock {
23 | protected List blocks;
24 |
25 | public MultiBlock(List blocks) {
26 | this.blocks = new ArrayList<>(blocks);
27 | }
28 |
29 | public MultiBlock(Block... blocks) {
30 | this(Arrays.asList(blocks));
31 | }
32 |
33 | public void add(Block block) {
34 | blocks.add(block);
35 | }
36 |
37 | public void add(Block... blocks) {
38 | for (var block : blocks) {
39 | add(block);
40 | }
41 | }
42 |
43 | public int getSize() {
44 | return blocks.size();
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/java/org/mal_lang/formatter/blocks/StackBlock.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020-2022 Foreseeti AB
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.mal_lang.formatter.blocks;
17 |
18 | import java.util.List;
19 |
20 | public class StackBlock extends MultiBlock {
21 | public StackBlock(Block... blocks) {
22 | super(blocks);
23 | }
24 |
25 | public StackBlock(List blocks) {
26 | super(blocks);
27 | }
28 |
29 | @Override
30 | public void update(int index, int margin) {
31 | cost = 0;
32 | output = "";
33 | var loop = false;
34 | for (var block : blocks) {
35 | block.update(index, margin);
36 | if (loop) {
37 | cost += 1 * COST_PER_NEWLINE;
38 | output += "\n" + " ".repeat(index);
39 | } else {
40 | loop = true;
41 | }
42 | cost += block.getCost();
43 | output += block.getOutput();
44 | this.index = block.getIndex();
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/java/org/mal_lang/formatter/blocks/TextBlock.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020-2022 Foreseeti AB
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.mal_lang.formatter.blocks;
17 |
18 | public class TextBlock extends AbstractBlock {
19 | boolean free;
20 |
21 | public TextBlock(String value, boolean free) {
22 | output = value;
23 | this.free = free;
24 | }
25 |
26 | public TextBlock(String value) {
27 | this(value, false);
28 | }
29 |
30 | @Override
31 | public void update(int index, int margin) {
32 | this.index = index + this.getOutput().length();
33 | if (free) {
34 | this.cost = 0;
35 | } else if (this.index < margin) {
36 | this.cost = 0;
37 | } else {
38 | this.cost = COST_PER_CHARACTER_OVER_MARGIN * (this.index - margin);
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/java/org/mal_lang/formatter/blocks/WrapBlock.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020-2022 Foreseeti AB
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.mal_lang.formatter.blocks;
17 |
18 | import java.util.ArrayList;
19 | import java.util.Arrays;
20 | import java.util.List;
21 | import java.util.Map;
22 | import java.util.concurrent.ConcurrentHashMap;
23 |
24 | public class WrapBlock extends ChoiceBlock {
25 | private List separators;
26 | private int indent;
27 | private int n;
28 | private List _blocks;
29 | private boolean alwaysSeparate;
30 |
31 | public WrapBlock(List separators, int indent, boolean alwaysSeparate, List blocks) {
32 | super();
33 | this.separators = new ArrayList<>(separators);
34 | this.alwaysSeparate = alwaysSeparate;
35 | this.indent = indent;
36 | _blocks = new ArrayList<>(blocks);
37 | n = _blocks.size();
38 | super.add(b(1));
39 | }
40 |
41 | public WrapBlock(List separators, int indent, List blocks) {
42 | this(separators, indent, true, blocks);
43 | }
44 |
45 | public WrapBlock(String separator, int indent, List blocks) {
46 | this(List.of(new TextBlock(separator)), indent, false, blocks);
47 | }
48 |
49 | public WrapBlock(String separator, int indent, Block... blocks) {
50 | this(separator, indent, Arrays.asList(blocks));
51 | }
52 |
53 | private Block breakBlock(int i, Block block) {
54 | if (alwaysSeparate) {
55 | block = new LineBlock(s(i), block);
56 | }
57 | return new IndentBlock(indent, block);
58 | }
59 |
60 | private Block l(int i) {
61 | return _blocks.get(i - 1);
62 | }
63 |
64 | private Block s(int i) {
65 | if (i > separators.size()) {
66 | return separators.get(0);
67 | } else {
68 | return separators.get(i - 1);
69 | }
70 | }
71 |
72 | private Block lb(int start, int end) {
73 | var block = new LineBlock(l(start));
74 | for (int i = start + 1; i <= end; i++) {
75 | block.add(s(i - 1), l(i));
76 | }
77 | return block;
78 | }
79 |
80 | private Map bCache = new ConcurrentHashMap<>();
81 |
82 | private Block b(int index) {
83 | return bCache.computeIfAbsent(
84 | index,
85 | i -> {
86 | if (i == n) {
87 | return l(i);
88 | } else {
89 | var block = new ChoiceBlock(new StackBlock(l(i), breakBlock(i, b(i + 1))));
90 | for (int j = i + 1; j < n; j++) {
91 | block.add(new StackBlock(lb(i, j), breakBlock(j, b(j + 1))));
92 | }
93 | block.add(lb(i, n));
94 | return block;
95 | }
96 | });
97 | }
98 |
99 | @Override
100 | public void add(Block block) {
101 | throw new RuntimeException("WrapBlock cannot be changed after instantiation");
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/resources-filtered/META-INF/MANIFEST.MF:
--------------------------------------------------------------------------------
1 | Manifest-Version: 1.0
2 | Created-By: ${java.version} (${java.vendor})
3 | Implementation-Title: ${project.title}
4 | Implementation-Version: ${project.version}
5 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/resources/reference/AttackStepMax.java:
--------------------------------------------------------------------------------
1 | package core;
2 |
3 | import java.util.Set;
4 |
5 | public class AttackStepMax extends AttackStep {
6 | public AttackStepMax(String name) {
7 | super(name);
8 | }
9 |
10 | @Override
11 | public void updateTtc(AttackStep parent, double parentTtc, Set activeAttackSteps) {
12 | expectedParents.remove(parent);
13 | visitedParents.add(parent);
14 | if (expectedParents.isEmpty()) {
15 | if (parentTtc + localTtc() < ttc) {
16 | ttc = parentTtc + localTtc();
17 | activeAttackSteps.add(this);
18 | }
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/resources/reference/AttackStepMin.java:
--------------------------------------------------------------------------------
1 | package core;
2 |
3 | import java.util.Set;
4 |
5 | public class AttackStepMin extends AttackStep {
6 | public AttackStepMin(String name) {
7 | super(name);
8 | }
9 |
10 | @Override
11 | public void updateTtc(AttackStep parent, double parentTtc, Set activeAttackSteps) {
12 | expectedParents.remove(parent);
13 | visitedParents.add(parent);
14 | if (parentTtc + localTtc() < ttc) {
15 | ttc = parentTtc + localTtc();
16 | activeAttackSteps.add(this);
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/resources/reference/Defense.java:
--------------------------------------------------------------------------------
1 | package core;
2 |
3 | import java.util.HashSet;
4 | import java.util.Set;
5 |
6 | public class Defense {
7 | public AttackStep disable;
8 | public boolean defaultValue;
9 | public static Set allDefenses = new HashSet<>();
10 | String assetName = "Anonymous";
11 |
12 | public Defense(String name) {
13 | allDefenses.add(this);
14 | this.assetName = name;
15 | }
16 |
17 | public boolean isEnabled() {
18 | return defaultValue;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/resources/securicad/Attacker.java:
--------------------------------------------------------------------------------
1 | import com.foreseeti.corelib.AssociationManager;
2 | import com.foreseeti.corelib.DefaultValue;
3 | import com.foreseeti.corelib.FAnnotations.DisplayClass;
4 | import com.foreseeti.corelib.FAnnotations.TypeName;
5 | import com.foreseeti.corelib.FClass;
6 | import com.foreseeti.corelib.util.FProbSet;
7 | import com.foreseeti.simulator.AbstractAttacker;
8 | import com.foreseeti.simulator.AttackStep;
9 | import com.foreseeti.simulator.BaseLangLink;
10 | import com.foreseeti.simulator.Defense;
11 | import java.util.Set;
12 |
13 | @DisplayClass(supportCapexOpex = false, category = "Attacker")
14 | @TypeName(name = "Attacker")
15 | public class Attacker extends AbstractAttacker {
16 | public Attacker() {
17 | this(DefaultValue.False);
18 | }
19 |
20 | public Attacker(DefaultValue val) {
21 | firstSteps = new FProbSet<>();
22 | fillElementMap();
23 | }
24 |
25 | public Attacker(Attacker other) {
26 | super(other);
27 | firstSteps = new FProbSet<>();
28 | entryPoint = new EntryPoint(other.entryPoint);
29 | fillElementMap();
30 | }
31 |
32 | @Override
33 | public String getConnectionValidationErrors(
34 | String sourceFieldName, FClass target, String targetFieldName) {
35 | if (Attacker.class.isAssignableFrom(target.getClass())) {
36 | return "Attacker can not be connected to other Attackers";
37 | }
38 | return getConnectionValidationErrors(target.getClass());
39 | }
40 |
41 | @Override
42 | public void registerAssociations() {
43 | AssociationManager.addSupportedAssociationMultiple(
44 | this.getClass(),
45 | getName(1),
46 | AttackStep.class,
47 | 0,
48 | AssociationManager.NO_LIMIT,
49 | BaseLangLink.Attacker_AttackStep);
50 | }
51 |
52 | @Override
53 | public Set getAttackSteps() {
54 | return Set.of(entryPoint);
55 | }
56 |
57 | @Override
58 | public Set getDefenses() {
59 | return Set.of();
60 | }
61 |
62 | @TypeName(name = "EntryPoint")
63 | public class EntryPoint extends AbstractAttacker.EntryPoint {
64 | public EntryPoint() {}
65 |
66 | public EntryPoint(AbstractAttacker.EntryPoint other) {
67 | super(other);
68 | }
69 |
70 | @Override
71 | public FClass getContainerFClass() {
72 | return Attacker.this;
73 | }
74 |
75 | @Override
76 | public java.util.Set getAttackStepChildren() {
77 | return FClass.toSampleSet(((Attacker) getContainerFClass()).firstSteps, null);
78 | }
79 | }
80 |
81 | @Override
82 | public boolean areAssociationsPublic() {
83 | return false;
84 | }
85 |
86 | @Override
87 | public boolean areModelElementsPublic() {
88 | return false;
89 | }
90 |
91 | @Override
92 | public boolean isAttacker() {
93 | return true;
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/resources/securicad/mock/corelib/AbstractSample.java:
--------------------------------------------------------------------------------
1 | package com.foreseeti.corelib;
2 |
3 | public abstract class AbstractSample implements BaseSample {}
4 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/resources/securicad/mock/corelib/AssociationManager.java:
--------------------------------------------------------------------------------
1 | package com.foreseeti.corelib;
2 |
3 | public final class AssociationManager {
4 | public static final int NO_LIMIT = Integer.MAX_VALUE;
5 |
6 | public static void addSupportedAssociationMultiple(
7 | Class extends FClass> thisClass,
8 | String fieldName,
9 | Class extends FClass> type,
10 | int minSize,
11 | int maxSize,
12 | Link link) {}
13 |
14 | public static void addSupportedAssociationMultiple(
15 | Class extends FClass> thisClass,
16 | String fieldName,
17 | Class extends FClass> type,
18 | Link link) {}
19 | }
20 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/resources/securicad/mock/corelib/BaseSample.java:
--------------------------------------------------------------------------------
1 | package com.foreseeti.corelib;
2 |
3 | import java.util.List;
4 |
5 | public interface BaseSample {
6 | public List getAttackSteps();
7 | }
8 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/resources/securicad/mock/corelib/DefaultValue.java:
--------------------------------------------------------------------------------
1 | package com.foreseeti.corelib;
2 |
3 | public enum DefaultValue {
4 | True(true),
5 | False(false);
6 |
7 | private final boolean value;
8 |
9 | DefaultValue(boolean value) {
10 | this.value = value;
11 | }
12 |
13 | public boolean get() {
14 | return value;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/resources/securicad/mock/corelib/FAnnotations.java:
--------------------------------------------------------------------------------
1 | package com.foreseeti.corelib;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Repeatable;
5 | import java.lang.annotation.Retention;
6 | import java.lang.annotation.RetentionPolicy;
7 | import java.lang.annotation.Target;
8 |
9 | public class FAnnotations {
10 | @Retention(RetentionPolicy.RUNTIME)
11 | @Target(ElementType.FIELD)
12 | public @interface Association {
13 | public int index();
14 |
15 | public String name();
16 | }
17 |
18 | @Retention(RetentionPolicy.RUNTIME)
19 | @Target(ElementType.FIELD)
20 | public @interface Display {}
21 |
22 | @Retention(RetentionPolicy.RUNTIME)
23 | @Target(ElementType.TYPE)
24 | public @interface DisplayClass {
25 | public boolean supportCapexOpex() default true;
26 |
27 | public String category();
28 | }
29 |
30 | @Retention(RetentionPolicy.RUNTIME)
31 | @Target(ElementType.TYPE)
32 | public @interface TypeDescription {
33 | public String text();
34 | }
35 |
36 | @Retention(RetentionPolicy.RUNTIME)
37 | @Target(ElementType.TYPE)
38 | public @interface TypeName {
39 | public String name();
40 | }
41 |
42 | public enum Risk {
43 | Confidentiality,
44 | Integrity,
45 | Availability
46 | }
47 |
48 | @Retention(RetentionPolicy.RUNTIME)
49 | @Target(ElementType.TYPE)
50 | public @interface RiskType {
51 | public Risk[] type();
52 | }
53 |
54 | @Retention(RetentionPolicy.RUNTIME)
55 | @Target(ElementType.TYPE)
56 | public @interface MetaInfoList {
57 | public MetaInfo[] value();
58 | }
59 |
60 | @Retention(RetentionPolicy.RUNTIME)
61 | @Repeatable(MetaInfoList.class)
62 | public @interface MetaInfo {
63 | public String key();
64 |
65 | public String value();
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/resources/securicad/mock/corelib/FClass.java:
--------------------------------------------------------------------------------
1 | package com.foreseeti.corelib;
2 |
3 | import com.foreseeti.corelib.util.FProb;
4 | import com.foreseeti.corelib.util.FProbSet;
5 | import java.util.Set;
6 |
7 | public abstract class FClass {
8 | public FClass() {}
9 |
10 | public FClass(FClass other) {}
11 |
12 | public Set getTTCColoringElements() {
13 | return Set.of();
14 | }
15 |
16 | public static T toSample(FProb probObj, BaseSample sample) {
17 | return null;
18 | }
19 |
20 | public static Set toSampleSet(FProbSet probSet, BaseSample sample) {
21 | return Set.of();
22 | }
23 |
24 | protected String getName(int index) {
25 | return null;
26 | }
27 |
28 | protected static String getName(Class extends FClass> clazz, int index) {
29 | return null;
30 | }
31 |
32 | public String getDescription() {
33 | return "";
34 | }
35 |
36 | public String getConnectionValidationErrors(
37 | String sourceFieldName, FClass target, String targetFieldName) {
38 | return null;
39 | }
40 |
41 | public String getConnectionValidationErrors(Class extends FClass> type) {
42 | return null;
43 | }
44 |
45 | protected abstract void registerAssociations();
46 |
47 | public boolean areAssociationsPublic() {
48 | return true;
49 | }
50 |
51 | public boolean areModelElementsPublic() {
52 | return true;
53 | }
54 |
55 | public boolean isAttacker() {
56 | return false;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/resources/securicad/mock/corelib/Link.java:
--------------------------------------------------------------------------------
1 | package com.foreseeti.corelib;
2 |
3 | public interface Link {
4 | public String getName();
5 | }
6 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/resources/securicad/mock/corelib/ModelElement.java:
--------------------------------------------------------------------------------
1 | package com.foreseeti.corelib;
2 |
3 | import com.foreseeti.corelib.math.FDistribution;
4 |
5 | public interface ModelElement {
6 | public void setEvidenceDistribution(FDistribution> dist);
7 |
8 | public FClass getContainerFClass();
9 | }
10 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/resources/securicad/mock/corelib/math/FBernoulliDistribution.java:
--------------------------------------------------------------------------------
1 | package com.foreseeti.corelib.math;
2 |
3 | public class FBernoulliDistribution implements FDistribution {
4 | public static FBernoulliDistribution getDist(double p) {
5 | return new FBernoulliDistribution();
6 | }
7 |
8 | public Boolean sample() {
9 | return Boolean.FALSE;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/resources/securicad/mock/corelib/math/FBinomialDistribution.java:
--------------------------------------------------------------------------------
1 | package com.foreseeti.corelib.math;
2 |
3 | public class FBinomialDistribution implements FDistribution {
4 | public static FBinomialDistribution getDist(int trials, double p) {
5 | return new FBinomialDistribution();
6 | }
7 |
8 | public Integer sample() {
9 | return Integer.valueOf(0);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/resources/securicad/mock/corelib/math/FDistribution.java:
--------------------------------------------------------------------------------
1 | package com.foreseeti.corelib.math;
2 |
3 | public interface FDistribution {
4 | public T sample();
5 | }
6 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/resources/securicad/mock/corelib/math/FExponentialDistribution.java:
--------------------------------------------------------------------------------
1 | package com.foreseeti.corelib.math;
2 |
3 | public class FExponentialDistribution implements FDistribution {
4 | public static FDistribution getDist(double m) {
5 | return new FExponentialDistribution();
6 | }
7 |
8 | public Double sample() {
9 | return Double.valueOf(0.0);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/resources/securicad/mock/corelib/math/FGammaDistribution.java:
--------------------------------------------------------------------------------
1 | package com.foreseeti.corelib.math;
2 |
3 | public class FGammaDistribution implements FDistribution {
4 | public static FGammaDistribution getDist(double shape, double scale) {
5 | return new FGammaDistribution();
6 | }
7 |
8 | public Double sample() {
9 | return Double.valueOf(0.0);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/resources/securicad/mock/corelib/math/FLogNormalDistribution.java:
--------------------------------------------------------------------------------
1 | package com.foreseeti.corelib.math;
2 |
3 | public class FLogNormalDistribution implements FDistribution {
4 | public static FLogNormalDistribution getDist(double shape, double scale) {
5 | return new FLogNormalDistribution();
6 | }
7 |
8 | public Double sample() {
9 | return Double.valueOf(0.0);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/resources/securicad/mock/corelib/math/FMath.java:
--------------------------------------------------------------------------------
1 | package com.foreseeti.corelib.math;
2 |
3 | public abstract class FMath {
4 | public static FDistribution getBernoulliDist(double p) {
5 | return FBernoulliDistribution.getDist(p);
6 | }
7 |
8 | public static FDistribution getBinomialDist(int trials, double p) {
9 | return FBinomialDistribution.getDist(trials, p);
10 | }
11 |
12 | public static FDistribution getExponentialDist(double m) {
13 | return FExponentialDistribution.getDist(m);
14 | }
15 |
16 | public static FDistribution getGammaDist(double shape, double scale) {
17 | return FGammaDistribution.getDist(shape, scale);
18 | }
19 |
20 | public static FDistribution getLogNormalDist(double shape, double scale) {
21 | return FLogNormalDistribution.getDist(shape, scale);
22 | }
23 |
24 | public static FDistribution getParetoDist(double scale, double shape) {
25 | return FParetoDistribution.getDist(scale, shape);
26 | }
27 |
28 | public static FDistribution getTruncatedNormalDist(double m, double sd) {
29 | return FTruncatedNormalDistribution.getDist(m, sd);
30 | }
31 |
32 | public static FDistribution getUniformDist(double lower, double upper) {
33 | return FUniformDistribution.getDist(lower, upper);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/resources/securicad/mock/corelib/math/FParetoDistribution.java:
--------------------------------------------------------------------------------
1 | package com.foreseeti.corelib.math;
2 |
3 | public class FParetoDistribution implements FDistribution {
4 | public static FParetoDistribution getDist(double shape, double scale) {
5 | return new FParetoDistribution();
6 | }
7 |
8 | public Double sample() {
9 | return Double.valueOf(0.0);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/resources/securicad/mock/corelib/math/FTruncatedNormalDistribution.java:
--------------------------------------------------------------------------------
1 | package com.foreseeti.corelib.math;
2 |
3 | public class FTruncatedNormalDistribution implements FDistribution {
4 | public static FDistribution getDist(double m, double sd) {
5 | return new FTruncatedNormalDistribution();
6 | }
7 |
8 | public Double sample() {
9 | return Double.valueOf(0.0);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/resources/securicad/mock/corelib/math/FUniformDistribution.java:
--------------------------------------------------------------------------------
1 | package com.foreseeti.corelib.math;
2 |
3 | public class FUniformDistribution implements FDistribution {
4 | public static FUniformDistribution getDist(double lower, double upper) {
5 | return new FUniformDistribution();
6 | }
7 |
8 | public Double sample() {
9 | return Double.valueOf(0.0);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/resources/securicad/mock/corelib/util/FProb.java:
--------------------------------------------------------------------------------
1 | package com.foreseeti.corelib.util;
2 |
3 | import com.foreseeti.corelib.FClass;
4 |
5 | public class FProb {
6 | public T getNonSampled() {
7 | return null;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/resources/securicad/mock/corelib/util/FProbSet.java:
--------------------------------------------------------------------------------
1 | package com.foreseeti.corelib.util;
2 |
3 | import com.foreseeti.corelib.FClass;
4 | import java.util.HashSet;
5 | import java.util.Set;
6 |
7 | public class FProbSet extends HashSet> {
8 | public Set getNonSampled() {
9 | return null;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/resources/securicad/mock/simulator/AbstractAttacker.java:
--------------------------------------------------------------------------------
1 | package com.foreseeti.simulator;
2 |
3 | import com.foreseeti.corelib.DefaultValue;
4 | import com.foreseeti.corelib.FAnnotations.Association;
5 | import com.foreseeti.corelib.FAnnotations.Display;
6 | import com.foreseeti.corelib.FAnnotations.TypeName;
7 | import com.foreseeti.corelib.FClass;
8 | import com.foreseeti.corelib.util.FProbSet;
9 | import java.util.Set;
10 |
11 | public abstract class AbstractAttacker extends SingleParentAsset {
12 | @Association(index = 1, name = "firstSteps")
13 | public FProbSet firstSteps;
14 |
15 | @Display
16 | @Association(index = 2, name = "entryPoint")
17 | public EntryPoint entryPoint = new EntryPoint();
18 |
19 | public AbstractAttacker() {}
20 |
21 | public AbstractAttacker(DefaultValue val) {}
22 |
23 | public AbstractAttacker(AbstractAttacker other) {}
24 |
25 | @TypeName(name = "EntryPoint")
26 | public class EntryPoint extends AttackStepMin {
27 | public EntryPoint() {}
28 |
29 | public EntryPoint(EntryPoint other) {
30 | super(other);
31 | }
32 |
33 | @Override
34 | public FClass getContainerFClass() {
35 | return AbstractAttacker.this;
36 | }
37 |
38 | @Override
39 | public Set getAttackStepChildren() {
40 | return FClass.toSampleSet(((AbstractAttacker) getContainerFClass()).firstSteps, null);
41 | }
42 | }
43 |
44 | @Override
45 | public boolean areAssociationsPublic() {
46 | return false;
47 | }
48 |
49 | @Override
50 | public boolean areModelElementsPublic() {
51 | return false;
52 | }
53 |
54 | @Override
55 | public boolean isAttacker() {
56 | return true;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/resources/securicad/mock/simulator/Asset.java:
--------------------------------------------------------------------------------
1 | package com.foreseeti.simulator;
2 |
3 | import java.util.Set;
4 |
5 | public interface Asset {
6 | public abstract Set getAttackSteps();
7 |
8 | public abstract Set getDefenses();
9 |
10 | public void fillElementMap();
11 |
12 | public default void clearGraphCache() {}
13 | }
14 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/resources/securicad/mock/simulator/AttackStep.java:
--------------------------------------------------------------------------------
1 | package com.foreseeti.simulator;
2 |
3 | import com.foreseeti.corelib.BaseSample;
4 | import com.foreseeti.corelib.FAnnotations.TypeName;
5 | import com.foreseeti.corelib.FClass;
6 | import com.foreseeti.corelib.ModelElement;
7 | import com.foreseeti.corelib.math.FDistribution;
8 | import java.util.Set;
9 |
10 | @TypeName(name = "AttackStep")
11 | public abstract class AttackStep extends FClass implements ModelElement {
12 | public AttackStep() {}
13 |
14 | public AttackStep(AttackStep other) {}
15 |
16 | @Override
17 | protected void registerAssociations() {}
18 |
19 | protected void setExpectedParents(ConcreteSample sample) {}
20 |
21 | public Set getAttackStepChildren() {
22 | return Set.of();
23 | }
24 |
25 | public double defaultLocalTtc(BaseSample sample, AttackStep caller) {
26 | return 0.0;
27 | }
28 |
29 | public void clearGraphCache() {}
30 |
31 | @Override
32 | public void setEvidenceDistribution(FDistribution> evidence) {}
33 |
34 | public Defense getInfluencingDefense() {
35 | return null;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/resources/securicad/mock/simulator/AttackStepMax.java:
--------------------------------------------------------------------------------
1 | package com.foreseeti.simulator;
2 |
3 | public abstract class AttackStepMax extends AttackStep {
4 | public AttackStepMax() {
5 | super();
6 | }
7 |
8 | public AttackStepMax(AttackStepMax other) {
9 | super(other);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/resources/securicad/mock/simulator/AttackStepMin.java:
--------------------------------------------------------------------------------
1 | package com.foreseeti.simulator;
2 |
3 | public abstract class AttackStepMin extends AttackStep {
4 | public AttackStepMin() {
5 | super();
6 | }
7 |
8 | public AttackStepMin(AttackStepMin other) {
9 | super(other);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/resources/securicad/mock/simulator/BaseLangLink.java:
--------------------------------------------------------------------------------
1 | package com.foreseeti.simulator;
2 |
3 | import com.foreseeti.corelib.Link;
4 |
5 | public enum BaseLangLink implements Link {
6 | Attacker_AttackStep("Attacks");
7 |
8 | protected final String name;
9 |
10 | BaseLangLink(String name) {
11 | this.name = name;
12 | }
13 |
14 | @Override
15 | public String getName() {
16 | return name;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/resources/securicad/mock/simulator/ConcreteSample.java:
--------------------------------------------------------------------------------
1 | package com.foreseeti.simulator;
2 |
3 | import com.foreseeti.corelib.AbstractSample;
4 | import com.foreseeti.corelib.ModelElement;
5 | import java.util.List;
6 |
7 | public class ConcreteSample extends AbstractSample {
8 | public void addExpectedParent(AttackStep as, AttackStep expectedParent) {}
9 |
10 | @Override
11 | public List getAttackSteps() {
12 | return List.of();
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/resources/securicad/mock/simulator/Defense.java:
--------------------------------------------------------------------------------
1 | package com.foreseeti.simulator;
2 |
3 | import com.foreseeti.corelib.DefaultValue;
4 | import com.foreseeti.corelib.ModelElement;
5 | import com.foreseeti.corelib.math.FDistribution;
6 |
7 | public abstract class Defense implements ModelElement {
8 | public AttackStep disable;
9 |
10 | public Defense() {}
11 |
12 | public Defense(Boolean b) {}
13 |
14 | public Defense(DefaultValue val) {}
15 |
16 | public Defense(Defense other) {}
17 |
18 | public boolean isEnabled(ConcreteSample sample) {
19 | return false;
20 | }
21 |
22 | @Override
23 | public void setEvidenceDistribution(FDistribution> evidence) {}
24 | }
25 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/resources/securicad/mock/simulator/MultiParentAsset.java:
--------------------------------------------------------------------------------
1 | package com.foreseeti.simulator;
2 |
3 | import com.foreseeti.corelib.FClass;
4 |
5 | public abstract class MultiParentAsset extends FClass implements Asset {
6 | public MultiParentAsset() {
7 | super();
8 | }
9 |
10 | public MultiParentAsset(MultiParentAsset other) {
11 | super(other);
12 | }
13 |
14 | @Override
15 | public void fillElementMap() {}
16 | }
17 |
--------------------------------------------------------------------------------
/malcompiler-lib/src/main/resources/securicad/mock/simulator/SingleParentAsset.java:
--------------------------------------------------------------------------------
1 | package com.foreseeti.simulator;
2 |
3 | import com.foreseeti.corelib.FClass;
4 |
5 | public abstract class SingleParentAsset extends FClass implements Asset {
6 | public SingleParentAsset() {
7 | super();
8 | }
9 |
10 | public SingleParentAsset(SingleParentAsset other) {
11 | super(other);
12 | }
13 |
14 | @Override
15 | public void fillElementMap() {}
16 | }
17 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/java/org/mal_lang/compiler/test/formatter/TestFormatter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 Foreseeti AB
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.mal_lang.compiler.test.formatter;
17 |
18 | import static org.junit.jupiter.api.Assertions.assertEquals;
19 | import static org.junit.jupiter.api.Assertions.fail;
20 |
21 | import java.io.File;
22 | import java.io.IOException;
23 | import org.junit.jupiter.api.Test;
24 | import org.mal_lang.compiler.lib.CompilerException;
25 | import org.mal_lang.compiler.test.MalTest;
26 | import org.mal_lang.formatter.Formatter;
27 |
28 | public class TestFormatter extends MalTest {
29 |
30 | public void outputEqual(String unformattedPath, String formattedPath, int lineWidth) {
31 | String formattedString = assertReadFileClassPath(formattedPath);
32 | File input = assertGetFileClassPath(unformattedPath);
33 | try {
34 | String formatted = new String(Formatter.format(input, lineWidth, false));
35 | if (!formattedString.equals(formatted)) {
36 | fail(String.format("%s formatted does not equal %s", unformattedPath, formattedPath));
37 | }
38 | } catch (IOException | CompilerException e) {
39 | fail(e.getMessage());
40 | }
41 | }
42 |
43 | public void formats(String path) {
44 | File file = assertGetFileClassPath(path);
45 | try {
46 | Formatter.format(file, 100, false);
47 | } catch (IOException | CompilerException e) {
48 | fail(e.getMessage());
49 | }
50 | }
51 |
52 | @Test
53 | public void testInvalid() {
54 | File file = assertGetFileClassPath("parser/bad-asset1.mal");
55 | try {
56 | Formatter.format(file, 100, false);
57 | fail("bad-asset1.mal should not compile");
58 | } catch (IOException | CompilerException e) {
59 | assertEquals("There were syntax errors", e.getMessage());
60 | }
61 | assertEmptyOut();
62 | String[] expected = {
63 | "[PARSER ERROR] expected 'abstract', 'asset', or '}', found identifier",
64 | "[FORMATTER ERROR] Code to be formatted must be syntactically valid",
65 | ""
66 | };
67 | assertErrLines(expected);
68 | }
69 |
70 | @Test
71 | public void testComments() {
72 | outputEqual("formatter/comments.mal", "formatter/comments.ans", 100);
73 | }
74 |
75 | @Test
76 | public void testReadable() {
77 | outputEqual("formatter/readable.mal", "formatter/readable.ans", 100);
78 | }
79 |
80 | @Test
81 | public void testOneline() {
82 | outputEqual("formatter/oneline.mal", "formatter/oneline.ans", 100);
83 | }
84 |
85 | @Test
86 | public void testEscape() {
87 | outputEqual("formatter/escape.mal", "formatter/escape.ans", 100);
88 | }
89 |
90 | @Test
91 | public void testMargin50() {
92 | outputEqual("formatter/margin.mal", "formatter/margin50.ans", 50);
93 | }
94 |
95 | @Test
96 | public void testMargin30() {
97 | outputEqual("formatter/margin.mal", "formatter/margin30.ans", 30);
98 | }
99 |
100 | @Test
101 | public void testComplexFormat() {
102 | formats("analyzer/complex.mal");
103 | }
104 |
105 | @Test
106 | public void testDistributionsFormat() {
107 | formats("analyzer/distributions.mal");
108 | }
109 |
110 | @Test
111 | public void testBledFormat() {
112 | formats("bled/bled.mal");
113 | }
114 |
115 | @Test
116 | public void testAllFeatures() {
117 | formats("all-features/core.mal");
118 | }
119 |
120 | @Test
121 | public void testAttackStepSet() {
122 | formats("generator/attack-step-set.mal");
123 | }
124 |
125 | @Test
126 | public void testDebugStep() {
127 | formats("generator/debug-step.mal");
128 | }
129 |
130 | @Test
131 | public void testDist() {
132 | formats("generator/dist.mal");
133 | }
134 |
135 | @Test
136 | public void testNaming() {
137 | formats("generator/naming.mal");
138 | }
139 |
140 | @Test
141 | public void testNested() {
142 | formats("generator/nested.mal");
143 | }
144 |
145 | @Test
146 | public void testSteps() {
147 | formats("generator/steps.mal");
148 | }
149 |
150 | @Test
151 | public void testVariable() {
152 | formats("generator/variable.mal");
153 | }
154 |
155 | @Test
156 | public void testReverse() {
157 | formats("lang-converter/reverse.mal");
158 | }
159 |
160 | @Test
161 | public void testVehicleLang() {
162 | formats("vehiclelang/vehicleLang.mal");
163 | // formats("vehiclelang/vehicleLangEncryption.mal"); // Doesn't compile
164 | formats("vehiclelang/vehicleLangEthernet.mal");
165 | formats("vehiclelang/vehicleLangPublicInterfaces.mal");
166 | }
167 | }
168 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/java/org/mal_lang/compiler/test/lib/AssertToken.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019-2022 Foreseeti AB
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.mal_lang.compiler.test.lib;
17 |
18 | import static org.junit.jupiter.api.Assertions.assertEquals;
19 | import static org.junit.jupiter.api.Assertions.fail;
20 | import static org.mal_lang.compiler.test.MalTest.getFileClassPath;
21 |
22 | import java.io.IOException;
23 | import java.net.URISyntaxException;
24 | import org.mal_lang.compiler.lib.CompilerException;
25 | import org.mal_lang.compiler.lib.Lexer;
26 | import org.mal_lang.compiler.lib.Token;
27 | import org.mal_lang.compiler.lib.TokenType;
28 |
29 | public final class AssertToken {
30 | // Prevent instantiation
31 | private AssertToken() {}
32 |
33 | public static Lexer getLexerClassPath(String filename) throws IOException, URISyntaxException {
34 | return new Lexer(getFileClassPath(filename));
35 | }
36 |
37 | public static Lexer assertGetLexerClassPath(String filename) {
38 | try {
39 | return getLexerClassPath(filename);
40 | } catch (IOException | URISyntaxException e) {
41 | fail(e.getMessage());
42 | }
43 | throw new RuntimeException("This should be unreachable");
44 | }
45 |
46 | public static void assertTokenTypes(TokenType[] tokenTypes, String filename) {
47 | try {
48 | var lex = assertGetLexerClassPath(filename);
49 | for (var tokenType : tokenTypes) {
50 | assertEquals(tokenType, lex.next().type);
51 | }
52 | } catch (CompilerException e) {
53 | fail(e.getMessage());
54 | }
55 | }
56 |
57 | private static void assertToken(Token expected, Token actual) {
58 | assertEquals(expected.filename, actual.filename, actual.posString());
59 | assertEquals(expected.line, actual.line, actual.posString());
60 | assertEquals(expected.col, actual.col, actual.posString());
61 | assertEquals(expected.type, actual.type, actual.posString());
62 | assertEquals(expected.stringValue, actual.stringValue, actual.posString());
63 | assertEquals(expected.doubleValue, actual.doubleValue, actual.posString());
64 | assertEquals(expected.intValue, actual.intValue, actual.posString());
65 | }
66 |
67 | public static void assertTokens(Token[] tokens, String filename) {
68 | try {
69 | var lex = assertGetLexerClassPath(filename);
70 | for (var token : tokens) {
71 | assertToken(token, lex.next());
72 | }
73 | } catch (CompilerException e) {
74 | fail(e.getMessage());
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/java/org/mal_lang/compiler/test/lib/d3/TestD3Generator.java:
--------------------------------------------------------------------------------
1 | package org.mal_lang.compiler.test.lib.d3;
2 |
3 | import static org.junit.jupiter.api.Assertions.fail;
4 | import static org.mal_lang.compiler.test.lib.AssertLang.assertGetLangClassPath;
5 |
6 | import java.io.FileNotFoundException;
7 | import java.util.Map;
8 | import org.junit.jupiter.api.Test;
9 | import org.mal_lang.compiler.lib.CompilerException;
10 | import org.mal_lang.compiler.test.MalTest;
11 |
12 | public class TestD3Generator extends MalTest {
13 |
14 | public void generate(String resource) {
15 | var path = getNewTmpDir("d3");
16 | var lang = assertGetLangClassPath(resource);
17 | try {
18 | org.mal_lang.compiler.lib.d3.Generator.generate(lang, Map.of("path", path));
19 | } catch (FileNotFoundException | CompilerException e) {
20 | fail(e);
21 | }
22 | }
23 |
24 | @Test
25 | public void testBled() {
26 | generate("bled/bled.mal");
27 | }
28 |
29 | @Test
30 | public void testVehicleLang() {
31 | generate("vehiclelang/vehicleLang.mal");
32 | }
33 |
34 | @Test
35 | public void testComplex() {
36 | generate("analyzer/complex.mal");
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/java/org/mal_lang/compiler/test/vehiclelang/AdvancedVulnerabilityTest.java:
--------------------------------------------------------------------------------
1 | package org.mal_lang.compiler.test.vehiclelang;
2 |
3 | import core.Asset;
4 | import core.AttackStep;
5 | import core.Attacker;
6 | import core.Defense;
7 | import org.junit.jupiter.api.AfterEach;
8 | import org.junit.jupiter.api.Test;
9 | import org.mal_lang.compiler.test.MalTest;
10 | import vehicle.Account;
11 | import vehicle.CANNetwork;
12 | import vehicle.ConnectionOrientedDataflow;
13 | import vehicle.Data;
14 | import vehicle.ECU;
15 | import vehicle.SensorOrActuator;
16 | import vehicle.UDSService;
17 | import vehicle.Vulnerability;
18 |
19 | public class AdvancedVulnerabilityTest extends MalTest {
20 |
21 | @Test
22 | public void testVulnerabilityOnUDS() {
23 | // Testing vulnerability on an UDS service.
24 | /*
25 | |- PrivEsc(Vulnerability)
26 | Root(account)---|
27 | | _______| Data
28 | | | |
29 | UDS_Service <---> Ecu#1 <---> vNet(CAN)
30 | | | |
31 | | | |---> Ecu#2 <---> Sensor/Actuator
32 | UDS_Dataflow <-----------------
33 | */
34 | // TARGET: udsService.access & dataflow.respond ENTRY_POINT: UDS_Service.connect
35 | System.out.println(
36 | "### "
37 | + Thread.currentThread()
38 | .getStackTrace()[1]
39 | .getMethodName()); // Printing the test's name
40 |
41 | ECU ecu1 =
42 | new ECU("ECU#1", true, true); // Enabled operation mode and message confliction protection
43 | ECU ecu2 = new ECU("ECU#2", true, true);
44 | SensorOrActuator phyMachine = new SensorOrActuator("Sensor/Actuator");
45 | CANNetwork vNet = new CANNetwork("CAN");
46 | ConnectionOrientedDataflow dataflow = new ConnectionOrientedDataflow("UDS_Dataflow");
47 | UDSService udsService = new UDSService("UDS_Service");
48 | Account root = new Account("Root");
49 | Vulnerability privEsc = new Vulnerability("PrivEsc");
50 | Data data = new Data("Data");
51 |
52 | ecu1.addExecutees(udsService);
53 | ecu1.addVehiclenetworks(vNet);
54 | ecu1.addData(data);
55 | ecu2.addVehiclenetworks(vNet);
56 | ecu2.addSensorsOrActuators(phyMachine);
57 | udsService.addAccount(root);
58 | udsService.addConnectionVulnerabilities(privEsc);
59 | privEsc.addPrivileges(root);
60 | udsService.addDataflows(dataflow);
61 | vNet.addDataflows(dataflow);
62 |
63 | Attacker attacker = new Attacker();
64 | attacker.addAttackPoint(udsService.connect);
65 | attacker.attack();
66 |
67 | privEsc.exploit.assertCompromisedWithEffort();
68 | root.authenticate.assertUncompromised();
69 | root.compromise.assertCompromisedWithEffort();
70 | udsService.access.assertCompromisedWithEffort();
71 | ecu1.connect.assertCompromisedWithEffort();
72 | data.requestAccess.assertCompromisedWithEffort();
73 | dataflow.respond.assertCompromisedWithEffort();
74 |
75 | vNet.accessNetworkLayer.assertUncompromised();
76 | ecu2.connect.assertUncompromised();
77 | }
78 |
79 | @AfterEach
80 | public void deleteModel() {
81 | Asset.allAssets.clear();
82 | AttackStep.allAttackSteps.clear();
83 | Defense.allDefenses.clear();
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/java/org/mal_lang/compiler/test/vehiclelang/CoreDataTest.java:
--------------------------------------------------------------------------------
1 | package org.mal_lang.compiler.test.vehiclelang;
2 |
3 | import core.Asset;
4 | import core.AttackStep;
5 | import core.Attacker;
6 | import core.Defense;
7 | import org.junit.jupiter.api.AfterEach;
8 | import org.junit.jupiter.api.Test;
9 | import org.mal_lang.compiler.test.MalTest;
10 | import vehicle.ConnectionOrientedDataflow;
11 | import vehicle.ConnectionlessDataflow;
12 | import vehicle.Data;
13 |
14 | public class CoreDataTest extends MalTest {
15 |
16 | @Test
17 | public void testDataAccess() {
18 | // Testing data access after partial authentication.
19 | Data data = new Data("Data");
20 |
21 | Attacker attacker = new Attacker();
22 | attacker.addAttackPoint(data.requestAccess);
23 | attacker.addAttackPoint(data.anyAccountRead);
24 |
25 | attacker.attack();
26 |
27 | data.read.assertCompromisedInstantaneously();
28 | data.write.assertUncompromised();
29 | data.delete.assertUncompromised();
30 | }
31 |
32 | @Test
33 | public void testDataflow1DataAccess() {
34 | // Testing connection oriented dataflow's data access after Man-in-the-Middle attack.
35 | Data data = new Data("Data");
36 | ConnectionOrientedDataflow dataflow = new ConnectionOrientedDataflow("Dataflow");
37 |
38 | dataflow.addData(data);
39 |
40 | Attacker attacker = new Attacker();
41 | attacker.addAttackPoint(dataflow.manInTheMiddle);
42 |
43 | attacker.attack();
44 |
45 | data.read.assertCompromisedInstantaneously();
46 | data.write.assertCompromisedInstantaneously();
47 | data.delete.assertCompromisedInstantaneously();
48 | }
49 |
50 | @Test
51 | public void testDataflow2DataAccess() {
52 | // Testing connectionless dataflow's data access after Man-in-the-Middle attack.
53 | Data data = new Data("Data");
54 | ConnectionlessDataflow dataflow = new ConnectionlessDataflow("Dataflow");
55 |
56 | dataflow.addData(data);
57 |
58 | Attacker attacker = new Attacker();
59 | attacker.addAttackPoint(dataflow.manInTheMiddle);
60 |
61 | attacker.attack();
62 |
63 | data.read.assertCompromisedInstantaneously();
64 | data.write.assertCompromisedInstantaneously();
65 | data.delete.assertCompromisedInstantaneously();
66 | }
67 |
68 | @AfterEach
69 | public void deleteModel() {
70 | Asset.allAssets.clear();
71 | AttackStep.allAttackSteps.clear();
72 | Defense.allDefenses.clear();
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/java/org/mal_lang/compiler/test/vehiclelang/CoreEcuTest.java:
--------------------------------------------------------------------------------
1 | package org.mal_lang.compiler.test.vehiclelang;
2 |
3 | import core.Asset;
4 | import core.AttackStep;
5 | import core.Attacker;
6 | import core.Defense;
7 | import org.junit.jupiter.api.AfterEach;
8 | import org.junit.jupiter.api.Test;
9 | import org.mal_lang.compiler.test.MalTest;
10 | import vehicle.ECU;
11 |
12 | public class CoreEcuTest extends MalTest {
13 |
14 | @Test
15 | public void testConnectEcuAttacks() {
16 | // Testing ECU attacks on connect with all defenses enabled.
17 | ECU ecu =
18 | new ECU("ECU", true, true); // Enabled operation mode and message confliction protection.
19 |
20 | Attacker attacker = new Attacker();
21 | attacker.addAttackPoint(ecu.connect);
22 | attacker.attack();
23 |
24 | ecu.changeOperationMode.assertUncompromised();
25 | ecu.access.assertUncompromised();
26 | ecu.gainLINAccessFromCAN.assertUncompromised();
27 | }
28 |
29 | @Test
30 | public void testConnectEcuAttacks2() {
31 | // Testing ECU attacks on connect with some defenses enabled.
32 | ECU ecu = new ECU("ECU2", false, true); // Enabled only message confliction protection.
33 |
34 | Attacker attacker = new Attacker();
35 | attacker.addAttackPoint(ecu.connect);
36 | attacker.attack();
37 |
38 | ecu.attemptChangeOperationMode.assertCompromisedWithEffort();
39 | ecu.changeOperationMode.assertUncompromised();
40 | ecu.access.assertUncompromised();
41 | ecu.gainLINAccessFromCAN.assertUncompromised();
42 | }
43 |
44 | @Test
45 | public void testAccessEcuAttacks() {
46 | // Testing ECU attacks on access with all defenses enabled.
47 | ECU ecu =
48 | new ECU("ECU3", true, true); // Enabled operation mode and message confliction protection.
49 |
50 | Attacker attacker = new Attacker();
51 | attacker.addAttackPoint(ecu.access);
52 | attacker.attack();
53 |
54 | ecu.changeOperationMode.assertUncompromised();
55 | ecu.gainLINAccessFromCAN.assertCompromisedInstantaneously();
56 | }
57 |
58 | @Test
59 | public void testAccessEcuAttacks2() {
60 | // Testing ECU attacks on access with some defenses enabled.
61 | ECU ecu = new ECU("ECU4", false, true); // Enabled only message confliction protection.
62 |
63 | Attacker attacker = new Attacker();
64 | attacker.addAttackPoint(ecu.access);
65 | attacker.attack();
66 |
67 | ecu.changeOperationMode.assertCompromisedInstantaneously();
68 | ecu.attemptChangeOperationMode.assertUncompromised();
69 | ecu.bypassMessageConfliction.assertCompromisedInstantaneously();
70 | ecu.gainLINAccessFromCAN.assertCompromisedInstantaneously();
71 | }
72 |
73 | @AfterEach
74 | public void deleteModel() {
75 | Asset.allAssets.clear();
76 | AttackStep.allAttackSteps.clear();
77 | Defense.allDefenses.clear();
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/java/org/mal_lang/compiler/test/vehiclelang/CoreMachineTest.java:
--------------------------------------------------------------------------------
1 | package org.mal_lang.compiler.test.vehiclelang;
2 |
3 | import core.Asset;
4 | import core.AttackStep;
5 | import core.Attacker;
6 | import core.Defense;
7 | import org.junit.jupiter.api.AfterEach;
8 | import org.junit.jupiter.api.Test;
9 | import org.mal_lang.compiler.test.MalTest;
10 | import vehicle.Account;
11 | import vehicle.Data;
12 | import vehicle.Machine;
13 | import vehicle.Software;
14 |
15 | public class CoreMachineTest extends MalTest {
16 |
17 | @Test
18 | public void testMachineAccess() {
19 | // Testing proper access to a machine.
20 | Machine machine = new Machine();
21 |
22 | Attacker attacker = new Attacker();
23 | attacker.addAttackPoint(machine.connect);
24 | attacker.addAttackPoint(machine.authenticate);
25 |
26 | attacker.attack();
27 |
28 | machine.access.assertCompromisedInstantaneously();
29 | machine.denialOfService.assertCompromisedInstantaneously();
30 | }
31 |
32 | @Test
33 | public void testBypassMachineAccess() {
34 | // Testing bypass access control to a machine.
35 | Machine machine = new Machine();
36 |
37 | Attacker attacker = new Attacker();
38 | attacker.addAttackPoint(machine.bypassAccessControl);
39 |
40 | attacker.attack();
41 |
42 | machine.access.assertCompromisedInstantaneously();
43 | machine.denialOfService.assertCompromisedInstantaneously();
44 | }
45 |
46 | @Test
47 | public void testSoftwareHostToGuest() {
48 | // Testing compromise account on a machine.
49 | /*
50 | Account <---> Machine <---> Software2
51 | | |
52 | Software1 <------
53 | */
54 | // TARGET: softwares ENTRY_POINT: account.compromise and machine.connect
55 | Machine machine = new Machine("Machine");
56 | Software software1 = new Software("Software1");
57 | Software software2 = new Software("Software2");
58 | Account account = new Account("Account");
59 |
60 | machine.addAccount(account);
61 | software1.addExecutor(machine);
62 | software2.addExecutor(machine);
63 | software1.addAccount(account);
64 |
65 | Attacker attacker = new Attacker();
66 | attacker.addAttackPoint(machine.connect);
67 | attacker.addAttackPoint(account.compromise);
68 |
69 | attacker.attack();
70 |
71 | machine.access.assertCompromisedInstantaneously();
72 | software1.connect.assertCompromisedInstantaneously();
73 | software1.access.assertCompromisedInstantaneously();
74 | software2.connect.assertCompromisedInstantaneously();
75 | software2.access.assertUncompromised();
76 | }
77 |
78 | @Test
79 | public void testSoftwareGuestToHost() {
80 | // Testing machine access from software.
81 | Machine machine = new Machine("Machine12");
82 | Software software = new Software("Software123");
83 |
84 | software.addExecutor(machine);
85 |
86 | Attacker attacker = new Attacker();
87 | attacker.addAttackPoint(software.connect);
88 | attacker.addAttackPoint(software.authenticate);
89 |
90 | attacker.attack();
91 |
92 | software.access.assertCompromisedInstantaneously();
93 | machine.connect.assertCompromisedInstantaneously();
94 | machine.access.assertUncompromised();
95 | }
96 |
97 | @Test
98 | public void testMachineAccountDataRWD() {
99 | // Testing data read access from account compromise.
100 | /*
101 | Account <---> Machine
102 | | |
103 | Data(read) <----
104 | */
105 | // TARGET: Data.read ENTRY_POINT: account.compromise and machine.connect
106 | Machine machine = new Machine("Machine");
107 | Account account = new Account("Account");
108 | Data data = new Data("Data");
109 |
110 | machine.addAccount(account);
111 | machine.addData(data);
112 | account.addReadData(data);
113 |
114 | Attacker attacker = new Attacker();
115 | attacker.addAttackPoint(machine.connect);
116 | attacker.addAttackPoint(account.compromise);
117 |
118 | attacker.attack();
119 |
120 | data.requestAccess.assertCompromisedInstantaneously();
121 | data.anyAccountRead.assertCompromisedInstantaneously();
122 | data.read.assertCompromisedInstantaneously();
123 | data.anyAccountWrite.assertUncompromised();
124 | data.write.assertUncompromised();
125 | data.anyAccountDelete.assertUncompromised();
126 | data.delete.assertUncompromised();
127 |
128 | machine.authenticate.assertCompromisedInstantaneously();
129 | }
130 |
131 | @AfterEach
132 | public void deleteModel() {
133 | Asset.allAssets.clear();
134 | AttackStep.allAttackSteps.clear();
135 | Defense.allDefenses.clear();
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/java/org/mal_lang/compiler/test/vehiclelang/CoreVulnerabilityTest.java:
--------------------------------------------------------------------------------
1 | package org.mal_lang.compiler.test.vehiclelang;
2 |
3 | import core.Asset;
4 | import core.AttackStep;
5 | import core.Attacker;
6 | import core.Defense;
7 | import org.junit.jupiter.api.AfterEach;
8 | import org.junit.jupiter.api.Test;
9 | import org.mal_lang.compiler.test.MalTest;
10 | import vehicle.Account;
11 | import vehicle.Machine;
12 | import vehicle.Software;
13 | import vehicle.Vulnerability;
14 |
15 | public class CoreVulnerabilityTest extends MalTest {
16 |
17 | @Test
18 | public void testVulnerability() {
19 |
20 | Software userland = new Software("Userland");
21 | Software kernel = new Software("Kernel");
22 | Account root = new Account("RootAccount");
23 | Account user = new Account("UserAccount");
24 | Vulnerability privEsc = new Vulnerability("PrivEsc");
25 |
26 | kernel.addAccount(root);
27 | userland.addAccount(user);
28 | userland.addExecutor(kernel);
29 | userland.addAccessVulnerabilities(privEsc);
30 | privEsc.addPrivileges(root);
31 |
32 | Attacker attacker = new Attacker();
33 | attacker.addAttackPoint(userland.connect);
34 | attacker.addAttackPoint(user.compromise);
35 |
36 | attacker.attack();
37 |
38 | userland.access.assertCompromisedInstantaneously();
39 | kernel.connect.assertCompromisedInstantaneously();
40 | privEsc.exploit.assertCompromisedWithEffort();
41 | root.compromise.assertCompromisedWithEffort();
42 | kernel.access.assertCompromisedWithEffort();
43 | }
44 |
45 | @Test
46 | public void testSoftware() {
47 | Machine machine = new Machine("Machine");
48 | Software software = new Software("Software");
49 | Vulnerability vulnerability = new Vulnerability("Vulnerability");
50 |
51 | machine.addExecutees(software);
52 | software.addConnectionVulnerabilities(vulnerability);
53 |
54 | Attacker attacker = new Attacker();
55 | attacker.addAttackPoint(
56 | machine.access); // Changed it to access because cannot connect to software without
57 | // machine.access
58 |
59 | attacker.attack();
60 |
61 | vulnerability.exploit.assertCompromisedWithEffort();
62 | }
63 |
64 | @AfterEach
65 | public void deleteModel() {
66 | Asset.allAssets.clear();
67 | AttackStep.allAttackSteps.clear();
68 | Defense.allDefenses.clear();
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/java/org/mal_lang/compiler/test/vehiclelang/InfotainmentTest.java:
--------------------------------------------------------------------------------
1 | package org.mal_lang.compiler.test.vehiclelang;
2 |
3 | import core.Asset;
4 | import core.AttackStep;
5 | import core.Attacker;
6 | import core.Defense;
7 | import org.junit.jupiter.api.AfterEach;
8 | import org.junit.jupiter.api.Test;
9 | import org.mal_lang.compiler.test.MalTest;
10 | import vehicle.Account;
11 | import vehicle.InfotainmentSystem;
12 | import vehicle.NetworkAccessService;
13 | import vehicle.VehicleNetwork;
14 |
15 | public class InfotainmentTest extends MalTest {
16 |
17 | @Test
18 | public void NetworkAccessFromInfotainmentTest() {
19 | /*
20 | This test case models an attack from the infotainment system which has a network access service (which might be stopped by default)
21 |
22 | ---> Account
23 | | |
24 | | Infotainment <---> VehicleNetwork
25 | | |
26 | NetworkAccessService
27 |
28 | */
29 | System.out.println("### " + Thread.currentThread().getStackTrace()[1].getMethodName());
30 | // Start of test
31 | InfotainmentSystem infosys = new InfotainmentSystem("InfoSys");
32 | VehicleNetwork vNet = new VehicleNetwork("vNet");
33 | NetworkAccessService netSrv = new NetworkAccessService("NetService");
34 | Account account = new Account("Account");
35 |
36 | infosys.addConnectedNetworks(vNet);
37 | infosys.addExecutees(netSrv);
38 | infosys.addAccount(account);
39 | netSrv.addAccount(account);
40 |
41 | Attacker atk = new Attacker();
42 | atk.addAttackPoint(infosys.connect);
43 | atk.addAttackPoint(account.compromise);
44 | atk.attack();
45 |
46 | infosys.access.assertCompromisedInstantaneously();
47 | netSrv.access.assertCompromisedInstantaneously();
48 | infosys.gainNetworkAccess.assertCompromisedInstantaneously();
49 |
50 | vNet.accessNetworkLayer.assertCompromisedInstantaneously();
51 | }
52 |
53 | @Test
54 | public void EngineerNetworkAccessFromInfotainmentTest() {
55 | /*
56 | This test case models an attack from the infotainment system which has not a network access service so the attacker must engineer it!
57 |
58 | Account
59 | |
60 | Infotainment <---> VehicleNetwork
61 |
62 | */
63 | System.out.println("### " + Thread.currentThread().getStackTrace()[1].getMethodName());
64 | // Start of test
65 | InfotainmentSystem infosys = new InfotainmentSystem("InfoSys");
66 | VehicleNetwork vNet = new VehicleNetwork("vNet");
67 | Account account = new Account("Account");
68 |
69 | infosys.addConnectedNetworks(vNet);
70 | infosys.addAccount(account);
71 |
72 | Attacker atk = new Attacker();
73 | atk.addAttackPoint(infosys.connect);
74 | atk.addAttackPoint(account.compromise);
75 | atk.attack();
76 |
77 | infosys.access.assertCompromisedInstantaneously();
78 | infosys.gainNetworkAccess.assertUncompromised();
79 | infosys.engineerNetworkAccess.assertCompromisedWithEffort();
80 | vNet.accessNetworkLayer.assertCompromisedWithEffort();
81 | }
82 |
83 | @AfterEach
84 | public void deleteModel() {
85 | Asset.allAssets.clear();
86 | AttackStep.allAttackSteps.clear();
87 | Defense.allDefenses.clear();
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/java/org/mal_lang/compiler/test/vehiclelang/expertSessionTest.java:
--------------------------------------------------------------------------------
1 | package org.mal_lang.compiler.test.vehiclelang;
2 |
3 | import core.Asset;
4 | import core.AttackStep;
5 | import core.Attacker;
6 | import core.Defense;
7 | import org.junit.jupiter.api.AfterEach;
8 | import org.junit.jupiter.api.Test;
9 | import org.mal_lang.compiler.test.MalTest;
10 | import vehicle.CANNetwork;
11 | import vehicle.ECU;
12 | import vehicle.EthernetNetwork;
13 | import vehicle.GatewayECU;
14 | import vehicle.InfotainmentSystem;
15 | import vehicle.Machine;
16 |
17 | public class expertSessionTest extends MalTest {
18 |
19 | @Test
20 | public void Test1() {
21 | // This test case was created in the reviewing session with a domain expert
22 | /*
23 | WiFi, BT, USB
24 | |
25 | Speaker <---> Amp. <---> Ethernet <---> InfotainmentSys
26 | |
27 | CAN
28 | |
29 | GatewayECU
30 | |
31 | internalCAN
32 | -----------------
33 | | | |
34 | TMS BMS EMS
35 | */
36 | System.out.println("### " + Thread.currentThread().getStackTrace()[1].getMethodName());
37 | // Start of test
38 | boolean firewallStatus = true;
39 | InfotainmentSystem infosys = new InfotainmentSystem("infosys");
40 | GatewayECU gwECU = new GatewayECU("gwECU", firewallStatus, true, true);
41 | CANNetwork can = new CANNetwork("can");
42 | CANNetwork internalCan = new CANNetwork("internalCan");
43 | ECU tms = new ECU("TMS"); // Transmission
44 | ECU ems = new ECU("EMS"); // Engine
45 | ECU bms = new ECU("BMS"); // Braking
46 | EthernetNetwork ethernet = new EthernetNetwork("ethernet");
47 | Machine amp = new Machine("AMP");
48 |
49 | infosys.addConnectedNetworks(can);
50 | infosys.addConnectedNetworks(ethernet);
51 | gwECU.addTrafficVNetworks(can);
52 | gwECU.addTrafficVNetworks(internalCan);
53 | internalCan.addNetworkECUs(tms);
54 | internalCan.addNetworkECUs(ems);
55 | internalCan.addNetworkECUs(bms);
56 |
57 | Attacker atk = new Attacker();
58 | // atk.addAttackPoint(infosys.connect);
59 | atk.addAttackPoint(infosys.access);
60 | atk.attack();
61 |
62 | infosys.engineerNetworkAccess.assertCompromisedWithEffort();
63 | can.accessNetworkLayer.assertCompromisedWithEffort();
64 | internalCan.accessNetworkLayer.assertUncompromised();
65 | tms.access.assertUncompromised();
66 | ems.access.assertUncompromised();
67 | bms.access.assertUncompromised();
68 | }
69 |
70 | @AfterEach
71 | public void deleteModel() {
72 | Asset.allAssets.clear();
73 | AttackStep.allAttackSteps.clear();
74 | Defense.allDefenses.clear();
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/java/org/mal_lang/compiler/test/vehiclelang/newTest.java:
--------------------------------------------------------------------------------
1 | package org.mal_lang.compiler.test.vehiclelang;
2 |
3 | import core.Asset;
4 | import core.AttackStep;
5 | import core.Attacker;
6 | import core.Defense;
7 | import org.junit.jupiter.api.AfterEach;
8 | import org.junit.jupiter.api.Test;
9 | import org.mal_lang.compiler.test.MalTest;
10 | import vehicle.Account;
11 | import vehicle.ConnectionlessDataflow;
12 | import vehicle.ECU;
13 | import vehicle.Firmware;
14 | import vehicle.GatewayECU;
15 | import vehicle.IDPS;
16 | import vehicle.MessageID;
17 | import vehicle.SensorOrActuator;
18 | import vehicle.TransmitterService;
19 | import vehicle.VehicleNetwork;
20 |
21 | public class newTest extends MalTest {
22 |
23 | @Test
24 | public void acceleratorTest() {
25 | // This test case was created in the reviewing session with another domain specific language
26 | // developer
27 | // Update 2018-12-05: Changed to assert the network as uncompromised because of modification to
28 | // maliciousFirmwareModification parents
29 | /*
30 | ---------------------------------
31 | | |
32 | Transmitter <---> accelDataflow ---> Firmware |
33 | | | | |
34 | accelECU <---> vNet1 <---> GatewayECU <---> vNet2 |
35 | | | |
36 | engineECU <------- IDPS |
37 | | | |
38 | engine -----> acceleratorAccount <---> canID <----------
39 |
40 | */
41 | System.out.println("### " + Thread.currentThread().getStackTrace()[1].getMethodName());
42 | // Start of test
43 | boolean firewallStatus = true;
44 | boolean firmwareValidationStatus = false;
45 | boolean secureBootStatus = false;
46 | ECU acceleratorEcu =
47 | new ECU(
48 | "acceleratorEcu",
49 | true,
50 | true); // Enabled operation mode and message confliction protection on all ECUs.
51 | ECU engineEcu = new ECU("engineEcu", true, true);
52 | GatewayECU gateEcu = new GatewayECU("GatewayECU", firewallStatus, true, true);
53 | IDPS idps = new IDPS("IDPS");
54 | VehicleNetwork vNet1 = new VehicleNetwork("vNet1");
55 | VehicleNetwork vNet2 = new VehicleNetwork("vNet2");
56 | ConnectionlessDataflow accelarationDataflow =
57 | new ConnectionlessDataflow("accelarationDataflow");
58 | TransmitterService transmitter = new TransmitterService("Transmitter");
59 | Firmware fw = new Firmware("fw", firmwareValidationStatus, secureBootStatus);
60 | SensorOrActuator engine = new SensorOrActuator("engine");
61 | MessageID canID = new MessageID("CAN-ID");
62 | Account acceleratorAccount = new Account("acceleratorAccount");
63 |
64 | acceleratorEcu.addExecutees(transmitter);
65 | transmitter.addDataflows(accelarationDataflow);
66 | acceleratorEcu.addVehiclenetworks(vNet1);
67 | engineEcu.addVehiclenetworks(vNet1);
68 | engineEcu.addSensorsOrActuators(engine);
69 | vNet1.addDataflows(accelarationDataflow);
70 | gateEcu.addTrafficVNetworks(vNet1);
71 | gateEcu.addTrafficVNetworks(vNet2);
72 | gateEcu.addIdps(idps);
73 | gateEcu.addFirmware(fw);
74 |
75 | acceleratorAccount.addCredentials(canID);
76 | accelarationDataflow.addData(canID);
77 | engineEcu.addAccount(acceleratorAccount);
78 |
79 | Attacker atk = new Attacker();
80 | atk.addAttackPoint(vNet2.physicalAccess);
81 | atk.addAttackPoint(canID.read);
82 | atk.attack();
83 |
84 | vNet2.physicalAccess.assertCompromisedInstantaneously();
85 | vNet2.accessNetworkLayer.assertCompromisedInstantaneously();
86 | gateEcu.connect.assertCompromisedInstantaneously();
87 | fw.maliciousFirmwareModification.assertUncompromised();
88 | vNet1.accessNetworkLayer.assertUncompromised();
89 | accelarationDataflow.transmit.assertUncompromised();
90 | accelarationDataflow.eavesdrop.assertUncompromised();
91 |
92 | // engine.access.assertUncompromised();
93 | acceleratorAccount.idAuthenticate.assertCompromisedInstantaneously();
94 | engineEcu.idAccess.assertCompromisedInstantaneously();
95 | engine.manipulate.assertCompromisedInstantaneously();
96 | }
97 |
98 | @AfterEach
99 | public void deleteModel() {
100 | Asset.allAssets.clear();
101 | AttackStep.allAttackSteps.clear();
102 | Defense.allDefenses.clear();
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/all-features/all-features.mal:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright notice
3 | */
4 |
5 | // These are required
6 | #id: "all-features"
7 | #version: "0.0.1"
8 |
9 | include "core.mal"
10 | include "subdir/subincluded.mal"
11 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/all-features/core.mal:
--------------------------------------------------------------------------------
1 | category C1
2 | user info: "This is C1"
3 | modeler info: "None for C1"
4 | developer info: "Reasoning for C1"
5 | {
6 | // This category only contains meta information
7 | }
8 |
9 | category C1 {
10 | asset A1
11 | {
12 | | a1Attack1 {C}
13 | user info: "This is a1Attack1"
14 | modeler info: "None for a1Attack1"
15 | developer info: "Reasoning for a1Attack1"
16 | // No reaches
17 | & a1Attack2 {I, C} []
18 | user info: "This is a1Attack2"
19 | -> a1Sub[A1]*.a1Attack1
20 | # a1Defense1 [Bernoulli(0.5)]
21 | developer info: "Reasoning for a1Defense"
22 | // No reaches
23 | # a1Defense2 [Disabled]
24 | -> a1Attack2
25 | E a1Exist1
26 | <- a1Sub
27 | /* No reaches */
28 | E a1Exist2
29 | modeler info: "None for a1Exist2"
30 | <- a7, a1Super.a1Super.a7
31 | -> (a1Super*)[A2].(a8.destroy)
32 | !E a1NotExist1
33 | <- a8.a8Sub*.a1[A7]
34 | // No reaches
35 | !E a1NotExist2
36 | <- a8.a4 /\ a4
37 | -> a4.a
38 | }
39 |
40 | asset A2 extends A1 {
41 | // Extend A1.a1Attack1, override cia, override ttc, override reaches
42 | | a1Attack1 {}
43 | -> a1Super.a1Attack1
44 | // Extend A1.a1Attack1, override cia, inherit ttc, inherit reaches
45 | & a1Attack2 {C, I, A}
46 | +> a1Super.a1Attack1
47 | // Extend A1.a1Defense1, override ttc, override reaches
48 | # a1Defense1 [Enabled]
49 | -> a1Sub.a1Sub.a1Attack2
50 | // Extend A1.a1Defense2, inherit ttc, inherit reaches
51 | # a1Defense2
52 | +> a1Attack2
53 | // Extend A1.a1Exist2, override requires, override reaches
54 | E a1Exist2
55 | <- a8
56 | -> a1Defense2
57 | // Extend A1.a1NotExist2, override requires, inherit reaches
58 | !E a1NotExist1
59 | <- a8
60 | +> a1Defense1
61 | }
62 |
63 | abstract asset A3 extends A1
64 | user info: "This is A3"
65 | {
66 | let unused = a1Sub
67 | | a3Attack {A, I, C}
68 | let used = a1Sub[A2]
69 | // Variable using variable
70 | let V1 = used()
71 | & AT
72 | -> V1().a1Attack1
73 | }
74 | }
75 |
76 | category C2 { /* This category is empty */ }
77 |
78 | category C2
79 | modeler info: "None for C2"
80 | {
81 | abstract asset A4
82 | modeler info: "None for A4"
83 | developer info: "Reasoning for A4"
84 | user info: "This is A4"
85 | {
86 | let var = a1.a1Sub*
87 | | a
88 | -> (var() \/ var2()).a1Attack1
89 | let var2 = var()[A3]
90 | }
91 | }
92 |
93 | category C2
94 | developer info: "Reasoning for C2"
95 | {
96 | asset A5 extends A4
97 | modeler info: "None for A5"
98 | { /* This asset is empty */ }
99 |
100 | abstract asset A6 extends A4
101 | developer info: "Reasoning for A6"
102 | {
103 | // Nothing extends this abstract asset
104 | }
105 | }
106 |
107 | category C3 {
108 | asset A7 extends A3 {
109 | | a7Attack
110 | -> a1().destroy
111 | let a6 = a1Super.a8
112 | let a1 = a6().a8Super
113 | }
114 |
115 | asset A8 {
116 | & destroy {C, I, A} [Exponential(5)]
117 | }
118 |
119 | asset A9 {
120 | // This asset has no fields or inherited fields
121 | // There are no attack steps or inherited attack steps either
122 | }
123 | }
124 |
125 | associations { /* No associations here */ }
126 |
127 | associations {
128 | // Some associations here
129 | A1 [a1] 1 <-- L1 --> 1..* [a4] A4
130 | A5 [a5] 1..1 <-- L2 --> 0..* [a6] A6
131 | }
132 |
133 | associations {
134 | // And some here
135 | A1 [a1Super] 0..1 <-- L3 --> * [a1Sub] A1
136 | A3 [a3] * <-- L3 --> * [a6] A6
137 | A7 [a7] 0..1 <-- L3 --> 1 [a1] A1
138 | A8 [a8] * <-- L4 --> * [a1] A1
139 | A8 [a8] * <-- L4 --> * [a4] A4
140 | A8 [a8Sub] * <-- L4 --> * [a8Super] A8
141 | }
142 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/all-features/included.mal:
--------------------------------------------------------------------------------
1 |
2 | # other : "other"
3 |
4 | include "all-features.mal"
5 | // This should already be included
6 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/all-features/subdir/subincluded.mal:
--------------------------------------------------------------------------------
1 |
2 | # // single line comment
3 | empty /* C-style single line comment */
4 | : /* multi line
5 | comment*/ ""
6 |
7 | include
8 | "../included.mal"
9 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/analyzer/bad1.mal:
--------------------------------------------------------------------------------
1 | // no #id
2 | // no #version
3 | #custom: ""
4 | #custom: "" // duplicate define
5 | category emp {} // empty category (warning)
6 | category cat
7 | developer info: "rat"
8 | {}
9 | category cat
10 | user info: "category"
11 | user info: "category" // duplicate info
12 | developer info: "rat" // duplicate rationale from different definition
13 | {
14 | asset _A
15 | developer info: "asset"
16 | developer info: "asset" // duplicate rationale
17 | {
18 | | compromise ->
19 | (a /\ a), // not attack step
20 | c.compromise
21 | | compromise <- a // duplicate attack step AND invalid require
22 | & invalidate ->
23 | (c \/ a).compromise, // invalid types
24 | c[_A].compromise // invalid typeof
25 | }
26 | abstract asset AA extends _A { // abstract but never used (warning)
27 | & compromise // types differ from parent
28 | | access +> // parent doesn't have attack step
29 | authorize, // invalid attack step
30 | b.authorize // invalid field
31 | }
32 | asset AA {} // duplicate asset
33 | asset _C {
34 | let var1 = var1
35 | let var1 = a // duplicate variable
36 | let aaa = a
37 | | compromise {C, C} -> // duplicate CIA
38 | var1, // cyclic variable
39 | aaa()*.compromise // invalid previous type
40 | E e {C} // defense with CIA
41 | <- a
42 | }
43 | }
44 | associations {
45 | _A [a] * <-- ac --> * [c] _C
46 | _A [a] * <-- aa --> * [a] _A // duplicate fields
47 | modeler info: "association"
48 | modeler info: "association" // duplicate association
49 | }
50 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/analyzer/bad2.mal:
--------------------------------------------------------------------------------
1 | #id: "" // invalid id
2 | #version: "" // invalid version
3 | category cat {
4 | asset _A extends _B {} // cyclic #1
5 | asset _B extends _C {} // cyclic #2
6 | asset _C extends _D {} // cyclic #3
7 | asset _D extends _A {} // cyclic #4
8 | }
9 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/analyzer/bad3.mal:
--------------------------------------------------------------------------------
1 | #id: "badvar"
2 | #version: "1.0.0"
3 | category System {
4 | asset Alpha {
5 | let VAR = charlies.alphas
6 | | compromise
7 | -> VAR().deltas.write
8 | }
9 | asset SubAlpha extends Alpha {
10 | let VAR = charlies.alphas // variable already defined in parent
11 | | access
12 | }
13 | asset Bravo {
14 | let VAR1 = alphas
15 | | access
16 | -> VAR1.compromise // trying to use VAR1 without parentheses ()
17 | }
18 | asset Charlie {
19 | | read
20 | }
21 | asset Delta {
22 | | write
23 | }
24 | }
25 | associations {
26 | Alpha [alphas] * <-- _ --> * [bravos] Bravo
27 | Alpha [alphas] * <-- _ --> * [deltas] Delta
28 | Alpha [alphas] * <-- _ --> * [charlies] Charlie
29 | Charlie [charlies] * <-- _ --> * [bravos] Bravo
30 | }
31 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/analyzer/complex-analyzer-debug.txt:
--------------------------------------------------------------------------------
1 | [ANALYZER WARNING] Association 'Computer [studentComputer] <-- Use --> Student [student]' is never used
2 | [ANALYZER WARNING] Association 'Computer [teacherComputer] <-- Use --> Teacher [teacher]' is never used
3 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/analyzer/complex-analyzer-verbose.txt:
--------------------------------------------------------------------------------
1 | [ANALYZER WARNING] Association 'Computer [studentComputer] <-- Use --> Student [student]' is never used
2 | [ANALYZER WARNING] Association 'Computer [teacherComputer] <-- Use --> Teacher [teacher]' is never used
3 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/analyzer/complex-analyzer-warnings.txt:
--------------------------------------------------------------------------------
1 | [ANALYZER WARNING] Association 'Computer [studentComputer] <-- Use --> Student [student]' is never used
2 | [ANALYZER WARNING] Association 'Computer [teacherComputer] <-- Use --> Teacher [teacher]' is never used
3 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/analyzer/complex-lexer-debug.txt:
--------------------------------------------------------------------------------
1 | [LEXER DEBUG] Creating lexer with file 'complex.mal'
2 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/analyzer/complex-lexer-verbose.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mal-lang/malcompiler/b8bf5e803470974b2815862a64b300a18e0dde8b/malcompiler-test/src/test/resources/analyzer/complex-lexer-verbose.txt
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/analyzer/complex-parser-debug.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mal-lang/malcompiler/b8bf5e803470974b2815862a64b300a18e0dde8b/malcompiler-test/src/test/resources/analyzer/complex-parser-debug.txt
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/analyzer/complex-parser-verbose.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mal-lang/malcompiler/b8bf5e803470974b2815862a64b300a18e0dde8b/malcompiler-test/src/test/resources/analyzer/complex-parser-verbose.txt
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/analyzer/complex.mal:
--------------------------------------------------------------------------------
1 | /*
2 | Modified version of "MAL Example with all Features" from Appendix A in
3 | "MAL BoK: the Meta Attack Language Body of Knowledge and User Guide" to
4 | be semantically and syntactically correct as well as have more features.
5 | */
6 | #id: "complex"
7 | #version: "1.0.0"
8 | category Person {
9 | abstract asset User {
10 | let MYVAR = computer.externalHD
11 | | impersonate @hidden
12 | -> compromise,
13 | stealInformation
14 | | compromise
15 | -> computer.stealSecret
16 | | stealInformation
17 | //collect example, a reference to attack step stolenSecrets via 2 steps.
18 | -> computer.internalHD.stealHDSecrets
19 | | stealFolder
20 | //varible example, using a variable declared in the asset.
21 | -> MYVAR().stealFolder
22 | }
23 | asset Student extends User {}
24 | asset Teacher extends User {
25 | | accessSchoolComputer
26 | user info: "An extra level of protection, their school computer must be used to impersonate them."
27 | -> compromise
28 | }
29 | }
30 | category Hardware {
31 | asset Computer {
32 | let HDDs = externalHD \/ internalHD
33 | | malwareInfection
34 | -> interceptTraffic
35 | | interceptTraffic
36 | -> retrievePassword
37 | & retrievePassword
38 | user info: "Retrieval of password is only possible if password is unencrypted"
39 | -> user.impersonate
40 | | bypassFirewall [Exponential(0.05) * Gamma(1.2, 1.7)]
41 | -> firewall.bypassFirewall
42 | | stealSecret
43 | //Set example, only when the external and internal HD have their secrets stolen do we have stolen secrets.
44 | -> HDDs().stealHDSecrets
45 | # passwordEncrypted
46 | -> retrievePassword
47 | E firewallProtected
48 | <- firewall
49 | -> firewall.bypassFirewall
50 | }
51 | asset Firewall {
52 | & bypassFirewall
53 | -> computer.retrievePassword,
54 | computer.interceptTraffic,
55 | //TypeOf example, after the firewall is bypassed the teacher can be impersonated.
56 | user[Teacher].impersonate
57 | }
58 | asset Harddrive {
59 | | stealHDSecrets
60 | | stealFolder
61 | //Transitive example, we can refer many steps away without writing all of the steps.
62 | -> ((folder.(subFolder)*).accessFolder)
63 | }
64 | asset SecretFolder {
65 | | accessFolder
66 | }
67 | }
68 | associations {
69 | Computer [studentComputer] 1..* <-- Use --> 1 [student] Student
70 | Computer [teacherComputer] 1 <-- Use --> 1 [teacher] Teacher
71 | Computer [computer] * <-- Protect --> 1 [firewall] Firewall
72 | Firewall [firewall] 1 <-- Protect --> * [user] User
73 | Computer [computer] 1 <-- Storage --> 1 [user] User
74 | Computer [extHDComputer] 1 <-- Use --> 1 [externalHD] Harddrive
75 | Computer [intHDComputer] 1 <-- Contain --> 1 [internalHD] Harddrive
76 | Harddrive [internalHD] 1 <-- Contain --> * [folder] SecretFolder
77 | SecretFolder [folder] 1 <-- Contain --> * [subFolder] SecretFolder
78 | }
79 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/analyzer/dist-fail.mal:
--------------------------------------------------------------------------------
1 | #id: "dist"
2 | #version: "1.0.0"
3 | category System {
4 | asset Alpha {
5 | | compromise [Bernoulli(0.5) * Exponential(0.1) - Bernoulli(0.7)]
6 | -> bravo.compromise
7 | }
8 | asset Bravo {
9 | | compromise
10 | }
11 | }
12 | associations {
13 | Bravo [bravo] 1 <-- _ --> 1 [alpha] Alpha
14 | }
15 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/analyzer/distributions.mal:
--------------------------------------------------------------------------------
1 | #id: "distributions"
2 | #version: "0.0.0"
3 | category Distributions {
4 | asset Distribution {
5 | | bernoulli [
6 | Bernoulli +
7 | Bernoulli() +
8 | Bernoulli(0,0) +
9 | Bernoulli(1.1) +
10 | Bernoulli(1) //ok
11 | ]
12 | | binomial [
13 | Binomial +
14 | Binomial() +
15 | Binomial(0) +
16 | Binomial(0,0,0) +
17 | Binomial(1,1.1) +
18 | Binomial(1,1) //ok
19 | ]
20 | | exponential [
21 | Exponential +
22 | Exponential() +
23 | Exponential(0,0) +
24 | Exponential(0) +
25 | Exponential(1) //ok
26 | ]
27 | | gamma [
28 | Gamma +
29 | Gamma() +
30 | Gamma(0) +
31 | Gamma(0,0,0) +
32 | Gamma(0,0) +
33 | Gamma(1,0) +
34 | Gamma(1,1) //ok
35 | ]
36 | | lognormal [
37 | LogNormal +
38 | LogNormal() +
39 | LogNormal(0) +
40 | LogNormal(0,0,0) +
41 | LogNormal(0,0) +
42 | LogNormal(0,1) //ok
43 | ]
44 | | pareto [
45 | Pareto +
46 | Pareto() +
47 | Pareto(0) +
48 | Pareto(0,0,0) +
49 | Pareto(0,0) +
50 | Pareto(1,0) +
51 | Pareto(1,1) //ok
52 | ]
53 | | truncatednormal [
54 | TruncatedNormal +
55 | TruncatedNormal() +
56 | TruncatedNormal(0) +
57 | TruncatedNormal(0,0,0) +
58 | TruncatedNormal(0,0) +
59 | TruncatedNormal(0,1) //ok
60 | ]
61 | | uniform [
62 | Uniform +
63 | Uniform() +
64 | Uniform(0) +
65 | Uniform(0,0,0) +
66 | Uniform(1,0) +
67 | Uniform(0,0) //ok
68 | ]
69 | | easyandcertain [
70 | EasyAndCertain(0) +
71 | EasyAndCertain() + //ok
72 | EasyAndCertain //ok
73 | ]
74 | | easyanduncertain [
75 | EasyAndUncertain(0) +
76 | EasyAndUncertain() + //ok
77 | EasyAndUncertain //ok
78 | ]
79 | | hardandcertain [
80 | HardAndCertain(0) +
81 | HardAndCertain() + //ok
82 | HardAndCertain //ok
83 | ]
84 | | hardanduncertain [
85 | HardAndUncertain(0) +
86 | HardAndUncertain() + //ok
87 | HardAndUncertain //ok
88 | ]
89 | | veryhardandcertain [
90 | VeryHardAndCertain(0) +
91 | VeryHardAndCertain() + //ok
92 | VeryHardAndCertain //ok
93 | ]
94 | | veryhardanduncertain [
95 | VeryHardAndUncertain(0) +
96 | VeryHardAndUncertain() + //ok
97 | VeryHardAndUncertain //ok
98 | ]
99 | | zero [
100 | Zero(0) +
101 | Zero() + //ok
102 | Zero //ok
103 | ]
104 | | infinity [
105 | Infinity(0) +
106 | Infinity() + //ok
107 | Infinity //ok
108 | ]
109 | # enabled1 [Enabled + Disabled]
110 | # enabled2 [Enabled(0)]
111 | # enabled3 [Enabled()] //ok
112 | # enabled4 [Enabled] //ok
113 | # disabled1 [Enabled + Disabled]
114 | # disabled2 [Disabled(0)]
115 | # disabled3 [Disabled()] //ok
116 | # disabled4 [Disabled] //ok
117 | # bern [Bernoulli(0.5)] //ok
118 | # nobern [Exponential(0.5)]
119 | & enab [Enabled()]
120 | & disab [Disabled()]
121 | | undefined [BestTTC(1.2)]
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/analyzer/invalid-assoc.mal:
--------------------------------------------------------------------------------
1 | #id: "broken.assoc"
2 | #version: "1.0.0"
3 |
4 | category System {
5 | asset Groups {
6 | }
7 |
8 | asset User {
9 | }
10 | }
11 |
12 | associations {
13 | User [users] * <-- UserGroups --> * [groups] Group
14 | }
15 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/bled/bled.mal:
--------------------------------------------------------------------------------
1 | #id: "BLED" // bleeding edge
2 | #version: "999.999.999"
3 | category LatestAndGreatest {
4 | asset Firewall {}
5 | asset IP {}
6 | asset Network {
7 | | helper @hidden
8 | | reached @debug
9 | }
10 | asset Host {
11 | | access ->
12 | (networks.hosts.ip /\ (fws.allowedIPs - fws.blockedIPs)).host.access
13 | }
14 | }
15 | associations {
16 | Firewall [fwBlocked] * <-- Rule --> * [blockedIPs] IP
17 | Firewall [fwAllowed] * <-- Rule --> * [allowedIPs] IP
18 | Host [host] 1 <-- Assigned --> 0..1 [ip] IP
19 | Network [networks] 1..* <-- Connected --> * [hosts] Host
20 | Host [host] 0..1 <-- Protects --> * [fws] Firewall
21 | }
22 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/formatter/comments.ans:
--------------------------------------------------------------------------------
1 | category Something
2 | {
3 | // multiple single line
4 | // comments with a user
5 |
6 | // made blank line
7 | // between them
8 |
9 | asset Something
10 | {
11 | | something
12 | -> something.something, // trailing comment that the
13 | // user put on separate lines
14 | something.something
15 |
16 | & something
17 | -> // comment as first step entry
18 | something.something,
19 |
20 | something.something // user made blank line before this line
21 | // with some extra comment text here
22 |
23 | }
24 | }
25 |
26 | /*
27 | multi
28 | line
29 | comment
30 | */
31 |
32 | /*
33 | * multi
34 | * line
35 | * comment
36 | */
37 |
38 | associations
39 | {
40 | // very long single line comment before a line that should fit on one line without breaking
41 | A1 [a1] 1 <-- L --> 1 [a2] A2
42 |
43 | A1 [a1] 1 <-- L --> 1 [a2] A2 // very long comment trailing a line that should fit despite this comment
44 |
45 | }
46 |
47 | /*
48 | * multi line comment
49 | * placed at the end
50 | */
51 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/formatter/comments.mal:
--------------------------------------------------------------------------------
1 | category Something {
2 | // multiple single line
3 | // comments with a user
4 |
5 | // made blank line
6 | // between them
7 |
8 | asset Something {
9 | | something
10 | -> something.something, // trailing comment that the
11 | // user put on separate lines
12 | something.something
13 |
14 | & something
15 | -> // comment as first step entry
16 | something.something,
17 |
18 | something.something // user made blank line before this line
19 | // with some extra comment text here
20 | }
21 | }
22 |
23 | /*
24 | multi
25 | line
26 | comment
27 | */
28 |
29 | /*
30 | * multi
31 | * line
32 | * comment
33 | */
34 |
35 | associations {
36 | // very long single line comment before a line that should fit on one line without breaking
37 | A1 [a1] 1 <-- L --> 1 [a2] A2
38 |
39 | A1 [a1] 1 <-- L --> 1 [a2] A2 // very long comment trailing a line that should fit despite this comment
40 | }
41 |
42 | /*
43 | * multi line comment
44 | * placed at the end
45 | */
46 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/formatter/escape.ans:
--------------------------------------------------------------------------------
1 | category System
2 | {
3 | asset Computer
4 | user info: "newline\nslash\\quote\"tab\tback\brrr\rf\f"
5 | {
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/formatter/escape.mal:
--------------------------------------------------------------------------------
1 | category System {
2 | asset Computer
3 | user info: "newline\nslash\\quote\"tab\tback\brrr\rf\f"
4 | {}
5 | }
6 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/formatter/margin.mal:
--------------------------------------------------------------------------------
1 | category System {
2 | abstract asset SomeLongNameThatIsLong extends SomeOtherName {
3 | let variable1 = steps.that.exists /\ combined.with.some.other.steps
4 | let variable2 = even.more.steps[Step]
5 |
6 | | compromise
7 | -> variable1()[SomeOtherName].substeps*.local.read,
8 | (variable2().after.open - well.named.steps.written)
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/formatter/margin30.ans:
--------------------------------------------------------------------------------
1 | category System
2 | {
3 | abstract asset
4 | SomeLongNameThatIsLong
5 | extends
6 | SomeOtherName
7 | {
8 | let variable1 =
9 | steps.that.exists /\
10 | combined.with.some
11 | .other.steps
12 | let variable2 =
13 | even.more.steps[Step]
14 |
15 | | compromise
16 | -> variable1()[SomeOtherName]
17 | .substeps*
18 | .local.read,
19 | (variable2()
20 | .after.open -
21 | well.named.steps
22 | .written)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/formatter/margin50.ans:
--------------------------------------------------------------------------------
1 | category System
2 | {
3 | abstract asset
4 | SomeLongNameThatIsLong extends SomeOtherName
5 | {
6 | let variable1 =
7 | steps.that.exists
8 | /\ combined.with.some.other.steps
9 | let variable2 = even.more.steps[Step]
10 |
11 | | compromise
12 | -> variable1()[SomeOtherName]
13 | .substeps*.local.read,
14 | (variable2().after.open
15 | - well.named.steps.written)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/formatter/oneline.ans:
--------------------------------------------------------------------------------
1 | #id: "oneline"
2 | #version: "1.0.0"
3 | category System
4 | {
5 | abstract asset Device
6 | {
7 | }
8 | asset Computer extends Device
9 | {
10 | | compromise
11 | -> access,
12 | network.local.access,
13 | (public.screen /\ local.screen).damage
14 | }
15 | }
16 | associations
17 | {
18 | Computer [comp1] 1 <-- link --> 1 [comp2] Computer
19 | }
20 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/formatter/oneline.mal:
--------------------------------------------------------------------------------
1 | #id:"oneline"#version:"1.0.0"category System{abstract asset Device{}asset Computer extends Device{|compromise->access,network.local.access,(public.screen/\local.screen).damage}}associations{Computer[comp1]1<--link-->1[comp2]Computer}
2 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/formatter/readable.ans:
--------------------------------------------------------------------------------
1 | #myDefine: "1.0.0"
2 | // Some comment
3 | category System
4 | developer /*secret inline comment*/ info: "Readable code"
5 | user info: "Null"
6 | {
7 | asset myAsset extends otherAsset
8 | user info: "Something something"
9 | {
10 | let myVar = very.long.expression /\ very.long.expression /\ very.long.expression
11 | /\ very.long.expression /\ very.long.expression /\ very.long.expression
12 | /\ very.long.expression /\ very.long.expression /\ very.long.expression
13 | | compromise @tag @tag2 {C, I} [Infinity() * (9 ^ 4)]
14 | -> someasset.compromise.(access() /\ blocked)[One].open,
15 | // other steps
16 | compromise,
17 | read
18 | }
19 | abstract asset Empty
20 | {
21 | }
22 | }
23 | associations
24 | {
25 | From [type] 1..* <-- name --> 1 [type2] To
26 | user info: "Assoc info"
27 | From [type] * <-- name --> * [type] To
28 | }
29 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/formatter/readable.mal:
--------------------------------------------------------------------------------
1 | #
2 | myDefine
3 | : "1.0.0"
4 | // Some comment
5 | category
6 |
7 | System developer/*secret inline comment*/ info: "Readable code"user info:"Null" {
8 | asset
9 | myAsset extends
10 | otherAsset
11 | user
12 | info
13 | :
14 |
15 |
16 | "Something something"
17 | { let myVar = very.long.expression/\very.long.expression/\very.long.expression/\very.long.expression/\very.long.expression/\very.long.expression/\very.long.expression/\very.long.expression/\very.long.expression
18 | | compromise @tag
19 | @tag2 { C ,I}
20 | [
21 | Infinity() *
22 | (9 ^4)
23 | ] ->
24 | someasset
25 | .
26 | compromise .(access() /\blocked)
27 | [
28 | One].open,
29 | // other steps
30 | compromise , read
31 | } abstract asset Empty{}
32 | }associations{From[type]1..*<--
33 | name
34 |
35 | -->1[
36 |
37 | type2]
38 | To user info:"Assoc info"From[type]*<--name-->*[type]To}
39 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/generator/attack-step-set.mal:
--------------------------------------------------------------------------------
1 | #id: "attack-step-set"
2 | #version: "0.0.0"
3 |
4 | category Container {
5 | asset File {
6 | | set
7 | }
8 |
9 | asset ZipFile extends File {
10 | }
11 | }
12 |
13 | associations {
14 | ZipFile [archive] 0..1 <-- Compressed --> * [archived] File
15 | }
16 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/generator/bad-lang.mal:
--------------------------------------------------------------------------------
1 | #id: "bad-lang"
2 | #version: "0.0.0"
3 |
4 | category Cat {
5 | asset int {
6 | | null
7 | }
8 | }
9 |
10 | associations {
11 | int [false] * <-- _ --> * [static] int
12 | }
13 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/generator/debug-step.mal:
--------------------------------------------------------------------------------
1 | #id: "dist"
2 | #version: "1.0.0"
3 | category System {
4 | asset Alpha {
5 | | access @debug
6 | -> charlies.compromise
7 | }
8 | asset Bravo extends Alpha {
9 | | access
10 | }
11 | asset Charlie {
12 | | compromise
13 | -> alphas.access
14 | }
15 | }
16 | associations {
17 | Alpha [alphas] * <-- _ --> * [charlies] Charlie
18 | }
19 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/generator/dist.mal:
--------------------------------------------------------------------------------
1 | #id: "dist"
2 | #version: "1.0.0"
3 | category System {
4 | asset Alpha {
5 | | compromise [Bernoulli(0.5) * Exponential(0.1)]
6 | -> bravo.compromise
7 | }
8 | asset Bravo {
9 | | compromise [Bernoulli(0.5) * LogNormal(7, 1) + Gamma(0.2, 1) * Bernoulli(0.9)]
10 | | access [5 + Bernoulli(0.5)]
11 | | read [Bernoulli(0.5) + Pareto(5, 1) + Gamma(0.2, 1) * Bernoulli(0.9)]
12 | | write [Binomial(5, 0.5) - Gamma(0.2, 1) / TruncatedNormal(0.9, 4) ^ Uniform(3, 4)]
13 | | delete [EasyAndCertain + EasyAndUncertain * HardAndCertain - HardAndUncertain]
14 | | overwrite [VeryHardAndCertain ^ VeryHardAndUncertain]
15 | | lock [Infinity - Zero + 5]
16 | }
17 | }
18 | associations {
19 | Bravo [bravo] 1 <-- _ --> 1 [alpha] Alpha
20 | }
21 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/generator/naming.mal:
--------------------------------------------------------------------------------
1 | #id: "naming"
2 | #version: "1.0.0"
3 | category System {
4 | asset A1 {
5 | | a1
6 | }
7 | asset A2 extends A1 {
8 | | a1 // OK
9 | | A2
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/generator/nested.mal:
--------------------------------------------------------------------------------
1 | #id: "nested"
2 | #version: "0.0.0"
3 |
4 | category System {
5 | asset Interface {
6 | let myNetworks = network.superNetwork*
7 | let myRoutes = myNetworks().myRoutes()
8 |
9 | | connect
10 | -> myRoutes().use
11 | }
12 |
13 | asset Network {
14 | let myRoutes = routes.subRoutes*
15 | }
16 |
17 | asset Route {
18 | | use
19 | }
20 | }
21 |
22 | associations {
23 | Interface [interfaces] * <-- _ --> 1 [network] Network
24 | Network [network] 1 <-- _ --> * [routes] Route
25 | Network [superNetwork] 1 <-- _ --> * [subNetworks] Network
26 | Route [superRoute] 1 <-- _ --> * [subRoutes] Route
27 | }
28 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/generator/steps.mal:
--------------------------------------------------------------------------------
1 | #id: "steps"
2 | #version: "0.0.0"
3 |
4 | category System {
5 | asset Alpha {
6 | | compromise ->
7 | bravos.subbravos*.access,
8 | bravos.subbravos*[SubBravo].access,
9 | bravos[SubBravo].subbravos*.access,
10 | bravos[SubBravo].subbravos[SubBravo]*.access,
11 | bravos.subbravos**.access,
12 | bravos.subbravos**[SubBravo].access,
13 | bravos[SubBravo].subbravos**[SubBravo]*.access,
14 | bravos[SubSubBravo].access,
15 | bravos[SubBravo][SubSubBravo].access,
16 | bravos.SUB()*.access,
17 | bravos.SUB()**.access,
18 | bravos[SubBravo].SUB()*.access,
19 | bravos[SubBravo].SUB()[SubBravo]*.access,
20 | bravos.(SUB()*)*.access
21 | }
22 | asset Bravo {
23 | let SUB = subbravos
24 | | access
25 | }
26 | asset SubBravo extends Bravo {
27 | }
28 | asset SubSubBravo extends SubBravo {
29 | }
30 | }
31 |
32 | associations {
33 | Alpha [alphas] * <-- _ --> * [bravos] Bravo
34 | Bravo [subbravos] * <-- _ --> * [parentbravos] Bravo
35 | }
36 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/generator/subtype.mal:
--------------------------------------------------------------------------------
1 | #id: "subtype"
2 | #version: "1.0.0"
3 |
4 | category Assets {
5 | abstract asset Resource {
6 | }
7 |
8 | asset User extends Resource {
9 | | assume
10 | //-> actions[Assume].perform
11 | }
12 |
13 | abstract asset Action {
14 | | perform
15 | }
16 |
17 | asset Assume extends Action {
18 | | perform
19 | -> resources[User].assume
20 | }
21 | }
22 |
23 | associations {
24 | Resource [resources] * <-- ResourceAction --> * [actions] Action
25 | }
26 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/generator/variable.mal:
--------------------------------------------------------------------------------
1 | #id: "variable"
2 | #version: "0.0.0"
3 |
4 | category System {
5 | asset Alpha {
6 | | read
7 | -> bravos.BRAVO_VAR().read
8 | }
9 | asset Bravo {
10 | let BRAVO_VAR = alphas
11 | | access
12 | }
13 | }
14 |
15 | associations {
16 | Alpha [alphas] * <-- _ --> * [bravos] Bravo
17 | }
18 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/lang-converter/reverse.mal:
--------------------------------------------------------------------------------
1 | #id: "reverse"
2 | #version: "0.0.0"
3 | category CAT {
4 | asset Action {
5 | | compromise
6 | }
7 | asset LocalAction extends Action {
8 | | compromise
9 | -> (a.ga /\ a.la).compromise
10 | }
11 | asset GlobalAction extends Action {
12 | | compromise
13 | }
14 | asset Alpha {}
15 | }
16 | associations {
17 | LocalAction [la] * <-- _ --> * [a] Alpha
18 | GlobalAction [ga] * <-- _ --> * [a] Alpha
19 | }
20 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/lexer/bad-unicode-1.txt:
--------------------------------------------------------------------------------
1 | "Bad escape: \ä"
2 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/lexer/bad-unicode-2.txt:
--------------------------------------------------------------------------------
1 | ä
2 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/lexer/invalid.txt:
--------------------------------------------------------------------------------
1 | <
2 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/lexer/invalid_escape_sequence.txt:
--------------------------------------------------------------------------------
1 | "\a"
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/lexer/keywords.txt:
--------------------------------------------------------------------------------
1 | include
2 | info
3 | category
4 | abstract
5 | asset
6 | extends
7 | associations
8 | let
9 | E
10 | C
11 | I
12 | A
13 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/lexer/lexemes.txt:
--------------------------------------------------------------------------------
1 | // comment
2 | /* multiline
3 | comment */
4 | "string"
5 | "escape\b\n\t\r\"\\"
6 | 1
7 | 1.2
8 | identifier
9 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/lexer/tokens.txt:
--------------------------------------------------------------------------------
1 | #
2 | :
3 | {
4 | }
5 | +>
6 | ->
7 | &
8 | |
9 | !E
10 | @
11 | [
12 | ]
13 | (
14 | )
15 | ,
16 | <-
17 | =
18 | \/
19 | /\
20 | .
21 | ..
22 | *
23 | +
24 | -
25 | /
26 | ^
27 | <--
28 | -->
29 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/lexer/unicode.txt:
--------------------------------------------------------------------------------
1 | "Unicode character: ä"
2 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/lexer/unterminated_comment.txt:
--------------------------------------------------------------------------------
1 | /*
2 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/lexer/unterminated_string.txt:
--------------------------------------------------------------------------------
1 | "str
2 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/parser/assets.mal:
--------------------------------------------------------------------------------
1 | category Cat {
2 | asset A1
3 | user info: "Info1"
4 | developer info: "Reason1"
5 | modeler info: "None1"
6 | {
7 | }
8 |
9 | abstract asset A2
10 | user info: "Info2"
11 | developer info: "Reason2"
12 | modeler info: "None2"
13 | {
14 | let x = y
15 | }
16 |
17 | asset A3 extends A1
18 | user info: "Info3"
19 | developer info: "Reason3"
20 | modeler info: "None3"
21 | {
22 | & a
23 | | a
24 | }
25 |
26 | abstract asset A4 extends A2
27 | user info: "Info4"
28 | developer info: "Reason4"
29 | modeler info: "None4"
30 | {
31 | # a
32 | let x = y
33 | E b
34 | !E c
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/parser/associations.mal:
--------------------------------------------------------------------------------
1 | associations {
2 | // different multiplicities
3 | A1 [b] 1 <-- L --> 1 [a] A2
4 | A1 [b] 1 <-- L --> * [a] A2
5 | A1 [b] 1 <-- L --> 0..1 [a] A2
6 | A1 [b] 1 <-- L --> 0..* [a] A2
7 | A1 [b] 1 <-- L --> 1..1 [a] A2
8 | A1 [b] 1 <-- L --> 1..* [a] A2
9 | A1 [b] * <-- L --> 1 [a] A2
10 | A1 [b] * <-- L --> * [a] A2
11 | A1 [b] * <-- L --> 0..1 [a] A2
12 | A1 [b] * <-- L --> 0..* [a] A2
13 | A1 [b] * <-- L --> 1..1 [a] A2
14 | A1 [b] * <-- L --> 1..* [a] A2
15 | A1 [b] 0..1 <-- L --> 1 [a] A2
16 | A1 [b] 0..1 <-- L --> * [a] A2
17 | A1 [b] 0..1 <-- L --> 0..1 [a] A2
18 | A1 [b] 0..1 <-- L --> 0..* [a] A2
19 | A1 [b] 0..1 <-- L --> 1..1 [a] A2
20 | A1 [b] 0..1 <-- L --> 1..* [a] A2
21 | A1 [b] 0..* <-- L --> 1 [a] A2
22 | A1 [b] 0..* <-- L --> * [a] A2
23 | A1 [b] 0..* <-- L --> 0..1 [a] A2
24 | A1 [b] 0..* <-- L --> 0..* [a] A2
25 | A1 [b] 0..* <-- L --> 1..1 [a] A2
26 | A1 [b] 0..* <-- L --> 1..* [a] A2
27 | A1 [b] 1..1 <-- L --> 1 [a] A2
28 | A1 [b] 1..1 <-- L --> * [a] A2
29 | A1 [b] 1..1 <-- L --> 0..1 [a] A2
30 | A1 [b] 1..1 <-- L --> 0..* [a] A2
31 | A1 [b] 1..1 <-- L --> 1..1 [a] A2
32 | A1 [b] 1..1 <-- L --> 1..* [a] A2
33 | A1 [b] 1..* <-- L --> 1 [a] A2
34 | A1 [b] 1..* <-- L --> * [a] A2
35 | A1 [b] 1..* <-- L --> 0..1 [a] A2
36 | A1 [b] 1..* <-- L --> 0..* [a] A2
37 | A1 [b] 1..* <-- L --> 1..1 [a] A2
38 | A1 [b] 1..* <-- L --> 1..* [a] A2
39 | }
40 |
41 | associations {
42 | // nothing
43 | }
44 |
45 | associations {
46 | // With meta
47 | A1 [b] 1 <-- L --> 1 [a] A2
48 | user info: "testing"
49 | developer info: "hej"
50 | modeler info: "\"!\"!\"!\""
51 | }
52 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/parser/attacksteps.mal:
--------------------------------------------------------------------------------
1 | category Cat {
2 | // Testing TTC
3 | asset A1 {
4 | & a [a+b]
5 | | b [a-b]
6 | # c [a*b]
7 | E d [a/b]
8 | !E e [a^b]
9 | & a [a+b*c]
10 | | b [a*b^e+c]
11 | # c [(a+b)*c]
12 | E d [a+(b*c)]
13 | !E e [(a*b)+c]
14 | & a [a*(b+c)]
15 | | b [a()]
16 | # c [a(0)]
17 | E d [a(13)]
18 | !E e [a(3.14)]
19 | & a [a(1, 1.1)]
20 | | b [a(1, 2.3, 3) + b]
21 | }
22 |
23 | // Testing CIA
24 | asset A2 {
25 | | a1
26 | | a2 {}
27 | | a3 {C}
28 | | a4 {C, C}
29 | | a5 {C, I, A}
30 | | a6 {A, C, I}
31 | | a7 {C, C, I, I, A, A}
32 | }
33 |
34 | // Testing meta
35 | asset A3 {
36 | & a
37 | user info: "Info"
38 | | b
39 | user info: "Info"
40 | developer info: "Reason"
41 | # c [TTC]
42 | user info: "Info"
43 | }
44 |
45 | // Testing existence
46 | asset A4 {
47 | let x = y
48 | let a = b
49 | E a
50 | <-
51 | a.y,
52 | b,
53 | c \/ d,
54 | a.(b[D])*
55 | }
56 |
57 | // Testing reaches
58 | asset A5 {
59 | let x = y
60 | let a = b
61 | & a {I}
62 | ->
63 | a.y,
64 | b,
65 | c \/ d,
66 | a.(b[D])*
67 | | b
68 | <- x
69 | -> y
70 | # c [t]
71 | <- x
72 | +> y
73 | E d {C} [t]
74 | user info: "Info"
75 | <- x
76 | -> y
77 | }
78 |
79 | // Testing tags
80 | asset A6 {
81 | | a @a
82 | | b @a {C}
83 | | c @a @b
84 | | d @a @b [t]
85 | | e @a @b @c {C} [t]
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/parser/bad-asset1.mal:
--------------------------------------------------------------------------------
1 | category Cat {
2 | abstractAsset A1 { }
3 | }
4 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/parser/bad-asset2.mal:
--------------------------------------------------------------------------------
1 | category Cat {
2 | abstract A1 { }
3 | }
4 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/parser/bad-asset3.mal:
--------------------------------------------------------------------------------
1 | category Cat {
2 | asset A1 extends { }
3 | }
4 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/parser/bad-asset4.mal:
--------------------------------------------------------------------------------
1 | category Cat {
2 | asset A1 {
3 | user info: "Info"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/parser/bad-association1.mal:
--------------------------------------------------------------------------------
1 | associations {
2 | A1 [a] 1 <- l -> 1 [b] A2
3 | }
4 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/parser/bad-association2.mal:
--------------------------------------------------------------------------------
1 | associations {
2 | A1 [13] 1 <-- l --> 1 [14] A2
3 | }
4 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/parser/bad-association3.mal:
--------------------------------------------------------------------------------
1 | associations {
2 | A1 [a] 2 <-- l --> 3 [b] A2
3 | }
4 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/parser/bad-association4.mal:
--------------------------------------------------------------------------------
1 | associations {
2 | A1 [a] 1 <-- l --> 0 [b] A2
3 | }
4 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/parser/bad-association5.mal:
--------------------------------------------------------------------------------
1 | associations {
2 | A1 [a] 1 <-- l --> 1.. 0 [b] A2
3 | }
4 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/parser/bad-attackstep1.mal:
--------------------------------------------------------------------------------
1 | category Cat {
2 | asset A1 {
3 | & a {} {}
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/parser/bad-attackstep2.mal:
--------------------------------------------------------------------------------
1 | category Cat {
2 | asset A1 {
3 | & a [(t]
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/parser/bad-attackstep3.mal:
--------------------------------------------------------------------------------
1 | category Cat {
2 | asset A1 {
3 | & a [t)]
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/parser/bad-attackstep4.mal:
--------------------------------------------------------------------------------
1 | category Cat {
2 | asset A1 {
3 | & a
4 | ->
5 | user info: "Info"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/parser/bad-attackstep5.mal:
--------------------------------------------------------------------------------
1 | category Cat {
2 | asset A1 {
3 | & a
4 | <-
5 | user info: "Info"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/parser/bad-attackstep6.mal:
--------------------------------------------------------------------------------
1 | category Cat {
2 | asset A1 {
3 | | a1 {Confidentiality}
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/parser/bad-attackstep7.mal:
--------------------------------------------------------------------------------
1 | category Cat {
2 | asset A1 {
3 | | a1 [t] {C, I, A}
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/parser/bad-attackstep8.mal:
--------------------------------------------------------------------------------
1 | category Cat {
2 | asset A1 {
3 | | a1 @ {C}
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/parser/bad-category1.mal:
--------------------------------------------------------------------------------
1 | category Cat {
2 | user info: "Info"
3 | }
4 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/parser/bad-define1.mal:
--------------------------------------------------------------------------------
1 | #a = "b"
2 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/parser/bad-define2.mal:
--------------------------------------------------------------------------------
1 | #a: b
2 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/parser/bad-include1.mal:
--------------------------------------------------------------------------------
1 | include subDir/non-existant.mal
2 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/parser/bad-include2.mal:
--------------------------------------------------------------------------------
1 | include "subDir/non-existant.mal"
2 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/parser/bad-include3.mal:
--------------------------------------------------------------------------------
1 | include "subDir/bad-included1.mal"
2 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/parser/bad-meta1.mal:
--------------------------------------------------------------------------------
1 | category Cat
2 | user information: "Info"
3 | {
4 | }
5 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/parser/bad-meta2.mal:
--------------------------------------------------------------------------------
1 | category Cat
2 | user info = "Info"
3 | {
4 | }
5 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/parser/bad-meta3.mal:
--------------------------------------------------------------------------------
1 | category Cat
2 | user info: Info
3 | {
4 | }
5 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/parser/categories.mal:
--------------------------------------------------------------------------------
1 | category C1 {
2 | asset A1 { }
3 | abstract asset A2 { }
4 | }
5 |
6 | category C2
7 | modeler info: "none"
8 | {
9 | asset A3 extends A1 { }
10 | abstract asset A4 extends A2 { }
11 | }
12 |
13 | category C3
14 | user info: "this is first C3"
15 | user info: "another info"
16 | developer info: "just to test"
17 | modeler info: "will not run through the analyzer"
18 | {
19 | }
20 |
21 | category C2
22 | {
23 | }
24 |
25 | category C3
26 | {
27 | }
28 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/parser/defines.mal:
--------------------------------------------------------------------------------
1 | #mal: "MAL"
2 |
3 | # hello: "Hello!"
4 |
5 | #
6 | def:
7 |
8 | "String\nLine"
9 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/parser/empty.mal:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mal-lang/malcompiler/b8bf5e803470974b2815862a64b300a18e0dde8b/malcompiler-test/src/test/resources/parser/empty.mal
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/parser/include.mal:
--------------------------------------------------------------------------------
1 | #a: "b"
2 |
3 | include "included1.mal"
4 |
5 | #b: "c"
6 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/parser/included1.mal:
--------------------------------------------------------------------------------
1 | #c: "d"
2 |
3 | include "subDir/subIncluded1.mal"
4 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/parser/included2.mal:
--------------------------------------------------------------------------------
1 | #e: "f"
2 |
3 | include "included1.mal"
4 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/parser/subDir/bad-included1.mal:
--------------------------------------------------------------------------------
1 | error
2 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/parser/subDir/subIncluded1.mal:
--------------------------------------------------------------------------------
1 | #d: "e"
2 |
3 | include "subIncluded1.mal"
4 | include "../included2.mal"
5 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/parser/to-string.ans:
--------------------------------------------------------------------------------
1 | defines = {
2 | Define(, ID(, "hej"), "hej"),
3 | Define(, ID(, "hej"), "hej")
4 | },
5 | categories = {
6 | Category(, ID(, "CAT"),
7 | meta = {
8 | Meta(, ID(, "user"), "meta")
9 | },
10 | assets = {
11 | Asset(, NOT_ABSTRACT, ID(, "A1"), PARENT(ID(, "A1")),
12 | meta = {
13 | },
14 | attacksteps = {
15 | },
16 | variables = {
17 | }
18 | ),
19 | Asset(, ABSTRACT, ID(, "A2"), NO_PARENT,
20 | meta = {
21 | Meta(, ID(, "user"), "info")
22 | },
23 | attacksteps = {
24 | AttackStep(, ALL, ID(, "at"),
25 | tags = {},
26 | cia = {},
27 | ttc = [],
28 | meta = {
29 | },
30 | Requires(,
31 | requires = {
32 | IDExpr(, ID(, "a"))
33 | }
34 | ),
35 | Reaches(, OVERRIDES,
36 | reaches = {
37 | IDExpr(, ID(, "a"))
38 | }
39 | )
40 | ),
41 | AttackStep(, ANY, ID(, "at2"),
42 | tags = {},
43 | cia = {},
44 | ttc = [TTCFuncExpr(, ID(, "a"))],
45 | meta = {
46 | Meta(, ID(, "user"), "at2")
47 | },
48 | NO_REQUIRES,
49 | Reaches(, INHERITS,
50 | reaches = {
51 | IDExpr(, ID(, "a")),
52 | IDExpr(, ID(, "b"))
53 | }
54 | )
55 | )
56 | },
57 | variables = {
58 | Variable(, ID(, "b"), IDExpr(, ID(, "a")))
59 | }
60 | )
61 | }
62 | )
63 | },
64 | associations = {
65 | Association(, ID(, "A1"), ID(, "a"), ZERO_OR_ONE, ID(, "L"), ONE_OR_MORE, ID(, "b"), ID(, "A2"),
66 | meta = {
67 | Meta(, ID(, "user"), "Info")
68 | }
69 | ),
70 | Association(, ID(, "A2"), ID(, "b"), ZERO_OR_MORE, ID(, "L"), ZERO_OR_MORE, ID(, "a"), ID(, "A1"),
71 | meta = {
72 | Meta(, ID(, "user"), "info"),
73 | Meta(, ID(, "user"), "info2")
74 | }
75 | ),
76 | Association(, ID(, "A1"), ID(, "a"), ONE, ID(, "L"), ONE, ID(, "b"), ID(, "A2"),
77 | meta = {
78 | }
79 | )
80 | }
81 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/parser/to-string.mal:
--------------------------------------------------------------------------------
1 | #hej: "hej"
2 |
3 | category CAT
4 | user info: "meta" {
5 | asset A1 extends A1 {
6 | }
7 | abstract asset A2
8 | user info: "info" {
9 | & at
10 | <- a
11 | -> a
12 | | at2 [a]
13 | user info: "at2"
14 | +> a, b
15 | let b = a
16 | }
17 | }
18 |
19 | # hej : "hej"
20 |
21 | associations {
22 | A1 [a] 0..1 <-- L --> 1..* [b] A2
23 | user info: "Info"
24 | A2 [b] 0..* <-- L --> * [a] A1
25 | user info: "info"
26 | user info: "info2"
27 | A1 [a] 1 <-- L --> 1..1 [b] A2
28 | }
29 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/parser/whitespace.mal:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/vehiclelang/vehicleLangEncryption.mal:
--------------------------------------------------------------------------------
1 | category Communication {
2 |
3 | asset CoreEncryptedData extends Data
4 | info: "Data is a concrete, syntactic representation of Information at rest."
5 | {
6 |
7 | & authenticatedRead
8 | info: "Access and authentication will allow reading of data."
9 | -> readEncrypted
10 |
11 | & authenticatedWrite
12 | info: "Access and authentication will allow writing of data."
13 | -> writeEncrypted
14 |
15 | & readEncrypted
16 | -> read
17 |
18 | & writeEncrypted
19 | -> write
20 |
21 | E decryptionKeysExist
22 | info: "If any decryption keys exist, this defense step is compromised, but if not, this will prevent readEncrypted from being reached."
23 | rationale: "Without this step, readEncrypted will, in the case of no modelled keys, be compromised, which is counterintuitive."
24 | <- decryptionKeys
25 | -> readEncrypted
26 |
27 | E encryptionKeysExist
28 | info: "If any encryption keys exist, this defense step is compromised, but if not, this will prevent witeEncrypted from being reached."
29 | rationale: "Without this step, writeEncrypted will, in the case of no modelled keys, be compromised, which is counterintuitive."
30 | <- encryptionKeys
31 | -> writeEncrypted
32 | }
33 | }
34 |
35 | category Security{
36 |
37 | asset CryptographicKey extends Data {
38 | | read
39 | -> decryptedData.readEncrypted,
40 | encryptedData.writeEncrypted
41 |
42 | }
43 |
44 | }
45 |
46 | associations {
47 | CoreEncryptedData [decryptedData] * <-- Decryption --> * [decryptionKeys] CryptographicKey
48 | CoreEncryptedData [encryptedData] * <-- Encryption --> * [encryptionKeys] CryptographicKey
49 | }
50 |
--------------------------------------------------------------------------------
/malcompiler-test/src/test/resources/vehiclelang/vehicleLangPublicInterfaces.mal:
--------------------------------------------------------------------------------
1 | category System {
2 |
3 | asset InfotainmentSystem extends Machine
4 | user info: "Represents the information & entertainment system found on all modern cars. It is a machine which can be connected to one or more networks."
5 | developer info: "It has the same functionality as a machine plus one additional attack step that is reached only when the NetworkAccessService is compromised."
6 | {
7 | | access
8 | developer info: "Adding one new connected attack step"
9 | +> engineerNetworkAccess
10 |
11 | | gainNetworkAccess
12 | user info: "If this attack step is reached then full network layer access is gained by the attacker."
13 | -> connectedNetworks.accessNetworkLayer
14 |
15 | | engineerNetworkAccess [Exponential(10.0)]
16 | user info: "This attack step is another way to reach full network access if there is no network access service on the infotainment system, but it requires effort!"
17 | -> connectedNetworks.accessNetworkLayer
18 | }
19 |
20 | asset NetworkAccessService extends NetworkService
21 | user info: "This service might run on an infotainment system and if compromised allows the attacker to access the networks connected to it."
22 | {
23 | | access
24 | +> executor.gainNetworkAccess
25 | }
26 | }
27 |
28 | category Communication {
29 |
30 | asset OBD2Connector
31 | user info: "Represents the OBD-II connector available in all modern cars and most vehicles in general."
32 | {
33 | | physicalAccess
34 | user info: "Physical access to the connector leads to access on the network layer."
35 | -> interfacingNetworks.accessNetworkLayer
36 |
37 | | connect
38 | -> bypassConnectorProtection,
39 | _connectNoProtection
40 |
41 | | bypassConnectorProtection [Exponential(20.0)]
42 | user info: "Remove or bypass objects blocking the OBD connector, for example ripping of protective plate or ganining access to driver cabin. Requires effort"
43 | -> physicalAccess
44 |
45 | & _connectNoProtection
46 | -> physicalAccess
47 |
48 | # connectorAccessProtection
49 | user info: "Any type of physical entity blocking attackers from physically connecting to the OBD-II port. For example a protective plate covering the port or port being placed where it's difficult to access."
50 | -> _connectNoProtection
51 | }
52 |
53 | asset ChargingPlugConnector
54 | user info: "The charging plug on many electric vehicles provides direct CAN bus access, while on others is only connected to the same network as the Battery Management System (BMS) ECU."
55 | developer info: "Florian Sagstetter, Security Challenges in Automotive Hardware/Software Architecture Design (2013)"
56 | {
57 | | physicalAccess
58 | user info: "No matter the case, physical access to the connector leads to access on the network layer of the connected network."
59 | -> connectedNetwork.accessNetworkLayer
60 | }
61 |
62 | asset AftermarketDongle
63 | user info: "An aftermarket dongle is a device that connects to the OBD-II port and provides some additional functionality to the vehicle's owner (e.g. error log reading, vehicle configuration, etc."
64 | {
65 | | connectDongle
66 | user info: "When a dongle is connected, the connect attack step is reached on OBD-II connector."
67 | -> _connectToNetwork
68 |
69 | & _connectToNetwork
70 | -> connector.connect
71 |
72 | # dongleIsHardened
73 | user info: "If the firmware on the connected dongle cannot be modified by the attacker, then access on the network layer cannto be achieved."
74 | developer info: "This defense might look more logical in the future where an external attacker will be able to use the dongle as an entry point in the vehicle."
75 | -> _connectToNetwork
76 | }
77 | }
78 |
79 | associations {
80 | VehicleNetwork [interfacingNetworks] * <-- Interface --> 0..1[connector] OBD2Connector
81 | VehicleNetwork [connectedNetwork] 0..1<-- Connections --> 0..1[chargingPlug] ChargingPlugConnector
82 | OBD2Connector [connector] 0..1<-- Connection --> 0..1[dongle] AftermarketDongle
83 | Network [connectedNetworks] * <-- Connection --> * [infotainment] InfotainmentSystem
84 | }
85 |
--------------------------------------------------------------------------------
/tools/git-hooks/install.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -eu
4 |
5 | cd "$(dirname "$0")/../.."
6 |
7 | repo_dir="$PWD"
8 | hooks_dir="$repo_dir/.git/hooks"
9 |
10 | echo_hook() {
11 | echo "#!/bin/sh"
12 | echo
13 | echo "set -eu"
14 | echo
15 | echo "cd \"\$(dirname \"\$0\")/../..\""
16 | echo
17 | echo "hook=\"\$(basename \"\$0\")\""
18 | echo "hook_dir=\"tools/git-hooks/\$hook.d\""
19 | echo
20 | echo "if [ -d \"\$hook_dir\" ]; then"
21 | echo " for script in \"\$hook_dir\"/*.sh; do"
22 | echo " if [ -x \"\$script\" ]; then"
23 | echo " echo \"Running \$hook hook \$(basename \"\$script\")\""
24 | echo " \"./\$script\" \"\$@\""
25 | echo " fi"
26 | echo " done"
27 | echo "fi"
28 | }
29 |
30 | main() {
31 | for hook in pre-commit prepare-commit-msg commit-msg post-commit; do
32 | hook_file="$hooks_dir/$hook"
33 | echo_hook >"$hook_file"
34 | chmod a+x "$hook_file"
35 | done
36 | }
37 |
38 | main
39 |
--------------------------------------------------------------------------------
/tools/git-hooks/pre-commit.d/format-java.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -eu
4 |
5 | cd "$(dirname "$0")/../../.."
6 |
7 | echo_changed_java() {
8 | set +e
9 | git diff --cached --name-only --diff-filter=ACMR | grep "\.java$"
10 | set -e
11 | }
12 |
13 | format_java() {
14 | # shellcheck source=../../scripts/google-java-format.sh
15 | . tools/scripts/google-java-format.sh
16 |
17 | latest_name="$(get_latest_name)"
18 | fmt_jar="$PWD/tools/$latest_name"
19 |
20 | if [ ! -f "$fmt_jar" ]; then
21 | echo "Downloading $latest_name"
22 | latest_url="$(get_latest_url)"
23 | wget -qO "$fmt_jar" "$latest_url"
24 | fi
25 |
26 | echo "Formatting $(echo "$changed_java" | wc -l) java files"
27 | echo "$changed_java" |
28 | tr "\n" "\0" |
29 | xargs -0 java \
30 | --add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \
31 | --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED \
32 | --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED \
33 | --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \
34 | --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED \
35 | -jar "$fmt_jar" \
36 | --replace
37 | echo "$changed_java" | tr "\n" "\0" | xargs -0 git add -f
38 | }
39 |
40 | main() {
41 | changed_java="$(echo_changed_java)"
42 | if [ -z "$changed_java" ]; then
43 | exit
44 | fi
45 | format_java
46 | }
47 |
48 | main
49 |
--------------------------------------------------------------------------------
/tools/git-hooks/pre-commit.d/format-pom.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -eu
4 |
5 | cd "$(dirname "$0")/../../.."
6 |
7 | echo_changed_pom() {
8 | set +e
9 | git diff --cached --name-only --diff-filter=ACMR | grep "pom\.xml$"
10 | set -e
11 | }
12 |
13 | format_pom() {
14 | # shellcheck source=../../scripts/utils.sh
15 | . tools/scripts/utils.sh
16 |
17 | echo "Formatting $(echo "$changed_pom" | wc -l) pom.xml files"
18 | mvn -B -q -Dincludes="$(comma_list "$changed_pom")" xml-format:xml-format tidy:pom
19 | # TODO: tidy:pom for every pom.xml?
20 | echo "$changed_pom" | tr "\n" "\0" | xargs -0 git add -f
21 | }
22 |
23 | main() {
24 | changed_pom="$(echo_changed_pom)"
25 | if [ -z "$changed_pom" ]; then
26 | exit
27 | fi
28 | format_pom
29 | }
30 |
31 | main
32 |
--------------------------------------------------------------------------------
/tools/git-hooks/pre-commit.d/format-xml.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -eu
4 |
5 | cd "$(dirname "$0")/../../.."
6 |
7 | echo_changed_xml() {
8 | set +e
9 | git diff --cached --name-only --diff-filter=ACMR | grep "\.xml$" | grep -v "pom\.xml$"
10 | set -e
11 | }
12 |
13 | format_xml() {
14 | # shellcheck source=../../scripts/utils.sh
15 | . tools/scripts/utils.sh
16 |
17 | echo "Formatting $(echo "$changed_xml" | wc -l) xml files"
18 | mvn -B -q -Dincludes="$(comma_list "$changed_xml")" xml-format:xml-format
19 | echo "$changed_xml" | tr "\n" "\0" | xargs -0 git add -f
20 | }
21 |
22 | main() {
23 | changed_xml="$(echo_changed_xml)"
24 | if [ -z "$changed_xml" ]; then
25 | exit
26 | fi
27 | format_xml
28 | }
29 |
30 | main
31 |
--------------------------------------------------------------------------------
/tools/scripts/google-java-format.sh:
--------------------------------------------------------------------------------
1 | # shellcheck shell=sh
2 |
3 | get_latest_assets() {
4 | curl -fLSs "https://api.github.com/repos/google/google-java-format/releases/latest" | jq -c .assets[]
5 | }
6 |
7 | get_latest_asset() {
8 | get_latest_assets | while IFS= read -r asset; do
9 | if echo "$asset" | jq -r .name | grep -q "^google-java-format-.*-all-deps\.jar$"; then
10 | echo "$asset"
11 | fi
12 | done
13 | }
14 |
15 | get_latest_name() {
16 | get_latest_asset | jq -r .name
17 | }
18 |
19 | get_latest_url() {
20 | get_latest_asset | jq -r .browser_download_url
21 | }
22 |
--------------------------------------------------------------------------------
/tools/scripts/utils.sh:
--------------------------------------------------------------------------------
1 | # shellcheck shell=sh
2 |
3 | comma_list() {
4 | echo "$1" | sed "/^[[:space:]]*$/d" | tr "\n" "," | sed "s/,$/\n/g"
5 | }
6 |
--------------------------------------------------------------------------------
/update_mal/new-spec.mal:
--------------------------------------------------------------------------------
1 | #id: "tmp"
2 | #version: "0.0.0"
3 |
4 | // Unicode: 🙃
5 |
6 | include "a"
7 | include "a // not a comment"
8 | include "a.b"
9 | include "a b"
10 | include "a/b"
11 | include "a\b"
12 |
13 | category C1 {
14 | }
15 |
16 | category C2
17 | user info: "info"
18 | {
19 | asset A1 {
20 | & At1
21 | | At2
22 | # At3
23 | !E At4
24 | E At5
25 | }
26 |
27 | asset A2
28 | user info: "info"
29 | {
30 | & At1 [Bernoulli(0.7)]
31 | & At2 [Binomial(0.5, 0.6)]
32 | & At3 [Exponential(0.1)]
33 | & At4 [Gamma(0.1, 0.2)]
34 | & At5 [Infinity]
35 | & At6 [LogNormal(1, 3)]
36 | & At7 [Normal(1.1, 2.0)]
37 | & At8 [Pareto(1.0, 0)]
38 | & At9 [TruncatedNormal(1.2, 1.3)]
39 | & At10 [Uniform(0, 10)]
40 | & At11 [Zero]
41 | }
42 |
43 | asset A3
44 | developer info: "rationale"
45 | {
46 | & At1
47 | & At2
48 | user info: "info"
49 | & At3
50 | developer info: "rationale"
51 | & At4
52 | modeler info: "assumptions"
53 | & At5
54 | user info: "info"
55 | modeler info: "assumptions"
56 | & At6
57 | user info: "info"
58 | developer info: "rationale"
59 | modeler info: "assumptions"
60 | }
61 |
62 | asset A4
63 | modeler info: "assumptions"
64 | {
65 | & A1
66 | <- A
67 | & A2
68 | <- A, B
69 | }
70 |
71 | asset A5
72 | user info: "info"
73 | modeler info: "assumptions"
74 | {
75 | & A1
76 | -> let v1 = a,
77 | a.b,
78 | (a),
79 | (a)[t],
80 | a /\ b,
81 | a \/ b,
82 | a /\ b \/ c,
83 | a[t],
84 | a*,
85 | a[t]*
86 | & A2
87 | +> a
88 | }
89 |
90 | asset A6
91 | user info: "info"
92 | developer info: "rationale"
93 | modeler info: "assumptions"
94 | {
95 | }
96 | }
97 |
98 | category C3
99 | developer info: "rationale"
100 | {
101 | asset A1 extends A1 {
102 | }
103 | }
104 |
105 | category C4
106 | modeler info: "assumptions"
107 | {
108 | abstract asset A1 {
109 | }
110 | }
111 |
112 | category C5
113 | user info: "info"
114 | modeler info: "assumptions"
115 | {
116 | asset distribution {
117 | }
118 | }
119 |
120 | category C6
121 | user info: "info"
122 | developer info: "rationale"
123 | modeler info: "assumptions"
124 | {
125 | }
126 |
127 | associations {
128 | I1 [t1] 1 <-- I2 --> 0..1 [t2] I3
129 | I4 [t3] 1..* <-- I5 --> * [t4] I6
130 | }
131 |
132 | assocations {
133 | A [a] 1 <-- A --> 1 [a] A
134 | user info: "info"
135 | A [a] 1 <-- A --> 1 [a] A
136 | developer info: "rationale"
137 | A [a] 1 <-- A --> 1 [a] A
138 | modeler info: "assumptions"
139 | A [a] 1 <-- A --> 1 [a] A
140 | user info: "info"
141 | modeler info: "assumptions"
142 | A [a] 1 <-- A --> 1 [a] A
143 | user info: "info"
144 | developer info: "rationale"
145 | modeler info: "assumptions"
146 | }
147 |
148 | // Comment
149 |
--------------------------------------------------------------------------------
/update_mal/old-spec.mal:
--------------------------------------------------------------------------------
1 | #id: "tmp"
2 | #version: "0.0.0"
3 |
4 | // Unicode: 🙃
5 |
6 | include a
7 | include a // not a comment
8 | include a.b
9 | include a b
10 | include a/b
11 | include a\b
12 |
13 | category C1 {
14 | }
15 |
16 | category C2
17 | info: "info"
18 | {
19 | asset A1 {
20 | & At1
21 | | At2
22 | # At3
23 | 3 At4
24 | E At5
25 | }
26 |
27 | asset A2
28 | info: "info"
29 | {
30 | & At1 [BernoulliDistribution(0.7)]
31 | & At2 [BinomialDistribution(0.5, 0.6)]
32 | & At3 [ExponentialDistribution(0.1)]
33 | & At4 [GammaDistribution(0.1, 0.2)]
34 | & At5 [Infinity]
35 | & At6 [LogNormalDistribution(1, 3)]
36 | & At7 [NormalDistribution(1.1, 2.0)]
37 | & At8 [ParetoDistribution(1.0, 0)]
38 | & At9 [TruncatedNormalDistribution(1.2, 1.3)]
39 | & At10 [UniformDistribution(0, 10)]
40 | & At11 [Zero]
41 | }
42 |
43 | asset A3
44 | rationale: "rationale"
45 | {
46 | & At1
47 | & At2
48 | info: "info"
49 | & At3
50 | rationale: "rationale"
51 | & At4
52 | assumptions: "assumptions"
53 | & At5
54 | info: "info"
55 | assumptions: "assumptions"
56 | & At6
57 | info: "info"
58 | rationale: "rationale"
59 | assumptions: "assumptions"
60 | }
61 |
62 | asset A4
63 | assumptions: "assumptions"
64 | {
65 | & A1
66 | <- A
67 | & A2
68 | <- A, B
69 | }
70 |
71 | asset A5
72 | info: "info"
73 | assumptions: "assumptions"
74 | {
75 | & A1
76 | -> let v1 = a,
77 | a.b,
78 | (a),
79 | (a)[t],
80 | a /\ b,
81 | a \/ b,
82 | a /\ b \/ c,
83 | a[t],
84 | a+,
85 | a[t]+
86 | & A2
87 | +> a
88 | }
89 |
90 | asset A6
91 | info: "info"
92 | rationale: "rationale"
93 | assumptions: "assumptions"
94 | {
95 | }
96 | }
97 |
98 | category C3
99 | rationale: "rationale"
100 | {
101 | asset A1 extends A1 {
102 | }
103 | }
104 |
105 | category C4
106 | assumptions: "assumptions"
107 | {
108 | abstractAsset A1 {
109 | }
110 | }
111 |
112 | category C5
113 | info: "info"
114 | assumptions: "assumptions"
115 | {
116 | asset distribution {
117 | }
118 | }
119 |
120 | category C6
121 | info: "info"
122 | rationale: "rationale"
123 | assumptions: "assumptions"
124 | {
125 | }
126 |
127 | associations {
128 | I1 [t1] 1 <-- I2 --> 0-1 [t2] I3
129 | I4 [t3] 1-* <-- I5 --> * [t4] I6
130 | }
131 |
132 | assocations {
133 | A [a] 1 <-- A --> 1 [a] A
134 | info: "info"
135 | A [a] 1 <-- A --> 1 [a] A
136 | rationale: "rationale"
137 | A [a] 1 <-- A --> 1 [a] A
138 | assumptions: "assumptions"
139 | A [a] 1 <-- A --> 1 [a] A
140 | info: "info"
141 | assumptions: "assumptions"
142 | A [a] 1 <-- A --> 1 [a] A
143 | info: "info"
144 | rationale: "rationale"
145 | assumptions: "assumptions"
146 | }
147 |
148 | // Comment
149 |
--------------------------------------------------------------------------------
/update_mal/run-tests.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | OLD_MAL="old-spec.mal"
4 | NEW_MAL="new-spec.mal"
5 | TMP_MAL="tmp-spec.mal"
6 | RUN_UPDATE="python3 update_mal.py"
7 | FAIL="0"
8 |
9 | function run_test {
10 | CMD="$1"
11 | eval "$CMD"
12 | if [[ "$(diff "$NEW_MAL" "$TMP_MAL")" != "" ]]; then
13 | >&2 echo "Error: \"$CMD\" failed"
14 | FAIL="1"
15 | fi
16 | rm -f "$TMP_MAL"
17 | }
18 |
19 | CWD="$(dirname "$(realpath "$0")")"
20 | cd "$CWD"
21 |
22 | # Test input from file, output to file
23 | run_test "$RUN_UPDATE -i $OLD_MAL -o $TMP_MAL"
24 |
25 | # Test input from file, output to stdout
26 | run_test "$RUN_UPDATE -i $OLD_MAL -o - > $TMP_MAL"
27 |
28 | # Test input from stdin, output to file
29 | run_test "$RUN_UPDATE -i - -o $TMP_MAL < $OLD_MAL"
30 |
31 | # Test input from stdin, output to stdout
32 | run_test "$RUN_UPDATE -i - -o - < $OLD_MAL > $TMP_MAL"
33 |
34 | if [[ "$FAIL" == "1" ]]; then
35 | exit 1
36 | fi
37 |
--------------------------------------------------------------------------------