From 543e53b9efe1272a3c512bd579e35f2382dca65e Mon Sep 17 00:00:00 2001 From: Immanuel Date: Fri, 26 Jul 2019 15:48:05 +0200 Subject: [PATCH 01/13] fixed a bug in TimeComparison; introduced new time operator --- clouditor-engine-core/src/main/antlr/CCL.g4 | 6 +++++- .../assurance/ccl/TimeComparison.java | 20 ++++++++++++------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/clouditor-engine-core/src/main/antlr/CCL.g4 b/clouditor-engine-core/src/main/antlr/CCL.g4 index 0436143e7..ec8160bdf 100644 --- a/clouditor-engine-core/src/main/antlr/CCL.g4 +++ b/clouditor-engine-core/src/main/antlr/CCL.g4 @@ -26,7 +26,9 @@ binaryComparison: field operator value; timeComparison: field timeOperator (time unit | nowOperator); timeOperator: BeforeOperator | - AfterOperator; + AfterOperator | + YoungerOperator | + OlderOperator; nowOperator: 'now'; time: Number; unit: @@ -66,6 +68,8 @@ ContainsOperator: 'contains'; BeforeOperator: 'before'; AfterOperator: 'after'; +YoungerOperator: 'younger'; +OlderOperator: 'older'; BooleanLiteral: True | diff --git a/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/TimeComparison.java b/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/TimeComparison.java index 9f18ef4da..00e870e99 100644 --- a/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/TimeComparison.java +++ b/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/TimeComparison.java @@ -29,6 +29,7 @@ package io.clouditor.assurance.ccl; +import java.time.DateTimeException; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Map; @@ -52,22 +53,25 @@ public boolean evaluate(Map properties) { if (fieldValue instanceof Long) { instant = Instant.ofEpochSecond((Long) fieldValue); - } else { + } else if ((fieldValue instanceof Map) && ((Map) fieldValue).get("epochSecond") != null) { // field values are not really instants but serialized Instant in the form of epochSecond // and nano, we need to re-create the Instant - if (!(fieldValue instanceof Map) || ((Map) fieldValue).get("epochSecond") == null) { - return false; - } instant = Instant.ofEpochSecond( ((Number) ((Map) fieldValue).get("epochSecond")).longValue(), ((Number) ((Map) fieldValue).get("nano")).longValue()); + } else { + try { + instant = Instant.parse((String) fieldValue); + } catch (ClassCastException e) { + return false; + } } - var value = Instant.now().plus(relativeValue, timeUnit); + var value = Instant.now().minus(relativeValue, timeUnit); - if (this.timeOperator == TimeOperator.AFTER) { + if (this.timeOperator == TimeOperator.BEFORE || this.timeOperator == TimeOperator.YOUNGER) { return instant.isAfter(value); } else { return instant.isBefore(value); @@ -96,6 +100,8 @@ public void setTimeOperator(TimeOperator timeOperator) { public enum TimeOperator { BEFORE, - AFTER + AFTER, + YOUNGER, + OLDER } } From cbb112142b7d62dc0d204268da74f02e2037f579 Mon Sep 17 00:00:00 2001 From: Immanuel Date: Tue, 6 Aug 2019 16:33:37 +0200 Subject: [PATCH 02/13] CCL rework; allows to apply rules to asset groups --- .../aws/iam/access-key-rotation-admin.md | 13 +++++++ clouditor-engine-core/src/main/antlr/CCL.g4 | 17 ++++++--- .../java/io/clouditor/assurance/Rule.java | 28 +++++++++++---- .../io/clouditor/assurance/RuleService.java | 9 +++-- .../io/clouditor/assurance/ccl/AssetType.java | 35 +++++++++++++++++++ .../assurance/ccl/CCLDeserializer.java | 29 ++++++++++++++- .../io/clouditor/assurance/ccl/Condition.java | 6 ++-- .../clouditor/assurance/ccl/GroupedAsset.java | 26 ++++++++++++++ .../clouditor/assurance/ccl/SimpleAsset.java | 21 +++++++++++ .../assurance/ccl/TimeComparison.java | 1 - 10 files changed, 166 insertions(+), 19 deletions(-) create mode 100644 clouditor-engine-aws/src/main/resources/rules/aws/iam/access-key-rotation-admin.md create mode 100644 clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/AssetType.java create mode 100644 clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/GroupedAsset.java create mode 100644 clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/SimpleAsset.java diff --git a/clouditor-engine-aws/src/main/resources/rules/aws/iam/access-key-rotation-admin.md b/clouditor-engine-aws/src/main/resources/rules/aws/iam/access-key-rotation-admin.md new file mode 100644 index 000000000..85b5f1337 --- /dev/null +++ b/clouditor-engine-aws/src/main/resources/rules/aws/iam/access-key-rotation-admin.md @@ -0,0 +1,13 @@ +# Rotate Access Keys Regularly admin + +Checks if AWS access keys are rotated regularly and are not older than a specified amount of time, i.e. 1000 days. + +```ccl +User with userName=="admin" has createDate younger 90 days in all accessKeys +``` + +## Controls + +* "IAM-02" +* "AWS 1.4" +* "BSI C5/KRY-04" diff --git a/clouditor-engine-core/src/main/antlr/CCL.g4 b/clouditor-engine-core/src/main/antlr/CCL.g4 index ec8160bdf..ca929bcd2 100644 --- a/clouditor-engine-core/src/main/antlr/CCL.g4 +++ b/clouditor-engine-core/src/main/antlr/CCL.g4 @@ -2,18 +2,25 @@ grammar CCL; condition: assetType 'has' expression EOF; -assetType : Identifier; +assetType : + simpleAsset | + groupedAsset; + +simpleAsset: field; + +groupedAsset: field 'with' expression; + field : Identifier; expression: simpleExpression | - inExpression ; + notExpression | + inExpression; simpleExpression: - '(' expression ')' | - notExpression | emptyExpression | withinExpression | - comparison; + comparison | + '(' expression ')' ; notExpression: 'not' expression; diff --git a/clouditor-engine-core/src/main/java/io/clouditor/assurance/Rule.java b/clouditor-engine-core/src/main/java/io/clouditor/assurance/Rule.java index 1d93c2ebe..d7bb51046 100644 --- a/clouditor-engine-core/src/main/java/io/clouditor/assurance/Rule.java +++ b/clouditor-engine-core/src/main/java/io/clouditor/assurance/Rule.java @@ -32,9 +32,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.clouditor.assurance.ccl.CCLDeserializer; -import io.clouditor.assurance.ccl.CCLSerializer; -import io.clouditor.assurance.ccl.Condition; +import io.clouditor.assurance.ccl.*; import io.clouditor.discovery.Asset; import java.util.ArrayList; import java.util.Collections; @@ -59,6 +57,20 @@ public class Rule { @JsonProperty private List controls = new ArrayList<>(); @JsonProperty private String id; + public boolean evaluateApplicability(Asset asset) { + + if (this.condition != null) { + if (this.condition.getAssetType() instanceof GroupedAsset) { + return this.condition.getAssetType().evaluate(asset.getProperties()); + } + } else if (this.conditions != null) { + if (this.conditions.get(0).getAssetType() instanceof GroupedAsset) { + return this.conditions.get(0).getAssetType().evaluate(asset.getProperties()); + } + } + return true; + } + public EvaluationResult evaluate(Asset asset) { var eval = new EvaluationResult(this, asset.getProperties()); @@ -83,14 +95,16 @@ public void setCondition(Condition condition) { public String getAssetType() { // single condition - if (this.condition != null) { - return this.condition.getAssetType(); + if (this.condition != null && this.condition.getAssetType() != null) { + return this.condition.getAssetType().getField(); } // multiple conditions - if (this.conditions != null && !this.conditions.isEmpty()) { + if (this.conditions != null + && !this.conditions.isEmpty() + && this.conditions.get(0).getAssetType() != null) { // take the first one - return this.conditions.get(0).getAssetType(); + return this.conditions.get(0).getAssetType().getField(); } // no asset type found, we cannot really use this rule then diff --git a/clouditor-engine-core/src/main/java/io/clouditor/assurance/RuleService.java b/clouditor-engine-core/src/main/java/io/clouditor/assurance/RuleService.java index 2611b0b26..f123bf646 100644 --- a/clouditor-engine-core/src/main/java/io/clouditor/assurance/RuleService.java +++ b/clouditor-engine-core/src/main/java/io/clouditor/assurance/RuleService.java @@ -285,8 +285,13 @@ public void handle(DiscoveryResult result) { // evaluate all rules rulesForAsset.forEach( rule -> { - var eval = rule.evaluate(asset); - + EvaluationResult eval; + if (!rule.evaluateApplicability(asset)) { + // simply add an empty EvaluationResult + eval = new EvaluationResult(rule, asset.getProperties()); + } else { + eval = rule.evaluate(asset); + } // TODO: can we really update the asset? asset.addEvaluationResult(eval); if (!eval.isOk()) { diff --git a/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/AssetType.java b/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/AssetType.java new file mode 100644 index 000000000..d8c328cae --- /dev/null +++ b/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/AssetType.java @@ -0,0 +1,35 @@ +package io.clouditor.assurance.ccl; + +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import java.util.Map; + +@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "@class") +public abstract class AssetType { + + public abstract boolean evaluate(Map properties); + + public abstract String getField(); + + Object getValueFromField(Map asset, String fieldName) { + // first, try to resolve it directly + if (asset.containsKey(fieldName)) { + return asset.get(fieldName); + } + + // split it at . + var names = fieldName.split("\\."); + + var base = asset; + var value = (Object) null; + + for (var name : names) { + value = base.get(name); + + if (value instanceof Map) { + base = (Map) value; + } + } + + return value; + } +} diff --git a/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/CCLDeserializer.java b/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/CCLDeserializer.java index 2317ccd60..3e10ce975 100644 --- a/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/CCLDeserializer.java +++ b/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/CCLDeserializer.java @@ -74,7 +74,7 @@ public Condition visitCondition(ConditionContext ctx) { if (ctx.assetType() != null && ctx.expression() != null) { var condition = new Condition(); - condition.setAssetType(ctx.assetType().getText()); + condition.setAssetType(ctx.assetType().accept(new AssetTypeListener())); condition.setExpression(ctx.expression().accept(new ExpressionListener())); return condition; @@ -84,6 +84,33 @@ public Condition visitCondition(ConditionContext ctx) { } } + private static class AssetTypeListener extends CCLBaseVisitor { + + @Override + public AssetType visitAssetType(CCLParser.AssetTypeContext ctx) { + if (ctx.simpleAsset() != null) { + var simpleAsset = new SimpleAsset(); + simpleAsset.setField(ctx.simpleAsset().field().getText()); + return simpleAsset; + } else if (ctx.groupedAsset() != null) { + return ctx.groupedAsset().accept(this); + } + + return super.visitAssetType(ctx); + } + + @Override + public AssetType visitGroupedAsset(CCLParser.GroupedAssetContext ctx) { + if (ctx.expression() != null) { + var asset = new GroupedAsset(); + asset.setField(ctx.field().getText()); + asset.setAssetExpression(ctx.expression().accept(new ExpressionListener())); + return asset; + } + return super.visitGroupedAsset(ctx); + } + } + private static class ExpressionListener extends CCLBaseVisitor { @Override diff --git a/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/Condition.java b/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/Condition.java index 55b548e7c..33b571433 100644 --- a/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/Condition.java +++ b/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/Condition.java @@ -33,7 +33,7 @@ public class Condition { - private String assetType; + private AssetType assetType; private Expression expression; @@ -47,11 +47,11 @@ public void setExpression(Expression expression) { this.expression = expression; } - public String getAssetType() { + public AssetType getAssetType() { return assetType; } - public void setAssetType(String assetType) { + public void setAssetType(AssetType assetType) { this.assetType = assetType; } diff --git a/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/GroupedAsset.java b/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/GroupedAsset.java new file mode 100644 index 000000000..90dda0249 --- /dev/null +++ b/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/GroupedAsset.java @@ -0,0 +1,26 @@ +package io.clouditor.assurance.ccl; + +import java.util.Map; + +public class GroupedAsset extends AssetType { + + private String field; + private Expression assetExpression; + + public String getField() { + return this.field; + } + + public void setField(String field) { + this.field = field; + } + + public void setAssetExpression(Expression assetExpression) { + this.assetExpression = assetExpression; + } + + @Override + public boolean evaluate(Map properties) { + return this.assetExpression.evaluate(properties); + } +} diff --git a/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/SimpleAsset.java b/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/SimpleAsset.java new file mode 100644 index 000000000..2a88ea574 --- /dev/null +++ b/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/SimpleAsset.java @@ -0,0 +1,21 @@ +package io.clouditor.assurance.ccl; + +import java.util.Map; + +public class SimpleAsset extends AssetType { + + private String field; + + public String getField() { + return this.field; + } + + public void setField(String field) { + this.field = field; + } + + @Override + public boolean evaluate(Map properties) { + return true; + } +} diff --git a/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/TimeComparison.java b/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/TimeComparison.java index 00e870e99..c4d6479dd 100644 --- a/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/TimeComparison.java +++ b/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/TimeComparison.java @@ -29,7 +29,6 @@ package io.clouditor.assurance.ccl; -import java.time.DateTimeException; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Map; From 243893700fdf6ac39eace12953aac17cfda8d59c Mon Sep 17 00:00:00 2001 From: Immanuel Date: Wed, 7 Aug 2019 15:00:50 +0200 Subject: [PATCH 03/13] fixed tests; reworked time comparison --- .../resources/rules/aws/iam/access-key-rotation.md | 2 +- .../discovery/aws/AwsIamUserScannerTest.java | 2 +- .../io/clouditor/assurance/ccl/TimeComparison.java | 13 +++++++++++-- .../io/clouditor/assurance/CCLDeserializerTest.java | 8 ++------ 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/clouditor-engine-aws/src/main/resources/rules/aws/iam/access-key-rotation.md b/clouditor-engine-aws/src/main/resources/rules/aws/iam/access-key-rotation.md index 7b9070391..b902c8f8d 100644 --- a/clouditor-engine-aws/src/main/resources/rules/aws/iam/access-key-rotation.md +++ b/clouditor-engine-aws/src/main/resources/rules/aws/iam/access-key-rotation.md @@ -3,7 +3,7 @@ Checks if AWS access keys are rotated regularly and are not older than a specified amount of time, i.e. 90 days. ```ccl -User has createDate after 90 days in all accessKeys +User has createDate younger 90 days in all accessKeys ``` ## Controls diff --git a/clouditor-engine-aws/src/test/java/io/clouditor/discovery/aws/AwsIamUserScannerTest.java b/clouditor-engine-aws/src/test/java/io/clouditor/discovery/aws/AwsIamUserScannerTest.java index 305f227f5..9269cdfd5 100644 --- a/clouditor-engine-aws/src/test/java/io/clouditor/discovery/aws/AwsIamUserScannerTest.java +++ b/clouditor-engine-aws/src/test/java/io/clouditor/discovery/aws/AwsIamUserScannerTest.java @@ -184,7 +184,7 @@ void testAccessKeyRotation() throws IOException { FileSystemManager.getInstance() .getPathForResource("rules/aws/iam/access-key-rotation.md")); - // user2 has an very old access key + // user2 has a very old access key assertFalse(rule.evaluate(assets.get(USER2_ARN)).isOk()); } diff --git a/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/TimeComparison.java b/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/TimeComparison.java index c4d6479dd..40da935de 100644 --- a/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/TimeComparison.java +++ b/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/TimeComparison.java @@ -50,6 +50,10 @@ public boolean evaluate(Map properties) { Instant instant; + if (fieldValue == null) { + return false; + } + if (fieldValue instanceof Long) { instant = Instant.ofEpochSecond((Long) fieldValue); } else if ((fieldValue instanceof Map) && ((Map) fieldValue).get("epochSecond") != null) { @@ -68,9 +72,14 @@ public boolean evaluate(Map properties) { } } - var value = Instant.now().minus(relativeValue, timeUnit); + Instant value; + if (this.timeOperator == TimeOperator.BEFORE || this.timeOperator == TimeOperator.AFTER) { + value = Instant.now().plus(relativeValue, timeUnit); + } else { + value = Instant.now().minus(relativeValue, timeUnit); + } - if (this.timeOperator == TimeOperator.BEFORE || this.timeOperator == TimeOperator.YOUNGER) { + if (this.timeOperator == TimeOperator.YOUNGER || this.timeOperator == TimeOperator.AFTER) { return instant.isAfter(value); } else { return instant.isBefore(value); diff --git a/clouditor-engine-core/src/test/java/io/clouditor/assurance/CCLDeserializerTest.java b/clouditor-engine-core/src/test/java/io/clouditor/assurance/CCLDeserializerTest.java index 0c54f5a80..6bbc5d4a0 100644 --- a/clouditor-engine-core/src/test/java/io/clouditor/assurance/CCLDeserializerTest.java +++ b/clouditor-engine-core/src/test/java/io/clouditor/assurance/CCLDeserializerTest.java @@ -129,7 +129,7 @@ void testInExpression() { @Test void testIsBeforeComparison() { var ccl = new CCLDeserializer(); - var condition = ccl.parse("User has createDate before now"); + var condition = ccl.parse("User has createDate older 10 days"); var asset = new AssetProperties(); asset.put("createDate", Map.of("epochSecond", 1550131042, "nano", 1)); @@ -142,10 +142,6 @@ void testIsBeforeComparison() { assertFalse(condition.evaluate(asset)); - condition = ccl.parse("User has createDate after now"); - - assertTrue(condition.evaluate(asset)); - // something older than 90 days asset.put( "createDate", @@ -153,7 +149,7 @@ void testIsBeforeComparison() { "epochSecond", Instant.now().minus(100, ChronoUnit.DAYS).getEpochSecond(), "nano", 0)); // user create date should not be older than 90 days - condition = ccl.parse("User has not createDate before 90 days"); + condition = ccl.parse("User has createDate younger 90 days"); assertFalse(condition.evaluate(asset)); } From d6db28042db44629e320d90354936682d4d67fdb Mon Sep 17 00:00:00 2001 From: Immanuel Date: Tue, 13 Aug 2019 09:22:45 +0200 Subject: [PATCH 04/13] added time comparison tests --- .../assurance/CCLDeserializerTest.java | 61 ++++++++++++++++--- 1 file changed, 51 insertions(+), 10 deletions(-) diff --git a/clouditor-engine-core/src/test/java/io/clouditor/assurance/CCLDeserializerTest.java b/clouditor-engine-core/src/test/java/io/clouditor/assurance/CCLDeserializerTest.java index 6bbc5d4a0..56b49d5da 100644 --- a/clouditor-engine-core/src/test/java/io/clouditor/assurance/CCLDeserializerTest.java +++ b/clouditor-engine-core/src/test/java/io/clouditor/assurance/CCLDeserializerTest.java @@ -129,27 +129,68 @@ void testInExpression() { @Test void testIsBeforeComparison() { var ccl = new CCLDeserializer(); - var condition = ccl.parse("User has createDate older 10 days"); + var condition = ccl.parse("AccessKey has expiry before 10 days"); + + var asset = new AssetProperties(); + asset.put("expiry", Map.of("epochSecond", Instant.now().getEpochSecond(), "nano", 1)); + + assertTrue(condition.evaluate(asset)); + + asset.clear(); + + asset.put("expiry", Map.of("epochSecond", Instant.now().plus(20, ChronoUnit.DAYS).getEpochSecond(), "nano", 1)); + + assertFalse(condition.evaluate(asset)); + } + + @Test + void testAfterComparison() { + var ccl = new CCLDeserializer(); + var condition = ccl.parse("AccessKey has expiry after 10 days"); + + var asset = new AssetProperties(); + asset.put("expiry", Map.of("epochSecond", Instant.now().plus(20, ChronoUnit.DAYS).getEpochSecond(), "nano", 1)); + + assertTrue(condition.evaluate(asset)); + + asset.clear(); + + asset.put("expiry", Map.of("epochSecond", Instant.now().getEpochSecond(), "nano", 1)); + + assertFalse(condition.evaluate(asset)); + } + + @Test + void testYoungerComparison() { + var ccl = new CCLDeserializer(); + var condition = ccl.parse("User has createDate younger 10 days"); var asset = new AssetProperties(); - asset.put("createDate", Map.of("epochSecond", 1550131042, "nano", 1)); + asset.put("createDate", Map.of("epochSecond", Instant.now().getEpochSecond(), "nano", 1)); assertTrue(condition.evaluate(asset)); asset.clear(); // something in the future - asset.put("createDate", Map.of("epochSecond", Integer.MAX_VALUE, "nano", 1)); + asset.put("createDate", Map.of("epochSecond", Instant.now().minus(20, ChronoUnit.DAYS).getEpochSecond(), "nano", 1)); assertFalse(condition.evaluate(asset)); + } - // something older than 90 days - asset.put( - "createDate", - Map.of( - "epochSecond", Instant.now().minus(100, ChronoUnit.DAYS).getEpochSecond(), "nano", 0)); + @Test + void testOlderComparison() { + var ccl = new CCLDeserializer(); + var condition = ccl.parse("User has createDate older 10 days"); + + var asset = new AssetProperties(); + + asset.put("createDate", Map.of("epochSecond", Instant.now().minus(20, ChronoUnit.DAYS).getEpochSecond(), "nano", 1)); + + assertTrue(condition.evaluate(asset)); + + asset.clear(); - // user create date should not be older than 90 days - condition = ccl.parse("User has createDate younger 90 days"); + asset.put("createDate", Map.of("epochSecond", Instant.now().getEpochSecond(), "nano", 1)); assertFalse(condition.evaluate(asset)); } From e92c42c71f6601ff3482735911621520ac74a6d4 Mon Sep 17 00:00:00 2001 From: Immanuel Date: Tue, 13 Aug 2019 09:28:18 +0200 Subject: [PATCH 05/13] spotless --- .../assurance/CCLDeserializerTest.java | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/clouditor-engine-core/src/test/java/io/clouditor/assurance/CCLDeserializerTest.java b/clouditor-engine-core/src/test/java/io/clouditor/assurance/CCLDeserializerTest.java index 56b49d5da..a7c285b44 100644 --- a/clouditor-engine-core/src/test/java/io/clouditor/assurance/CCLDeserializerTest.java +++ b/clouditor-engine-core/src/test/java/io/clouditor/assurance/CCLDeserializerTest.java @@ -138,7 +138,9 @@ void testIsBeforeComparison() { asset.clear(); - asset.put("expiry", Map.of("epochSecond", Instant.now().plus(20, ChronoUnit.DAYS).getEpochSecond(), "nano", 1)); + asset.put( + "expiry", + Map.of("epochSecond", Instant.now().plus(20, ChronoUnit.DAYS).getEpochSecond(), "nano", 1)); assertFalse(condition.evaluate(asset)); } @@ -149,7 +151,9 @@ void testAfterComparison() { var condition = ccl.parse("AccessKey has expiry after 10 days"); var asset = new AssetProperties(); - asset.put("expiry", Map.of("epochSecond", Instant.now().plus(20, ChronoUnit.DAYS).getEpochSecond(), "nano", 1)); + asset.put( + "expiry", + Map.of("epochSecond", Instant.now().plus(20, ChronoUnit.DAYS).getEpochSecond(), "nano", 1)); assertTrue(condition.evaluate(asset)); @@ -172,7 +176,10 @@ void testYoungerComparison() { asset.clear(); // something in the future - asset.put("createDate", Map.of("epochSecond", Instant.now().minus(20, ChronoUnit.DAYS).getEpochSecond(), "nano", 1)); + asset.put( + "createDate", + Map.of( + "epochSecond", Instant.now().minus(20, ChronoUnit.DAYS).getEpochSecond(), "nano", 1)); assertFalse(condition.evaluate(asset)); } @@ -184,7 +191,10 @@ void testOlderComparison() { var asset = new AssetProperties(); - asset.put("createDate", Map.of("epochSecond", Instant.now().minus(20, ChronoUnit.DAYS).getEpochSecond(), "nano", 1)); + asset.put( + "createDate", + Map.of( + "epochSecond", Instant.now().minus(20, ChronoUnit.DAYS).getEpochSecond(), "nano", 1)); assertTrue(condition.evaluate(asset)); From be5f0c7350d693401c2923131c73830d4e2cb206 Mon Sep 17 00:00:00 2001 From: Immanuel Date: Fri, 26 Jul 2019 15:48:05 +0200 Subject: [PATCH 06/13] fixed a bug in TimeComparison; introduced new time operator --- clouditor-engine-core/src/main/antlr/CCL.g4 | 6 +++++- .../assurance/ccl/TimeComparison.java | 20 ++++++++++++------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/clouditor-engine-core/src/main/antlr/CCL.g4 b/clouditor-engine-core/src/main/antlr/CCL.g4 index 0436143e7..ec8160bdf 100644 --- a/clouditor-engine-core/src/main/antlr/CCL.g4 +++ b/clouditor-engine-core/src/main/antlr/CCL.g4 @@ -26,7 +26,9 @@ binaryComparison: field operator value; timeComparison: field timeOperator (time unit | nowOperator); timeOperator: BeforeOperator | - AfterOperator; + AfterOperator | + YoungerOperator | + OlderOperator; nowOperator: 'now'; time: Number; unit: @@ -66,6 +68,8 @@ ContainsOperator: 'contains'; BeforeOperator: 'before'; AfterOperator: 'after'; +YoungerOperator: 'younger'; +OlderOperator: 'older'; BooleanLiteral: True | diff --git a/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/TimeComparison.java b/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/TimeComparison.java index 9f18ef4da..00e870e99 100644 --- a/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/TimeComparison.java +++ b/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/TimeComparison.java @@ -29,6 +29,7 @@ package io.clouditor.assurance.ccl; +import java.time.DateTimeException; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Map; @@ -52,22 +53,25 @@ public boolean evaluate(Map properties) { if (fieldValue instanceof Long) { instant = Instant.ofEpochSecond((Long) fieldValue); - } else { + } else if ((fieldValue instanceof Map) && ((Map) fieldValue).get("epochSecond") != null) { // field values are not really instants but serialized Instant in the form of epochSecond // and nano, we need to re-create the Instant - if (!(fieldValue instanceof Map) || ((Map) fieldValue).get("epochSecond") == null) { - return false; - } instant = Instant.ofEpochSecond( ((Number) ((Map) fieldValue).get("epochSecond")).longValue(), ((Number) ((Map) fieldValue).get("nano")).longValue()); + } else { + try { + instant = Instant.parse((String) fieldValue); + } catch (ClassCastException e) { + return false; + } } - var value = Instant.now().plus(relativeValue, timeUnit); + var value = Instant.now().minus(relativeValue, timeUnit); - if (this.timeOperator == TimeOperator.AFTER) { + if (this.timeOperator == TimeOperator.BEFORE || this.timeOperator == TimeOperator.YOUNGER) { return instant.isAfter(value); } else { return instant.isBefore(value); @@ -96,6 +100,8 @@ public void setTimeOperator(TimeOperator timeOperator) { public enum TimeOperator { BEFORE, - AFTER + AFTER, + YOUNGER, + OLDER } } From c36ea6c1402b63defb7d68a4b091c27ea07e8d2a Mon Sep 17 00:00:00 2001 From: Immanuel Date: Tue, 6 Aug 2019 16:33:37 +0200 Subject: [PATCH 07/13] CCL rework; allows to apply rules to asset groups --- .../aws/iam/access-key-rotation-admin.md | 13 +++++++ clouditor-engine-core/src/main/antlr/CCL.g4 | 17 ++++++--- .../java/io/clouditor/assurance/Rule.java | 28 +++++++++++---- .../io/clouditor/assurance/RuleService.java | 9 +++-- .../io/clouditor/assurance/ccl/AssetType.java | 35 +++++++++++++++++++ .../assurance/ccl/CCLDeserializer.java | 29 ++++++++++++++- .../io/clouditor/assurance/ccl/Condition.java | 6 ++-- .../clouditor/assurance/ccl/GroupedAsset.java | 26 ++++++++++++++ .../clouditor/assurance/ccl/SimpleAsset.java | 21 +++++++++++ .../assurance/ccl/TimeComparison.java | 1 - 10 files changed, 166 insertions(+), 19 deletions(-) create mode 100644 clouditor-engine-aws/src/main/resources/rules/aws/iam/access-key-rotation-admin.md create mode 100644 clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/AssetType.java create mode 100644 clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/GroupedAsset.java create mode 100644 clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/SimpleAsset.java diff --git a/clouditor-engine-aws/src/main/resources/rules/aws/iam/access-key-rotation-admin.md b/clouditor-engine-aws/src/main/resources/rules/aws/iam/access-key-rotation-admin.md new file mode 100644 index 000000000..85b5f1337 --- /dev/null +++ b/clouditor-engine-aws/src/main/resources/rules/aws/iam/access-key-rotation-admin.md @@ -0,0 +1,13 @@ +# Rotate Access Keys Regularly admin + +Checks if AWS access keys are rotated regularly and are not older than a specified amount of time, i.e. 1000 days. + +```ccl +User with userName=="admin" has createDate younger 90 days in all accessKeys +``` + +## Controls + +* "IAM-02" +* "AWS 1.4" +* "BSI C5/KRY-04" diff --git a/clouditor-engine-core/src/main/antlr/CCL.g4 b/clouditor-engine-core/src/main/antlr/CCL.g4 index ec8160bdf..ca929bcd2 100644 --- a/clouditor-engine-core/src/main/antlr/CCL.g4 +++ b/clouditor-engine-core/src/main/antlr/CCL.g4 @@ -2,18 +2,25 @@ grammar CCL; condition: assetType 'has' expression EOF; -assetType : Identifier; +assetType : + simpleAsset | + groupedAsset; + +simpleAsset: field; + +groupedAsset: field 'with' expression; + field : Identifier; expression: simpleExpression | - inExpression ; + notExpression | + inExpression; simpleExpression: - '(' expression ')' | - notExpression | emptyExpression | withinExpression | - comparison; + comparison | + '(' expression ')' ; notExpression: 'not' expression; diff --git a/clouditor-engine-core/src/main/java/io/clouditor/assurance/Rule.java b/clouditor-engine-core/src/main/java/io/clouditor/assurance/Rule.java index 1d93c2ebe..d7bb51046 100644 --- a/clouditor-engine-core/src/main/java/io/clouditor/assurance/Rule.java +++ b/clouditor-engine-core/src/main/java/io/clouditor/assurance/Rule.java @@ -32,9 +32,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.clouditor.assurance.ccl.CCLDeserializer; -import io.clouditor.assurance.ccl.CCLSerializer; -import io.clouditor.assurance.ccl.Condition; +import io.clouditor.assurance.ccl.*; import io.clouditor.discovery.Asset; import java.util.ArrayList; import java.util.Collections; @@ -59,6 +57,20 @@ public class Rule { @JsonProperty private List controls = new ArrayList<>(); @JsonProperty private String id; + public boolean evaluateApplicability(Asset asset) { + + if (this.condition != null) { + if (this.condition.getAssetType() instanceof GroupedAsset) { + return this.condition.getAssetType().evaluate(asset.getProperties()); + } + } else if (this.conditions != null) { + if (this.conditions.get(0).getAssetType() instanceof GroupedAsset) { + return this.conditions.get(0).getAssetType().evaluate(asset.getProperties()); + } + } + return true; + } + public EvaluationResult evaluate(Asset asset) { var eval = new EvaluationResult(this, asset.getProperties()); @@ -83,14 +95,16 @@ public void setCondition(Condition condition) { public String getAssetType() { // single condition - if (this.condition != null) { - return this.condition.getAssetType(); + if (this.condition != null && this.condition.getAssetType() != null) { + return this.condition.getAssetType().getField(); } // multiple conditions - if (this.conditions != null && !this.conditions.isEmpty()) { + if (this.conditions != null + && !this.conditions.isEmpty() + && this.conditions.get(0).getAssetType() != null) { // take the first one - return this.conditions.get(0).getAssetType(); + return this.conditions.get(0).getAssetType().getField(); } // no asset type found, we cannot really use this rule then diff --git a/clouditor-engine-core/src/main/java/io/clouditor/assurance/RuleService.java b/clouditor-engine-core/src/main/java/io/clouditor/assurance/RuleService.java index 2611b0b26..f123bf646 100644 --- a/clouditor-engine-core/src/main/java/io/clouditor/assurance/RuleService.java +++ b/clouditor-engine-core/src/main/java/io/clouditor/assurance/RuleService.java @@ -285,8 +285,13 @@ public void handle(DiscoveryResult result) { // evaluate all rules rulesForAsset.forEach( rule -> { - var eval = rule.evaluate(asset); - + EvaluationResult eval; + if (!rule.evaluateApplicability(asset)) { + // simply add an empty EvaluationResult + eval = new EvaluationResult(rule, asset.getProperties()); + } else { + eval = rule.evaluate(asset); + } // TODO: can we really update the asset? asset.addEvaluationResult(eval); if (!eval.isOk()) { diff --git a/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/AssetType.java b/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/AssetType.java new file mode 100644 index 000000000..d8c328cae --- /dev/null +++ b/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/AssetType.java @@ -0,0 +1,35 @@ +package io.clouditor.assurance.ccl; + +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import java.util.Map; + +@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "@class") +public abstract class AssetType { + + public abstract boolean evaluate(Map properties); + + public abstract String getField(); + + Object getValueFromField(Map asset, String fieldName) { + // first, try to resolve it directly + if (asset.containsKey(fieldName)) { + return asset.get(fieldName); + } + + // split it at . + var names = fieldName.split("\\."); + + var base = asset; + var value = (Object) null; + + for (var name : names) { + value = base.get(name); + + if (value instanceof Map) { + base = (Map) value; + } + } + + return value; + } +} diff --git a/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/CCLDeserializer.java b/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/CCLDeserializer.java index 2317ccd60..3e10ce975 100644 --- a/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/CCLDeserializer.java +++ b/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/CCLDeserializer.java @@ -74,7 +74,7 @@ public Condition visitCondition(ConditionContext ctx) { if (ctx.assetType() != null && ctx.expression() != null) { var condition = new Condition(); - condition.setAssetType(ctx.assetType().getText()); + condition.setAssetType(ctx.assetType().accept(new AssetTypeListener())); condition.setExpression(ctx.expression().accept(new ExpressionListener())); return condition; @@ -84,6 +84,33 @@ public Condition visitCondition(ConditionContext ctx) { } } + private static class AssetTypeListener extends CCLBaseVisitor { + + @Override + public AssetType visitAssetType(CCLParser.AssetTypeContext ctx) { + if (ctx.simpleAsset() != null) { + var simpleAsset = new SimpleAsset(); + simpleAsset.setField(ctx.simpleAsset().field().getText()); + return simpleAsset; + } else if (ctx.groupedAsset() != null) { + return ctx.groupedAsset().accept(this); + } + + return super.visitAssetType(ctx); + } + + @Override + public AssetType visitGroupedAsset(CCLParser.GroupedAssetContext ctx) { + if (ctx.expression() != null) { + var asset = new GroupedAsset(); + asset.setField(ctx.field().getText()); + asset.setAssetExpression(ctx.expression().accept(new ExpressionListener())); + return asset; + } + return super.visitGroupedAsset(ctx); + } + } + private static class ExpressionListener extends CCLBaseVisitor { @Override diff --git a/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/Condition.java b/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/Condition.java index 55b548e7c..33b571433 100644 --- a/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/Condition.java +++ b/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/Condition.java @@ -33,7 +33,7 @@ public class Condition { - private String assetType; + private AssetType assetType; private Expression expression; @@ -47,11 +47,11 @@ public void setExpression(Expression expression) { this.expression = expression; } - public String getAssetType() { + public AssetType getAssetType() { return assetType; } - public void setAssetType(String assetType) { + public void setAssetType(AssetType assetType) { this.assetType = assetType; } diff --git a/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/GroupedAsset.java b/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/GroupedAsset.java new file mode 100644 index 000000000..90dda0249 --- /dev/null +++ b/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/GroupedAsset.java @@ -0,0 +1,26 @@ +package io.clouditor.assurance.ccl; + +import java.util.Map; + +public class GroupedAsset extends AssetType { + + private String field; + private Expression assetExpression; + + public String getField() { + return this.field; + } + + public void setField(String field) { + this.field = field; + } + + public void setAssetExpression(Expression assetExpression) { + this.assetExpression = assetExpression; + } + + @Override + public boolean evaluate(Map properties) { + return this.assetExpression.evaluate(properties); + } +} diff --git a/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/SimpleAsset.java b/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/SimpleAsset.java new file mode 100644 index 000000000..2a88ea574 --- /dev/null +++ b/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/SimpleAsset.java @@ -0,0 +1,21 @@ +package io.clouditor.assurance.ccl; + +import java.util.Map; + +public class SimpleAsset extends AssetType { + + private String field; + + public String getField() { + return this.field; + } + + public void setField(String field) { + this.field = field; + } + + @Override + public boolean evaluate(Map properties) { + return true; + } +} diff --git a/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/TimeComparison.java b/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/TimeComparison.java index 00e870e99..c4d6479dd 100644 --- a/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/TimeComparison.java +++ b/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/TimeComparison.java @@ -29,7 +29,6 @@ package io.clouditor.assurance.ccl; -import java.time.DateTimeException; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Map; From ef6fea9606f1965d29494dca2415db1c8a6c0839 Mon Sep 17 00:00:00 2001 From: Immanuel Date: Wed, 7 Aug 2019 15:00:50 +0200 Subject: [PATCH 08/13] fixed tests; reworked time comparison --- .../resources/rules/aws/iam/access-key-rotation.md | 2 +- .../discovery/aws/AwsIamUserScannerTest.java | 2 +- .../io/clouditor/assurance/ccl/TimeComparison.java | 13 +++++++++++-- .../io/clouditor/assurance/CCLDeserializerTest.java | 8 ++------ 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/clouditor-engine-aws/src/main/resources/rules/aws/iam/access-key-rotation.md b/clouditor-engine-aws/src/main/resources/rules/aws/iam/access-key-rotation.md index 7b9070391..b902c8f8d 100644 --- a/clouditor-engine-aws/src/main/resources/rules/aws/iam/access-key-rotation.md +++ b/clouditor-engine-aws/src/main/resources/rules/aws/iam/access-key-rotation.md @@ -3,7 +3,7 @@ Checks if AWS access keys are rotated regularly and are not older than a specified amount of time, i.e. 90 days. ```ccl -User has createDate after 90 days in all accessKeys +User has createDate younger 90 days in all accessKeys ``` ## Controls diff --git a/clouditor-engine-aws/src/test/java/io/clouditor/discovery/aws/AwsIamUserScannerTest.java b/clouditor-engine-aws/src/test/java/io/clouditor/discovery/aws/AwsIamUserScannerTest.java index 305f227f5..9269cdfd5 100644 --- a/clouditor-engine-aws/src/test/java/io/clouditor/discovery/aws/AwsIamUserScannerTest.java +++ b/clouditor-engine-aws/src/test/java/io/clouditor/discovery/aws/AwsIamUserScannerTest.java @@ -184,7 +184,7 @@ void testAccessKeyRotation() throws IOException { FileSystemManager.getInstance() .getPathForResource("rules/aws/iam/access-key-rotation.md")); - // user2 has an very old access key + // user2 has a very old access key assertFalse(rule.evaluate(assets.get(USER2_ARN)).isOk()); } diff --git a/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/TimeComparison.java b/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/TimeComparison.java index c4d6479dd..40da935de 100644 --- a/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/TimeComparison.java +++ b/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/TimeComparison.java @@ -50,6 +50,10 @@ public boolean evaluate(Map properties) { Instant instant; + if (fieldValue == null) { + return false; + } + if (fieldValue instanceof Long) { instant = Instant.ofEpochSecond((Long) fieldValue); } else if ((fieldValue instanceof Map) && ((Map) fieldValue).get("epochSecond") != null) { @@ -68,9 +72,14 @@ public boolean evaluate(Map properties) { } } - var value = Instant.now().minus(relativeValue, timeUnit); + Instant value; + if (this.timeOperator == TimeOperator.BEFORE || this.timeOperator == TimeOperator.AFTER) { + value = Instant.now().plus(relativeValue, timeUnit); + } else { + value = Instant.now().minus(relativeValue, timeUnit); + } - if (this.timeOperator == TimeOperator.BEFORE || this.timeOperator == TimeOperator.YOUNGER) { + if (this.timeOperator == TimeOperator.YOUNGER || this.timeOperator == TimeOperator.AFTER) { return instant.isAfter(value); } else { return instant.isBefore(value); diff --git a/clouditor-engine-core/src/test/java/io/clouditor/assurance/CCLDeserializerTest.java b/clouditor-engine-core/src/test/java/io/clouditor/assurance/CCLDeserializerTest.java index 0c54f5a80..6bbc5d4a0 100644 --- a/clouditor-engine-core/src/test/java/io/clouditor/assurance/CCLDeserializerTest.java +++ b/clouditor-engine-core/src/test/java/io/clouditor/assurance/CCLDeserializerTest.java @@ -129,7 +129,7 @@ void testInExpression() { @Test void testIsBeforeComparison() { var ccl = new CCLDeserializer(); - var condition = ccl.parse("User has createDate before now"); + var condition = ccl.parse("User has createDate older 10 days"); var asset = new AssetProperties(); asset.put("createDate", Map.of("epochSecond", 1550131042, "nano", 1)); @@ -142,10 +142,6 @@ void testIsBeforeComparison() { assertFalse(condition.evaluate(asset)); - condition = ccl.parse("User has createDate after now"); - - assertTrue(condition.evaluate(asset)); - // something older than 90 days asset.put( "createDate", @@ -153,7 +149,7 @@ void testIsBeforeComparison() { "epochSecond", Instant.now().minus(100, ChronoUnit.DAYS).getEpochSecond(), "nano", 0)); // user create date should not be older than 90 days - condition = ccl.parse("User has not createDate before 90 days"); + condition = ccl.parse("User has createDate younger 90 days"); assertFalse(condition.evaluate(asset)); } From e807d2169da62f332fde4429cc79d9459d27e9eb Mon Sep 17 00:00:00 2001 From: Immanuel Date: Tue, 13 Aug 2019 09:22:45 +0200 Subject: [PATCH 09/13] added time comparison tests --- .../assurance/CCLDeserializerTest.java | 61 ++++++++++++++++--- 1 file changed, 51 insertions(+), 10 deletions(-) diff --git a/clouditor-engine-core/src/test/java/io/clouditor/assurance/CCLDeserializerTest.java b/clouditor-engine-core/src/test/java/io/clouditor/assurance/CCLDeserializerTest.java index 6bbc5d4a0..56b49d5da 100644 --- a/clouditor-engine-core/src/test/java/io/clouditor/assurance/CCLDeserializerTest.java +++ b/clouditor-engine-core/src/test/java/io/clouditor/assurance/CCLDeserializerTest.java @@ -129,27 +129,68 @@ void testInExpression() { @Test void testIsBeforeComparison() { var ccl = new CCLDeserializer(); - var condition = ccl.parse("User has createDate older 10 days"); + var condition = ccl.parse("AccessKey has expiry before 10 days"); + + var asset = new AssetProperties(); + asset.put("expiry", Map.of("epochSecond", Instant.now().getEpochSecond(), "nano", 1)); + + assertTrue(condition.evaluate(asset)); + + asset.clear(); + + asset.put("expiry", Map.of("epochSecond", Instant.now().plus(20, ChronoUnit.DAYS).getEpochSecond(), "nano", 1)); + + assertFalse(condition.evaluate(asset)); + } + + @Test + void testAfterComparison() { + var ccl = new CCLDeserializer(); + var condition = ccl.parse("AccessKey has expiry after 10 days"); + + var asset = new AssetProperties(); + asset.put("expiry", Map.of("epochSecond", Instant.now().plus(20, ChronoUnit.DAYS).getEpochSecond(), "nano", 1)); + + assertTrue(condition.evaluate(asset)); + + asset.clear(); + + asset.put("expiry", Map.of("epochSecond", Instant.now().getEpochSecond(), "nano", 1)); + + assertFalse(condition.evaluate(asset)); + } + + @Test + void testYoungerComparison() { + var ccl = new CCLDeserializer(); + var condition = ccl.parse("User has createDate younger 10 days"); var asset = new AssetProperties(); - asset.put("createDate", Map.of("epochSecond", 1550131042, "nano", 1)); + asset.put("createDate", Map.of("epochSecond", Instant.now().getEpochSecond(), "nano", 1)); assertTrue(condition.evaluate(asset)); asset.clear(); // something in the future - asset.put("createDate", Map.of("epochSecond", Integer.MAX_VALUE, "nano", 1)); + asset.put("createDate", Map.of("epochSecond", Instant.now().minus(20, ChronoUnit.DAYS).getEpochSecond(), "nano", 1)); assertFalse(condition.evaluate(asset)); + } - // something older than 90 days - asset.put( - "createDate", - Map.of( - "epochSecond", Instant.now().minus(100, ChronoUnit.DAYS).getEpochSecond(), "nano", 0)); + @Test + void testOlderComparison() { + var ccl = new CCLDeserializer(); + var condition = ccl.parse("User has createDate older 10 days"); + + var asset = new AssetProperties(); + + asset.put("createDate", Map.of("epochSecond", Instant.now().minus(20, ChronoUnit.DAYS).getEpochSecond(), "nano", 1)); + + assertTrue(condition.evaluate(asset)); + + asset.clear(); - // user create date should not be older than 90 days - condition = ccl.parse("User has createDate younger 90 days"); + asset.put("createDate", Map.of("epochSecond", Instant.now().getEpochSecond(), "nano", 1)); assertFalse(condition.evaluate(asset)); } From e42cc578b8af22e23e708b7a7e79272a609802a4 Mon Sep 17 00:00:00 2001 From: Immanuel Date: Tue, 13 Aug 2019 09:28:18 +0200 Subject: [PATCH 10/13] spotless --- .../assurance/CCLDeserializerTest.java | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/clouditor-engine-core/src/test/java/io/clouditor/assurance/CCLDeserializerTest.java b/clouditor-engine-core/src/test/java/io/clouditor/assurance/CCLDeserializerTest.java index 56b49d5da..a7c285b44 100644 --- a/clouditor-engine-core/src/test/java/io/clouditor/assurance/CCLDeserializerTest.java +++ b/clouditor-engine-core/src/test/java/io/clouditor/assurance/CCLDeserializerTest.java @@ -138,7 +138,9 @@ void testIsBeforeComparison() { asset.clear(); - asset.put("expiry", Map.of("epochSecond", Instant.now().plus(20, ChronoUnit.DAYS).getEpochSecond(), "nano", 1)); + asset.put( + "expiry", + Map.of("epochSecond", Instant.now().plus(20, ChronoUnit.DAYS).getEpochSecond(), "nano", 1)); assertFalse(condition.evaluate(asset)); } @@ -149,7 +151,9 @@ void testAfterComparison() { var condition = ccl.parse("AccessKey has expiry after 10 days"); var asset = new AssetProperties(); - asset.put("expiry", Map.of("epochSecond", Instant.now().plus(20, ChronoUnit.DAYS).getEpochSecond(), "nano", 1)); + asset.put( + "expiry", + Map.of("epochSecond", Instant.now().plus(20, ChronoUnit.DAYS).getEpochSecond(), "nano", 1)); assertTrue(condition.evaluate(asset)); @@ -172,7 +176,10 @@ void testYoungerComparison() { asset.clear(); // something in the future - asset.put("createDate", Map.of("epochSecond", Instant.now().minus(20, ChronoUnit.DAYS).getEpochSecond(), "nano", 1)); + asset.put( + "createDate", + Map.of( + "epochSecond", Instant.now().minus(20, ChronoUnit.DAYS).getEpochSecond(), "nano", 1)); assertFalse(condition.evaluate(asset)); } @@ -184,7 +191,10 @@ void testOlderComparison() { var asset = new AssetProperties(); - asset.put("createDate", Map.of("epochSecond", Instant.now().minus(20, ChronoUnit.DAYS).getEpochSecond(), "nano", 1)); + asset.put( + "createDate", + Map.of( + "epochSecond", Instant.now().minus(20, ChronoUnit.DAYS).getEpochSecond(), "nano", 1)); assertTrue(condition.evaluate(asset)); From b70b0766c768356b07cbf964ac30792da24db0e1 Mon Sep 17 00:00:00 2001 From: Immanuel Date: Wed, 14 Aug 2019 13:59:23 +0200 Subject: [PATCH 11/13] fixed broken rule --- clouditor-engine-aws/src/main/resources/rules/aws/iam/mfa.md | 2 +- .../assurance/ccl/{GroupedAsset.java => FilteredAssetType.java} | 0 .../assurance/ccl/{SimpleAsset.java => SimpleAssetType.java} | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/{GroupedAsset.java => FilteredAssetType.java} (100%) rename clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/{SimpleAsset.java => SimpleAssetType.java} (100%) diff --git a/clouditor-engine-aws/src/main/resources/rules/aws/iam/mfa.md b/clouditor-engine-aws/src/main/resources/rules/aws/iam/mfa.md index 12dbb3242..5f6b01ebc 100644 --- a/clouditor-engine-aws/src/main/resources/rules/aws/iam/mfa.md +++ b/clouditor-engine-aws/src/main/resources/rules/aws/iam/mfa.md @@ -3,7 +3,7 @@ Checks if Multi-Factor Authentication (MFA) is enabled for all users. ```ccl -condition: User has not empty mfaDevices +User has not empty mfaDevices ``` ## Controls diff --git a/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/GroupedAsset.java b/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/FilteredAssetType.java similarity index 100% rename from clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/GroupedAsset.java rename to clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/FilteredAssetType.java diff --git a/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/SimpleAsset.java b/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/SimpleAssetType.java similarity index 100% rename from clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/SimpleAsset.java rename to clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/SimpleAssetType.java From 0cf86fe57b17394e73f32a200a1573115a453923 Mon Sep 17 00:00:00 2001 From: Immanuel Date: Wed, 14 Aug 2019 14:00:36 +0200 Subject: [PATCH 12/13] fixed remaining issues for pull request --- clouditor-engine-core/src/main/antlr/CCL.g4 | 8 +-- .../java/io/clouditor/assurance/Rule.java | 8 +-- .../io/clouditor/assurance/ccl/AssetType.java | 53 +++++++++++-------- .../assurance/ccl/CCLDeserializer.java | 18 +++---- .../assurance/ccl/FilteredAssetType.java | 41 +++++++++++--- .../assurance/ccl/SimpleAssetType.java | 41 +++++++++++--- 6 files changed, 117 insertions(+), 52 deletions(-) diff --git a/clouditor-engine-core/src/main/antlr/CCL.g4 b/clouditor-engine-core/src/main/antlr/CCL.g4 index ca929bcd2..c5bbdfecd 100644 --- a/clouditor-engine-core/src/main/antlr/CCL.g4 +++ b/clouditor-engine-core/src/main/antlr/CCL.g4 @@ -3,12 +3,12 @@ grammar CCL; condition: assetType 'has' expression EOF; assetType : - simpleAsset | - groupedAsset; + simpleAssetType | + filteredAssetType; -simpleAsset: field; +simpleAssetType: field; -groupedAsset: field 'with' expression; +filteredAssetType: field 'with' expression; field : Identifier; expression: diff --git a/clouditor-engine-core/src/main/java/io/clouditor/assurance/Rule.java b/clouditor-engine-core/src/main/java/io/clouditor/assurance/Rule.java index d7bb51046..f941cce3a 100644 --- a/clouditor-engine-core/src/main/java/io/clouditor/assurance/Rule.java +++ b/clouditor-engine-core/src/main/java/io/clouditor/assurance/Rule.java @@ -60,11 +60,11 @@ public class Rule { public boolean evaluateApplicability(Asset asset) { if (this.condition != null) { - if (this.condition.getAssetType() instanceof GroupedAsset) { + if (this.condition.getAssetType() instanceof FilteredAssetType) { return this.condition.getAssetType().evaluate(asset.getProperties()); } } else if (this.conditions != null) { - if (this.conditions.get(0).getAssetType() instanceof GroupedAsset) { + if (this.conditions.get(0).getAssetType() instanceof FilteredAssetType) { return this.conditions.get(0).getAssetType().evaluate(asset.getProperties()); } } @@ -96,7 +96,7 @@ public void setCondition(Condition condition) { public String getAssetType() { // single condition if (this.condition != null && this.condition.getAssetType() != null) { - return this.condition.getAssetType().getField(); + return this.condition.getAssetType().getValue(); } // multiple conditions @@ -104,7 +104,7 @@ public String getAssetType() { && !this.conditions.isEmpty() && this.conditions.get(0).getAssetType() != null) { // take the first one - return this.conditions.get(0).getAssetType().getField(); + return this.conditions.get(0).getAssetType().getValue(); } // no asset type found, we cannot really use this rule then diff --git a/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/AssetType.java b/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/AssetType.java index d8c328cae..d24bf4cf7 100644 --- a/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/AssetType.java +++ b/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/AssetType.java @@ -1,3 +1,32 @@ +/* + * Copyright (c) 2016-2019, Fraunhofer AISEC. All rights reserved. + * + * + * $$\ $$\ $$\ $$\ + * $$ | $$ |\__| $$ | + * $$$$$$$\ $$ | $$$$$$\ $$\ $$\ $$$$$$$ |$$\ $$$$$$\ $$$$$$\ $$$$$$\ + * $$ _____|$$ |$$ __$$\ $$ | $$ |$$ __$$ |$$ |\_$$ _| $$ __$$\ $$ __$$\ + * $$ / $$ |$$ / $$ |$$ | $$ |$$ / $$ |$$ | $$ | $$ / $$ |$$ | \__| + * $$ | $$ |$$ | $$ |$$ | $$ |$$ | $$ |$$ | $$ |$$\ $$ | $$ |$$ | + * \$$$$$$\ $$ |\$$$$$ |\$$$$$ |\$$$$$$ |$$ | \$$$ |\$$$$$ |$$ | + * \_______|\__| \______/ \______/ \_______|\__| \____/ \______/ \__| + * + * This file is part of Clouditor Community Edition. + * + * Clouditor Community Edition is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Clouditor Community Edition is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * long with Clouditor Community Edition. If not, see + */ + package io.clouditor.assurance.ccl; import com.fasterxml.jackson.annotation.JsonTypeInfo; @@ -8,28 +37,6 @@ public abstract class AssetType { public abstract boolean evaluate(Map properties); - public abstract String getField(); - - Object getValueFromField(Map asset, String fieldName) { - // first, try to resolve it directly - if (asset.containsKey(fieldName)) { - return asset.get(fieldName); - } - - // split it at . - var names = fieldName.split("\\."); - - var base = asset; - var value = (Object) null; - - for (var name : names) { - value = base.get(name); - - if (value instanceof Map) { - base = (Map) value; - } - } + public abstract String getValue(); - return value; - } } diff --git a/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/CCLDeserializer.java b/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/CCLDeserializer.java index 3e10ce975..f94bed5fd 100644 --- a/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/CCLDeserializer.java +++ b/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/CCLDeserializer.java @@ -88,26 +88,26 @@ private static class AssetTypeListener extends CCLBaseVisitor { @Override public AssetType visitAssetType(CCLParser.AssetTypeContext ctx) { - if (ctx.simpleAsset() != null) { - var simpleAsset = new SimpleAsset(); - simpleAsset.setField(ctx.simpleAsset().field().getText()); + if (ctx.simpleAssetType() != null) { + var simpleAsset = new SimpleAssetType(); + simpleAsset.setValue(ctx.simpleAssetType().field().getText()); return simpleAsset; - } else if (ctx.groupedAsset() != null) { - return ctx.groupedAsset().accept(this); + } else if (ctx.filteredAssetType() != null) { + return ctx.filteredAssetType().accept(this); } return super.visitAssetType(ctx); } @Override - public AssetType visitGroupedAsset(CCLParser.GroupedAssetContext ctx) { + public AssetType visitFilteredAssetType(CCLParser.FilteredAssetTypeContext ctx) { if (ctx.expression() != null) { - var asset = new GroupedAsset(); - asset.setField(ctx.field().getText()); + var asset = new FilteredAssetType(); + asset.setValue(ctx.field().getText()); asset.setAssetExpression(ctx.expression().accept(new ExpressionListener())); return asset; } - return super.visitGroupedAsset(ctx); + return super.visitFilteredAssetType(ctx); } } diff --git a/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/FilteredAssetType.java b/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/FilteredAssetType.java index 90dda0249..455921120 100644 --- a/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/FilteredAssetType.java +++ b/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/FilteredAssetType.java @@ -1,18 +1,47 @@ +/* + * Copyright (c) 2016-2019, Fraunhofer AISEC. All rights reserved. + * + * + * $$\ $$\ $$\ $$\ + * $$ | $$ |\__| $$ | + * $$$$$$$\ $$ | $$$$$$\ $$\ $$\ $$$$$$$ |$$\ $$$$$$\ $$$$$$\ $$$$$$\ + * $$ _____|$$ |$$ __$$\ $$ | $$ |$$ __$$ |$$ |\_$$ _| $$ __$$\ $$ __$$\ + * $$ / $$ |$$ / $$ |$$ | $$ |$$ / $$ |$$ | $$ | $$ / $$ |$$ | \__| + * $$ | $$ |$$ | $$ |$$ | $$ |$$ | $$ |$$ | $$ |$$\ $$ | $$ |$$ | + * \$$$$$$\ $$ |\$$$$$ |\$$$$$ |\$$$$$$ |$$ | \$$$ |\$$$$$ |$$ | + * \_______|\__| \______/ \______/ \_______|\__| \____/ \______/ \__| + * + * This file is part of Clouditor Community Edition. + * + * Clouditor Community Edition is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Clouditor Community Edition is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * long with Clouditor Community Edition. If not, see + */ + package io.clouditor.assurance.ccl; import java.util.Map; -public class GroupedAsset extends AssetType { +public class FilteredAssetType extends AssetType { - private String field; + private String value; private Expression assetExpression; - public String getField() { - return this.field; + public String getValue() { + return this.value; } - public void setField(String field) { - this.field = field; + public void setValue(String value) { + this.value = value; } public void setAssetExpression(Expression assetExpression) { diff --git a/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/SimpleAssetType.java b/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/SimpleAssetType.java index 2a88ea574..c9c328133 100644 --- a/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/SimpleAssetType.java +++ b/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/SimpleAssetType.java @@ -1,17 +1,46 @@ +/* + * Copyright (c) 2016-2019, Fraunhofer AISEC. All rights reserved. + * + * + * $$\ $$\ $$\ $$\ + * $$ | $$ |\__| $$ | + * $$$$$$$\ $$ | $$$$$$\ $$\ $$\ $$$$$$$ |$$\ $$$$$$\ $$$$$$\ $$$$$$\ + * $$ _____|$$ |$$ __$$\ $$ | $$ |$$ __$$ |$$ |\_$$ _| $$ __$$\ $$ __$$\ + * $$ / $$ |$$ / $$ |$$ | $$ |$$ / $$ |$$ | $$ | $$ / $$ |$$ | \__| + * $$ | $$ |$$ | $$ |$$ | $$ |$$ | $$ |$$ | $$ |$$\ $$ | $$ |$$ | + * \$$$$$$\ $$ |\$$$$$ |\$$$$$ |\$$$$$$ |$$ | \$$$ |\$$$$$ |$$ | + * \_______|\__| \______/ \______/ \_______|\__| \____/ \______/ \__| + * + * This file is part of Clouditor Community Edition. + * + * Clouditor Community Edition is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Clouditor Community Edition is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * long with Clouditor Community Edition. If not, see + */ + package io.clouditor.assurance.ccl; import java.util.Map; -public class SimpleAsset extends AssetType { +public class SimpleAssetType extends AssetType { - private String field; + private String value; - public String getField() { - return this.field; + public String getValue() { + return this.value; } - public void setField(String field) { - this.field = field; + public void setValue(String value) { + this.value = value; } @Override From d904bb14e62b2273cb71ce0df6ea17261f60b64f Mon Sep 17 00:00:00 2001 From: Immanuel Date: Wed, 14 Aug 2019 14:02:57 +0200 Subject: [PATCH 13/13] spotless --- .../src/main/java/io/clouditor/assurance/ccl/AssetType.java | 1 - 1 file changed, 1 deletion(-) diff --git a/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/AssetType.java b/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/AssetType.java index d24bf4cf7..9f0c5b07a 100644 --- a/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/AssetType.java +++ b/clouditor-engine-core/src/main/java/io/clouditor/assurance/ccl/AssetType.java @@ -38,5 +38,4 @@ public abstract class AssetType { public abstract boolean evaluate(Map properties); public abstract String getValue(); - }