diff --git a/src/main/java/com/checkmarx/ast/kicsRealtimeResults/kicsRealtimeResults.java b/src/main/java/com/checkmarx/ast/kicsRealtimeResults/KicsRealtimeResults.java similarity index 88% rename from src/main/java/com/checkmarx/ast/kicsRealtimeResults/kicsRealtimeResults.java rename to src/main/java/com/checkmarx/ast/kicsRealtimeResults/KicsRealtimeResults.java index c9fd88d4..f623160c 100644 --- a/src/main/java/com/checkmarx/ast/kicsRealtimeResults/kicsRealtimeResults.java +++ b/src/main/java/com/checkmarx/ast/kicsRealtimeResults/KicsRealtimeResults.java @@ -21,7 +21,7 @@ @JsonDeserialize() @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public class kicsRealtimeResults { +public class KicsRealtimeResults { int totalCount; String version; @@ -29,14 +29,14 @@ public class kicsRealtimeResults { KicsSummary kicsSummary; @JsonCreator - public kicsRealtimeResults(@JsonProperty("total_counter") int totalCount, @JsonProperty("queries") List results,@JsonProperty("kics_version") String version, @JsonProperty("severity_counters") KicsSummary kicsSummary) { + public KicsRealtimeResults(@JsonProperty("total_counter") int totalCount, @JsonProperty("queries") List results, @JsonProperty("kics_version") String version, @JsonProperty("severity_counters") KicsSummary kicsSummary) { this.totalCount = totalCount; this.version = version; this.results = results; this.kicsSummary = kicsSummary; } public static T fromLine(String line) { - return parse(line, TypeFactory.defaultInstance().constructType(kicsRealtimeResults.class)); + return parse(line, TypeFactory.defaultInstance().constructType(KicsRealtimeResults.class)); } private static T parse(String line, JavaType type) { diff --git a/src/main/java/com/checkmarx/ast/remediation/KicsRemediation.java b/src/main/java/com/checkmarx/ast/remediation/KicsRemediation.java new file mode 100644 index 00000000..d8863c40 --- /dev/null +++ b/src/main/java/com/checkmarx/ast/remediation/KicsRemediation.java @@ -0,0 +1,56 @@ +package com.checkmarx.ast.remediation; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.type.TypeFactory; +import lombok.Value; +import org.apache.commons.lang3.StringUtils; + +import java.io.IOException; + +@Value +@JsonDeserialize() +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public class KicsRemediation { + String availableRemediation; + String appliedRemediation; + + @JsonCreator + public KicsRemediation(@JsonProperty("available_remediation_count") String availableRemediation, @JsonProperty("applied_remediation_count") String appliedRemediation) { + this.availableRemediation = availableRemediation; + this.appliedRemediation = appliedRemediation; + } + + public static T fromLine(String line) { + return parse(line, TypeFactory.defaultInstance().constructType(KicsRemediation.class)); + } + + private static T parse(String line, JavaType type) { + T result = null; + try { + if (!StringUtils.isBlank(line) && isValidJSON(line)) { + result = new ObjectMapper().readValue(line, type); + + } + } catch (IOException e) { + e.printStackTrace(); + } + return result; + } + + private static boolean isValidJSON(final String json) { + try { + final ObjectMapper mapper = new ObjectMapper(); + mapper.readTree(json); + return true; + } catch (IOException e) { + return false; + } + } +} diff --git a/src/main/java/com/checkmarx/ast/wrapper/CxWrapper.java b/src/main/java/com/checkmarx/ast/wrapper/CxWrapper.java index ef2ae8c4..7d118a58 100644 --- a/src/main/java/com/checkmarx/ast/wrapper/CxWrapper.java +++ b/src/main/java/com/checkmarx/ast/wrapper/CxWrapper.java @@ -1,9 +1,10 @@ package com.checkmarx.ast.wrapper; import com.checkmarx.ast.codebashing.CodeBashing; -import com.checkmarx.ast.kicsRealtimeResults.kicsRealtimeResults; +import com.checkmarx.ast.kicsRealtimeResults.KicsRealtimeResults; import com.checkmarx.ast.predicate.Predicate; import com.checkmarx.ast.project.Project; +import com.checkmarx.ast.remediation.KicsRemediation; import com.checkmarx.ast.results.ReportFormat; import com.checkmarx.ast.results.Results; import com.checkmarx.ast.results.ResultsSummary; @@ -304,7 +305,7 @@ public int getResultsBfl(@NonNull UUID scanId, @NonNull String queryId, List arguments = new ArrayList<>(); + arguments.add(this.executable); + arguments.add("utils"); + arguments.add("remediation"); + arguments.add("kics"); + arguments.add("--results-file"); + arguments.add(resultsFile); + arguments.add("--kics-files"); + arguments.add(kicsFile); + if (engine.length() > 0) { + arguments.add(CxConstants.ENGINE); + arguments.add(engine); + } + if (similarityIds.length() > 0) { + arguments.add("--similarity-ids"); + arguments.add(similarityIds); + } + KicsRemediation remediation = Execution.executeCommand(arguments, logger, KicsRemediation::fromLine); + return remediation; } private int getIndexOfBfLNode(List bflNodes, List resultNodes) { diff --git a/src/test/java/com/checkmarx/ast/RemediationTest.java b/src/test/java/com/checkmarx/ast/RemediationTest.java new file mode 100644 index 00000000..d8a1a407 --- /dev/null +++ b/src/test/java/com/checkmarx/ast/RemediationTest.java @@ -0,0 +1,31 @@ +package com.checkmarx.ast; + +import com.checkmarx.ast.remediation.KicsRemediation; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import java.nio.file.Path; +import java.nio.file.Paths; + +class RemediationTest extends BaseTest { + private static String RESULTS_FILE = "target/test-classes/results.json"; + + private static Path path = Paths.get("target/test-classes/"); + private static String KICS_FILE = path.toAbsolutePath().toString(); + private static String QUERY_ID = "9574288c118e8c87eea31b6f0b011295a39ec5e70d83fb70e839b8db4a99eba8"; + private static String ENGINE = "docker"; + + @Test + void testKicsRemediation() throws Exception { + KicsRemediation remediation = wrapper.kicsRemediate(RESULTS_FILE,KICS_FILE,"",""); + Assertions.assertTrue(remediation.getAppliedRemediation() != ""); + Assertions.assertTrue(remediation.getAvailableRemediation() != ""); + } + + @Test + void testKicsRemediationSimilarityFilter() throws Exception { + KicsRemediation remediation = wrapper.kicsRemediate(RESULTS_FILE,KICS_FILE,ENGINE,QUERY_ID); + Assertions.assertTrue(remediation.getAppliedRemediation() != ""); + Assertions.assertTrue(remediation.getAvailableRemediation() != ""); + } + +} diff --git a/src/test/java/com/checkmarx/ast/ScanTest.java b/src/test/java/com/checkmarx/ast/ScanTest.java index 655ff787..292c67aa 100644 --- a/src/test/java/com/checkmarx/ast/ScanTest.java +++ b/src/test/java/com/checkmarx/ast/ScanTest.java @@ -1,6 +1,6 @@ package com.checkmarx.ast; -import com.checkmarx.ast.kicsRealtimeResults.kicsRealtimeResults; +import com.checkmarx.ast.kicsRealtimeResults.KicsRealtimeResults; import com.checkmarx.ast.scan.Scan; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -34,7 +34,7 @@ void testScanCreate() throws Exception { @Test void testKicsRealtimeScan() throws Exception { - kicsRealtimeResults scan = wrapper.kicsRealtimeScan("target/test-classes/Dockerfile","","v"); + KicsRealtimeResults scan = wrapper.kicsRealtimeScan("target/test-classes/Dockerfile","","v"); Assertions.assertTrue(scan.getResults().size() >= 1); } diff --git a/src/test/resources/positive1.tf b/src/test/resources/positive1.tf new file mode 100644 index 00000000..532c4152 --- /dev/null +++ b/src/test/resources/positive1.tf @@ -0,0 +1,20 @@ +resource "aws_lb_listener" "listener5" { + load_balancer_arn = aws_lb.test3.arn + port = 80 + default_action { + type = "redirect" + + redirect { + port = "80" + protocol = "HTTP" + status_code = "HTTP_301" + } + } +} + +resource "aws_lb" "test3" { + name = "test123" + load_balancer_type = "application" + subnets = [aws_subnet.subnet1.id, aws_subnet.subnet2.id] + internal = true +} diff --git a/src/test/resources/results.json b/src/test/resources/results.json new file mode 100644 index 00000000..b20bbb9d --- /dev/null +++ b/src/test/resources/results.json @@ -0,0 +1 @@ +{"kics_version":"v1.5.12","total_counter":6,"queries":[{"query_name":"ALB Listening on HTTP","query_id":"de7f5e83-da88-4046-871f-ea18504b1d43","severity":"HIGH","platform":"Terraform","category":"Networking and Firewall","description":"AWS Application Load Balancer (alb) should not listen on HTTP","query_url":"https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_listener","files":[{"file_name":"../../path/positive1.tf","similarity_id":"b42a19486a8e18324a9b2c06147b1c49feb3ba39a0e4aeafec5665e60f98d047","line":9,"issue_type":"IncorrectValue","search_key":"aws_lb_listener[listener5].default_action.redirect.protocol","search_line":0,"search_value":"","expected_value":"'default_action.redirect.protocol' is equal to 'HTTPS'","actual_value":"'default_action.redirect.protocol' is equal 'HTTP'","remediation":"{\"after\":\"HTTPS\",\"before\":\"HTTP\"}","remediation_type":"replacement"}]},{"query_name":"ALB Not Dropping Invalid Headers","query_id":"6e3fd2ed-5c83-4c68-9679-7700d224d379","severity":"MEDIUM","platform":"Terraform","category":"Best Practices","description":"It's considered a best practice when using Application Load Balancers to drop invalid header fields","query_url":"https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb#drop_invalid_header_fields","files":[{"file_name":"../../path/positive1.tf","similarity_id":"9574288c118e8c87eea31b6f0b011295a39ec5e70d83fb70e839b8db4a99eba8","line":15,"issue_type":"MissingAttribute","search_key":"aws_lb[{{test3}}]","search_line":0,"search_value":"","expected_value":"aws_lb[{{test3}}].drop_invalid_header_fields is set to true","actual_value":"aws_lb[{{test3}}].drop_invalid_header_fields is missing","remediation":"drop_invalid_header_fields = true","remediation_type":"addition"}]},{"query_name":"ALB Deletion Protection Disabled","query_id":"afecd1f1-6378-4f7e-bb3b-60c35801fdd4","severity":"LOW","platform":"Terraform","category":"Insecure Configurations","description":"Application Load Balancer should have deletion protection enabled","query_url":"https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb#enable_deletion_protection","files":[{"file_name":"../../path/positive1.tf","similarity_id":"cc22618d82eee56de73a07a558bf689f2efe1ddc42393323d14f77b0c37c29a8","line":15,"issue_type":"MissingAttribute","search_key":"aws_lb[test3]","search_line":0,"search_value":"","expected_value":"'enable_deletion_protection' is defined and set to true","actual_value":"'enable_deletion_protection' is undefined or null","remediation":"enable_deletion_protection = true","remediation_type":"addition"}]},{"query_name":"IAM Access Analyzer Not Enabled","query_id":"e592a0c5-5bdb-414c-9066-5dba7cdea370","severity":"LOW","platform":"Terraform","category":"Best Practices","description":"IAM Access Analyzer should be enabled and configured to continuously monitor resource permissions","query_url":"https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/accessanalyzer_analyzer","files":[{"file_name":"../../path/positive1.tf","similarity_id":"23486f3c10caaa350ec4e2a26d49de0969a585e7df6ec7814fe44466c8e1ff9e","line":1,"issue_type":"MissingAttribute","search_key":"resource","search_line":0,"search_value":"","expected_value":"'aws_accessanalyzer_analyzer' is set","actual_value":"'aws_accessanalyzer_analyzer' is undefined","remediation":"","remediation_type":""}]},{"query_name":"Shield Advanced Not In Use","query_id":"084c6686-2a70-4710-91b1-000393e54c12","severity":"LOW","platform":"Terraform","category":"Networking and Firewall","description":"AWS Shield Advanced should be used for Amazon Route 53 hosted zone, AWS Global Accelerator accelerator, Elastic IP Address, Elastic Load Balancing, and Amazon CloudFront Distribution to protect these resources against robust DDoS attacks","query_url":"https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/shield_protection#resource_arn","files":[{"file_name":"../../path/positive1.tf","similarity_id":"069aa314d2934c4c50746ec0045e0802e3c6f8f803ea5528c60d92917cc4d318","line":15,"issue_type":"MissingAttribute","search_key":"aws_lb[test3]","search_line":0,"search_value":"","expected_value":"aws_lb has shield advanced associated","actual_value":"aws_lb does not have shield advanced associated","remediation":"","remediation_type":""}]},{"query_name":"Resource Not Using Tags","query_id":"e38a8e0a-b88b-4902-b3fe-b0fcb17d5c10","severity":"INFO","platform":"Terraform","category":"Best Practices","description":"AWS services resource tags are an essential part of managing components. As a best practice, the field 'tags' should have additional tags defined other than 'Name'","query_url":"https://registry.terraform.io/providers/hashicorp/aws/latest/docs/guides/resource-tagging","files":[{"file_name":"../../path/positive1.tf","similarity_id":"e9119956188af27eb6b095cf0fadbf0d784270d9238e4650390e3b3d9a9756f5","line":15,"issue_type":"MissingAttribute","search_key":"aws_lb[{{test3}}]","search_line":0,"search_value":"","expected_value":"aws_lb[{{test3}}].tags is defined and not null","actual_value":"aws_lb[{{test3}}].tags is undefined or null","remediation":"","remediation_type":""}]}],"severity_counters":{"HIGH":1,"INFO":1,"LOW":3,"MEDIUM":1}} \ No newline at end of file