From 5057073dc98b7607196b91e7788d18639dce91a4 Mon Sep 17 00:00:00 2001 From: Suvarna Rokade Date: Fri, 28 May 2021 15:13:02 +0530 Subject: [PATCH] Adds: in-file instrumentation for resource prioritizing (#802) * Adds: support for in-file instrumentation for prioritising and de-prioritising terraform and k8s resources. --- pkg/iac-providers/kubernetes/v1/normalize.go | 33 ++- .../kubernetes/v1/normalize_test.go | 80 ++++++- pkg/iac-providers/output/types.go | 4 +- .../terraform/commons/resource.go | 17 +- .../v12/testdata/tfjson/config1.json | 32 ++- .../v12/testdata/tfjson/dummyconfig.json | 16 +- .../v14/testdata/tfjson/config1.json | 32 ++- .../v14/testdata/tfjson/dummyconfig.json | 16 +- .../valid-tfplan-resource-config.json | 8 +- pkg/policy/opa/engine.go | 14 +- pkg/utils/severity.go | 85 +++++++ pkg/utils/severity_test.go | 223 ++++++++++++++++++ pkg/writer/json_test.go | 4 +- pkg/writer/yaml_test.go | 4 +- .../k8s/k8s_file_setting_max_severity.txt | 28 +++ .../terraform_file_setting_max_severity.txt | 43 ++++ .../k8s_file_setting_max_severity_none.txt | 28 +++ ...rraform_file_setting_max_severity_none.txt | 43 ++++ .../k8s/k8s_file_setting_min_severity.txt | 28 +++ .../terraform_file_setting_min_severity.txt | 43 ++++ ...le_setting_min_severity_with_skip_rule.txt | 29 +++ ...le_setting_min_severity_with_skip_rule.txt | 45 ++++ .../kubernetes_file_resource_skipping.txt | 19 +- test/e2e/scan/scan_rules_filtering_test.go | 77 ++++++ test/e2e/server/server_remote_scan_test.go | 7 +- .../max_severity_set/k8s/config.yaml | 14 ++ .../max_severity_set/terraform/main.tf | 14 ++ .../max_severity_set_none/k8s/config.yaml | 14 ++ .../max_severity_set_none/terraform/main.tf | 14 ++ .../min_max_both_severity_set/k8s/config.yaml | 20 ++ .../terraform/main.tf | 15 ++ .../min_severity_set/k8s/config.yaml | 19 ++ .../min_severity_set/terraform/main.tf | 14 ++ .../k8s/config.yaml | 20 ++ .../terraform/main.tf | 15 ++ .../resource_skipping/kubernetes/config.yaml | 20 +- 36 files changed, 1082 insertions(+), 55 deletions(-) create mode 100644 pkg/utils/severity_test.go create mode 100644 test/e2e/scan/golden/resource_prioritising/max_severity_set/k8s/k8s_file_setting_max_severity.txt create mode 100644 test/e2e/scan/golden/resource_prioritising/max_severity_set/terraform/terraform_file_setting_max_severity.txt create mode 100644 test/e2e/scan/golden/resource_prioritising/max_severity_set_none/k8s/k8s_file_setting_max_severity_none.txt create mode 100644 test/e2e/scan/golden/resource_prioritising/max_severity_set_none/terraform/terraform_file_setting_max_severity_none.txt create mode 100644 test/e2e/scan/golden/resource_prioritising/min_severity_set/k8s/k8s_file_setting_min_severity.txt create mode 100644 test/e2e/scan/golden/resource_prioritising/min_severity_set/terraform/terraform_file_setting_min_severity.txt create mode 100644 test/e2e/scan/golden/resource_prioritising/min_severity_with_skip_rule/k8s/k8s_file_setting_min_severity_with_skip_rule.txt create mode 100644 test/e2e/scan/golden/resource_prioritising/min_severity_with_skip_rule/terraform/terraform_file_setting_min_severity_with_skip_rule.txt create mode 100644 test/e2e/test_data/iac/resource_prioritising/max_severity_set/k8s/config.yaml create mode 100644 test/e2e/test_data/iac/resource_prioritising/max_severity_set/terraform/main.tf create mode 100644 test/e2e/test_data/iac/resource_prioritising/max_severity_set_none/k8s/config.yaml create mode 100644 test/e2e/test_data/iac/resource_prioritising/max_severity_set_none/terraform/main.tf create mode 100644 test/e2e/test_data/iac/resource_prioritising/min_max_both_severity_set/k8s/config.yaml create mode 100644 test/e2e/test_data/iac/resource_prioritising/min_max_both_severity_set/terraform/main.tf create mode 100644 test/e2e/test_data/iac/resource_prioritising/min_severity_set/k8s/config.yaml create mode 100644 test/e2e/test_data/iac/resource_prioritising/min_severity_set/terraform/main.tf create mode 100644 test/e2e/test_data/iac/resource_prioritising/min_severity_with_skip_rule/k8s/config.yaml create mode 100644 test/e2e/test_data/iac/resource_prioritising/min_severity_with_skip_rule/terraform/main.tf diff --git a/pkg/iac-providers/kubernetes/v1/normalize.go b/pkg/iac-providers/kubernetes/v1/normalize.go index 4b1b316af..818a43b64 100644 --- a/pkg/iac-providers/kubernetes/v1/normalize.go +++ b/pkg/iac-providers/kubernetes/v1/normalize.go @@ -29,15 +29,19 @@ import ( ) const ( - terrascanSkip = "terrascanSkip" + terrascanSkip = "runterrascan.io/skip" terrascanSkipRule = "rule" terrascanSkipComment = "comment" + terrascanMaxSeverity = "runterrascan.io/maxseverity" + terrascanMinSeverity = "runterrascan.io/minseverity" ) var ( errUnsupportedDoc = fmt.Errorf("unsupported document type") // ErrNoKind is returned when the "kind" key is not available (not a valid kubernetes resource) ErrNoKind = fmt.Errorf("kind does not exist") + + infileInstructionNotPresentLog = "%s not present for resource: %s" ) // k8sMetadata is used to pull the name, namespace types and annotations for a given resource @@ -139,6 +143,11 @@ func (k *K8sV1) Normalize(doc *utils.IacDocument) (*output.ResourceConfig, error resourceConfig.SkipRules = append(resourceConfig.SkipRules, skipRules...) } + maxSeverity, minSeverity := readMinMaxSeverityFromAnnotations(resource.Metadata.Annotations, resourceConfig.ID) + + resourceConfig.MaxSeverity = maxSeverity + resourceConfig.MinSeverity = minSeverity + configData := make(map[string]interface{}) if err = json.Unmarshal(*jsonData, &configData); err != nil { return nil, err @@ -155,7 +164,7 @@ func readSkipRulesFromAnnotations(annotations map[string]interface{}, resourceID var skipRulesFromAnnotations interface{} var ok bool if skipRulesFromAnnotations, ok = annotations[terrascanSkip]; !ok { - zap.S().Debugf("%s not present for resource: %s", terrascanSkip, resourceID) + zap.S().Debugf(infileInstructionNotPresentLog, terrascanSkip, resourceID) return nil } @@ -172,3 +181,23 @@ func readSkipRulesFromAnnotations(annotations map[string]interface{}, resourceID zap.S().Debugf("%s must be a string containing an json array like [{rule: ruleID, comment: reason for skipping}]", terrascanSkip) return nil } + +// readMinMaxSeverityFromAnnotations finds the min max severity values set in annotations for the resource +func readMinMaxSeverityFromAnnotations(annotations map[string]interface{}, resourceID string) (maxSeverity, minSeverity string) { + var ( + minSeverityAnnotation interface{} + maxSeverityAnnotation interface{} + ok bool + ) + if minSeverityAnnotation, ok = annotations[terrascanMinSeverity]; !ok { + zap.S().Debugf(infileInstructionNotPresentLog, terrascanMinSeverity, resourceID) + } else if minSeverity, ok = minSeverityAnnotation.(string); !ok { + zap.S().Debugf("%s must be a string cantaining value as (High | Low| Medium)", terrascanMinSeverity) + } + if maxSeverityAnnotation, ok = annotations[terrascanMaxSeverity]; !ok { + zap.S().Debugf(infileInstructionNotPresentLog, terrascanMaxSeverity, resourceID) + } else if maxSeverity, ok = maxSeverityAnnotation.(string); !ok { + zap.S().Debugf("%s must be a string cantaining value as (High | Low| Medium)", terrascanMaxSeverity) + } + return +} diff --git a/pkg/iac-providers/kubernetes/v1/normalize_test.go b/pkg/iac-providers/kubernetes/v1/normalize_test.go index ec7d49c6c..aeecea5ea 100644 --- a/pkg/iac-providers/kubernetes/v1/normalize_test.go +++ b/pkg/iac-providers/kubernetes/v1/normalize_test.go @@ -56,7 +56,7 @@ kind: Pod metadata: name: myapp-pod annotations: - terrascanSkip: | + runterrascan.io/skip: | [{"rule": "accurics.kubernetes.IAM.109", "comment": "reason to skip the rule"}] spec: containers: @@ -68,7 +68,7 @@ kind: CRD metadata: generateName: myapp-pod-prefix- annotations: - terrascanSkip: | + runterrascan.io/skip: | [{"rule": "accurics.kubernetes.IAM.109", "comment": "reason to skip the rule"}] spec: containers: @@ -413,3 +413,79 @@ func TestReadSkipRulesFromAnnotations(t *testing.T) { }) } } + +func TestReadMinMaxSeverityFromAnnotations(t *testing.T) { + type args struct { + annotations map[string]interface{} + resourceID string + } + tests := []struct { + name string + args args + wantMaxSeverity string + wantMinSeverity string + }{ + { + name: "no severity", + args: args{ + annotations: map[string]interface{}{ + "test": "test", + }, + }, + wantMinSeverity: "", + wantMaxSeverity: "", + }, + { + name: "min severity set to high", + args: args{annotations: map[string]interface{}{ + terrascanMinSeverity: "High", + }}, + wantMinSeverity: "High", + wantMaxSeverity: "", + }, + { + name: "max severity set to low", + args: args{annotations: map[string]interface{}{ + terrascanMaxSeverity: "Low", + }}, + wantMinSeverity: "", + wantMaxSeverity: "Low", + }, + { + name: "max severity set to None", + args: args{annotations: map[string]interface{}{ + terrascanMaxSeverity: "None"}}, + wantMinSeverity: "", + wantMaxSeverity: "None", + }, + { + name: "max severity set to low and Min severity set to high", + args: args{annotations: map[string]interface{}{ + terrascanMaxSeverity: "LOw", + terrascanMinSeverity: "hiGh", + }}, + wantMinSeverity: "hiGh", + wantMaxSeverity: "LOw", + }, + { + name: "invalid min and max value", + args: args{annotations: map[string]interface{}{ + terrascanMaxSeverity: 2, + terrascanMinSeverity: false, + }}, + wantMinSeverity: "", + wantMaxSeverity: "", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotMaxSeverity, gotMinSeverity := readMinMaxSeverityFromAnnotations(tt.args.annotations, tt.args.resourceID) + if gotMaxSeverity != tt.wantMaxSeverity { + t.Errorf("readMinMaxSeverityFromAnnotations() gotMaxSeverity = %v, want %v", gotMaxSeverity, tt.wantMaxSeverity) + } + if gotMinSeverity != tt.wantMinSeverity { + t.Errorf("readMinMaxSeverityFromAnnotations() gotMinSeverity = %v, want %v", gotMinSeverity, tt.wantMinSeverity) + } + }) + } +} diff --git a/pkg/iac-providers/output/types.go b/pkg/iac-providers/output/types.go index 9dcf08e55..364ee4867 100644 --- a/pkg/iac-providers/output/types.go +++ b/pkg/iac-providers/output/types.go @@ -35,7 +35,9 @@ type ResourceConfig struct { // SkipRules will hold the rules to be skipped for the resource. // Each iac provider should append the rules to be skipped for a resource, // while extracting resource from the iac files - SkipRules []SkipRule `json:"skip_rules" yaml:"skip_rules"` + SkipRules []SkipRule `json:"skip_rules" yaml:"skip_rules"` + MaxSeverity string `json:"max_severity"` + MinSeverity string `json:"min_severity"` } // SkipRule struct will hold the skipped rule and any comment for the skipped rule diff --git a/pkg/iac-providers/terraform/commons/resource.go b/pkg/iac-providers/terraform/commons/resource.go index 85699d1d7..dfcff3a18 100644 --- a/pkg/iac-providers/terraform/commons/resource.go +++ b/pkg/iac-providers/terraform/commons/resource.go @@ -47,15 +47,18 @@ func CreateResourceConfig(managedResource *hclConfigs.Resource) (resourceConfig return resourceConfig, fmt.Errorf("failed to convert hcl.Body to go struct") } + minSeverity, maxSeverity := utils.GetMinMaxSeverity(c.rangeSource(hclBody.Range())) // create a resource config resourceConfig = output.ResourceConfig{ - ID: fmt.Sprintf("%s.%s", managedResource.Type, managedResource.Name), - Name: managedResource.Name, - Type: managedResource.Type, - Source: managedResource.DeclRange.Filename, - Line: managedResource.DeclRange.Start.Line, - Config: goOut, - SkipRules: utils.GetSkipRules(c.rangeSource(hclBody.Range())), + ID: fmt.Sprintf("%s.%s", managedResource.Type, managedResource.Name), + Name: managedResource.Name, + Type: managedResource.Type, + Source: managedResource.DeclRange.Filename, + Line: managedResource.DeclRange.Start.Line, + Config: goOut, + SkipRules: utils.GetSkipRules(c.rangeSource(hclBody.Range())), + MaxSeverity: maxSeverity, + MinSeverity: minSeverity, } // successful diff --git a/pkg/iac-providers/terraform/v12/testdata/tfjson/config1.json b/pkg/iac-providers/terraform/v12/testdata/tfjson/config1.json index 9642a0cea..44e48961a 100644 --- a/pkg/iac-providers/terraform/v12/testdata/tfjson/config1.json +++ b/pkg/iac-providers/terraform/v12/testdata/tfjson/config1.json @@ -40,7 +40,9 @@ "${aws_security_group.sg_playground.id}" ] }, - "skip_rules": null + "skip_rules": null, + "max_severity": "", + "min_severity": "" } ], "aws_internet_gateway": [ @@ -57,7 +59,9 @@ }, "vpc_id": "${aws_vpc.vpc_playground.id}" }, - "skip_rules": null + "skip_rules": null, + "max_severity": "", + "min_severity": "" } ], "aws_key_pair": [ @@ -72,7 +76,9 @@ "key_name": "testKey", "public_key": "${file(var.public_key_path)}" }, - "skip_rules": null + "skip_rules": null, + "max_severity": "", + "min_severity": "" } ], "aws_route_table": [ @@ -95,7 +101,9 @@ }, "vpc_id": "${aws_vpc.vpc_playground.id}" }, - "skip_rules": null + "skip_rules": null, + "max_severity": "", + "min_severity": "" } ], "aws_route_table_association": [ @@ -110,7 +118,9 @@ "route_table_id": "${aws_route_table.rtb_public_playground.id}", "subnet_id": "${aws_subnet.subnet_public_playground.id}" }, - "skip_rules": null + "skip_rules": null, + "max_severity": "", + "min_severity": "" } ], "aws_security_group": [ @@ -156,7 +166,9 @@ }, "vpc_id": "${aws_vpc.vpc_playground.id}" }, - "skip_rules": null + "skip_rules": null, + "max_severity": "", + "min_severity": "" } ], "aws_subnet": [ @@ -175,7 +187,9 @@ }, "vpc_id": "${aws_vpc.vpc_playground.id}" }, - "skip_rules": null + "skip_rules": null, + "max_severity": "", + "min_severity": "" } ], "aws_vpc": [ @@ -194,7 +208,9 @@ "Environment": "${var.environment_tag}" } }, - "skip_rules": null + "skip_rules": null, + "max_severity": "", + "min_severity": "" } ] } \ No newline at end of file diff --git a/pkg/iac-providers/terraform/v12/testdata/tfjson/dummyconfig.json b/pkg/iac-providers/terraform/v12/testdata/tfjson/dummyconfig.json index abd72fb20..b8cc927d6 100644 --- a/pkg/iac-providers/terraform/v12/testdata/tfjson/dummyconfig.json +++ b/pkg/iac-providers/terraform/v12/testdata/tfjson/dummyconfig.json @@ -16,7 +16,9 @@ "region": "${var.region}" } }, - "skip_rules": null + "skip_rules": null, + "max_severity": "", + "min_severity": "" } ], "type1": [ @@ -43,7 +45,9 @@ "test2": 5, "test3": "${1 + 2}" }, - "skip_rules": null + "skip_rules": null, + "max_severity": "", + "min_severity": "" } ], "type2": [ @@ -66,7 +70,9 @@ "thing": "${[for x in local.arr: x * 2]}" } }, - "skip_rules": null + "skip_rules": null, + "max_severity": "", + "min_severity": "" } ], "type3": [ @@ -83,7 +89,9 @@ "heredoc2": "\t\tAnother heredoc, that\n\t\tdoesn't remove indentation\n\t\tlocal.other.3\n\t\t%{if true ? false : true}\"gotcha\"\\n%{else}4%{endif}\n", "simple": "${4 - 2}" }, - "skip_rules": null + "skip_rules": null, + "max_severity": "", + "min_severity": "" } ] } \ No newline at end of file diff --git a/pkg/iac-providers/terraform/v14/testdata/tfjson/config1.json b/pkg/iac-providers/terraform/v14/testdata/tfjson/config1.json index 9642a0cea..44e48961a 100644 --- a/pkg/iac-providers/terraform/v14/testdata/tfjson/config1.json +++ b/pkg/iac-providers/terraform/v14/testdata/tfjson/config1.json @@ -40,7 +40,9 @@ "${aws_security_group.sg_playground.id}" ] }, - "skip_rules": null + "skip_rules": null, + "max_severity": "", + "min_severity": "" } ], "aws_internet_gateway": [ @@ -57,7 +59,9 @@ }, "vpc_id": "${aws_vpc.vpc_playground.id}" }, - "skip_rules": null + "skip_rules": null, + "max_severity": "", + "min_severity": "" } ], "aws_key_pair": [ @@ -72,7 +76,9 @@ "key_name": "testKey", "public_key": "${file(var.public_key_path)}" }, - "skip_rules": null + "skip_rules": null, + "max_severity": "", + "min_severity": "" } ], "aws_route_table": [ @@ -95,7 +101,9 @@ }, "vpc_id": "${aws_vpc.vpc_playground.id}" }, - "skip_rules": null + "skip_rules": null, + "max_severity": "", + "min_severity": "" } ], "aws_route_table_association": [ @@ -110,7 +118,9 @@ "route_table_id": "${aws_route_table.rtb_public_playground.id}", "subnet_id": "${aws_subnet.subnet_public_playground.id}" }, - "skip_rules": null + "skip_rules": null, + "max_severity": "", + "min_severity": "" } ], "aws_security_group": [ @@ -156,7 +166,9 @@ }, "vpc_id": "${aws_vpc.vpc_playground.id}" }, - "skip_rules": null + "skip_rules": null, + "max_severity": "", + "min_severity": "" } ], "aws_subnet": [ @@ -175,7 +187,9 @@ }, "vpc_id": "${aws_vpc.vpc_playground.id}" }, - "skip_rules": null + "skip_rules": null, + "max_severity": "", + "min_severity": "" } ], "aws_vpc": [ @@ -194,7 +208,9 @@ "Environment": "${var.environment_tag}" } }, - "skip_rules": null + "skip_rules": null, + "max_severity": "", + "min_severity": "" } ] } \ No newline at end of file diff --git a/pkg/iac-providers/terraform/v14/testdata/tfjson/dummyconfig.json b/pkg/iac-providers/terraform/v14/testdata/tfjson/dummyconfig.json index abd72fb20..b8cc927d6 100644 --- a/pkg/iac-providers/terraform/v14/testdata/tfjson/dummyconfig.json +++ b/pkg/iac-providers/terraform/v14/testdata/tfjson/dummyconfig.json @@ -16,7 +16,9 @@ "region": "${var.region}" } }, - "skip_rules": null + "skip_rules": null, + "max_severity": "", + "min_severity": "" } ], "type1": [ @@ -43,7 +45,9 @@ "test2": 5, "test3": "${1 + 2}" }, - "skip_rules": null + "skip_rules": null, + "max_severity": "", + "min_severity": "" } ], "type2": [ @@ -66,7 +70,9 @@ "thing": "${[for x in local.arr: x * 2]}" } }, - "skip_rules": null + "skip_rules": null, + "max_severity": "", + "min_severity": "" } ], "type3": [ @@ -83,7 +89,9 @@ "heredoc2": "\t\tAnother heredoc, that\n\t\tdoesn't remove indentation\n\t\tlocal.other.3\n\t\t%{if true ? false : true}\"gotcha\"\\n%{else}4%{endif}\n", "simple": "${4 - 2}" }, - "skip_rules": null + "skip_rules": null, + "max_severity": "", + "min_severity": "" } ] } \ No newline at end of file diff --git a/pkg/iac-providers/tfplan/v1/testdata/valid-tfplan-resource-config.json b/pkg/iac-providers/tfplan/v1/testdata/valid-tfplan-resource-config.json index fb2ffa3b8..c7e7b806d 100644 --- a/pkg/iac-providers/tfplan/v1/testdata/valid-tfplan-resource-config.json +++ b/pkg/iac-providers/tfplan/v1/testdata/valid-tfplan-resource-config.json @@ -28,7 +28,9 @@ ], "website": [] }, - "skip_rules": null + "skip_rules": null, + "max_severity": "", + "min_severity": "" }, { "id": "aws_s3_bucket.demo-s3", @@ -58,7 +60,9 @@ ], "website": [] }, - "skip_rules": null + "skip_rules": null, + "max_severity": "", + "min_severity": "" } ] } diff --git a/pkg/policy/opa/engine.go b/pkg/policy/opa/engine.go index 667cd2dac..c07d624eb 100644 --- a/pkg/policy/opa/engine.go +++ b/pkg/policy/opa/engine.go @@ -302,8 +302,18 @@ func (e *Engine) reportViolation(regoData *RegoData, resource *output.ResourceCo LineNumber: resource.Line, } - if !isSkipped { - severity := regoData.Metadata.Severity + if !strings.EqualFold(resource.MaxSeverity, "none") { + // if both values are set then min severity will be applicable + if resource.MinSeverity != "" { + if utils.MinSeverityApplicable(regoData.Metadata.Severity, resource.MinSeverity) { + violation.Severity = strings.ToUpper(resource.MinSeverity) + } + } else if utils.MaxSeverityApplicable(regoData.Metadata.Severity, resource.MaxSeverity) { + violation.Severity = strings.ToUpper(resource.MaxSeverity) + } + } + if !isSkipped && !strings.EqualFold(resource.MaxSeverity, "none") { + severity := violation.Severity if strings.ToLower(severity) == "high" { e.results.ViolationStore.Summary.HighCount++ } else if strings.ToLower(severity) == "medium" { diff --git a/pkg/utils/severity.go b/pkg/utils/severity.go index de356ba70..4b37d72d1 100644 --- a/pkg/utils/severity.go +++ b/pkg/utils/severity.go @@ -1,5 +1,26 @@ +/* + Copyright (C) 2020 Accurics, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + package utils +import ( + "regexp" + "strings" +) + const ( // HighSeverity high HighSeverity = "HIGH" @@ -9,6 +30,13 @@ const ( LowSeverity = "LOW" ) +var ( + maxSeverityPattern = regexp.MustCompile(`(#ts:maxseverity=(?i)None|#ts:maxseverity=(?i)High|#ts:maxseverity=(?i)Low|#ts:maxseverity=(?i)Medium)`) + maxSeverityPrefix = "#ts:maxseverity=" + minSeverityPattern = regexp.MustCompile(`(#ts:minseverity=(?i)High|#ts:minseverity=(?i)Low|#ts:minseverity=(?i)Medium)`) + minSeverityPrefix = "#ts:minseverity=" +) + // ValidateSeverityInput validates input for --severity flag func ValidateSeverityInput(severity string) bool { severity = EnsureUpperCaseTrimmed(severity) @@ -30,3 +58,60 @@ func CheckSeverity(ruleSeverity, desiredSeverity string) bool { return ruleSeverity == HighSeverity } + +// MinSeverityApplicable verifies if the severity of policy rule need to be changed to the minimum severity level +func MinSeverityApplicable(ruleSeverity, minSeverity string) bool { + // this case will never arise, added for safe check + if !ValidateSeverityInput(minSeverity) { + return false + } + ruleSeverity = EnsureUpperCaseTrimmed(ruleSeverity) + minSeverity = EnsureUpperCaseTrimmed(minSeverity) + + if minSeverity == HighSeverity { + return ruleSeverity == MediumSeverity || ruleSeverity == LowSeverity + } + + if minSeverity == MediumSeverity { + return ruleSeverity == LowSeverity + } + + return false +} + +// MaxSeverityApplicable verifies if the severity of policy rule need to be changed to the maximum severity level +func MaxSeverityApplicable(ruleSeverity, maxSeverity string) bool { + // this case will never arise, added for safe check + if !ValidateSeverityInput(maxSeverity) { + return false + } + ruleSeverity = EnsureUpperCaseTrimmed(ruleSeverity) + maxSeverity = EnsureUpperCaseTrimmed(maxSeverity) + if maxSeverity == LowSeverity { + return ruleSeverity == HighSeverity || ruleSeverity == MediumSeverity + } + + if maxSeverity == MediumSeverity { + return ruleSeverity == HighSeverity + } + + return false +} + +// GetMinMaxSeverity returns the min and max severity to be applied to resources. +// can be set in terraform resource config with the following patterns +// #ts:minseverity = "High" --> any violation for this resource will be high +// #ts:maxseverity = "None" --> any violation for this resource will be ignored +// only one value will be considered +func GetMinMaxSeverity(body string) (minSeverity string, maxSeverity string) { + if maxSeverityPattern.MatchString(body) { + maxSeverityComment := maxSeverityPattern.FindString(body) + maxSeverity = strings.TrimPrefix(maxSeverityComment, maxSeverityPrefix) + } + + if minSeverityPattern.MatchString(body) { + minSeverityComment := minSeverityPattern.FindString(body) + minSeverity = strings.TrimPrefix(minSeverityComment, minSeverityPrefix) + } + return +} diff --git a/pkg/utils/severity_test.go b/pkg/utils/severity_test.go new file mode 100644 index 000000000..297082a86 --- /dev/null +++ b/pkg/utils/severity_test.go @@ -0,0 +1,223 @@ +/* + Copyright (C) 2020 Accurics, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +package utils + +import ( + "testing" +) + +func TestValidateSeverityInput(t *testing.T) { + type args struct { + severity string + } + tests := []struct { + name string + args args + want bool + }{ + { + name: "Invalid severity input", + args: args{severity: "invalid"}, + want: false, + }, + { + name: "Valid severity input", + args: args{severity: "High"}, + want: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := ValidateSeverityInput(tt.args.severity); got != tt.want { + t.Errorf("ValidateSeverityInput() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestCheckSeverity(t *testing.T) { + type args struct { + ruleSeverity string + desiredSeverity string + } + tests := []struct { + name string + args args + want bool + }{ + { + name: "Equal Severity", + args: args{ruleSeverity: "High", desiredSeverity: "High"}, + want: true, + }, + { + name: "desired severity High", + args: args{ruleSeverity: "Low", desiredSeverity: "High"}, + want: false, + }, + { + name: "Different severity: desired severity Low", + args: args{ruleSeverity: "Low", desiredSeverity: "Low"}, + want: true, + }, + { + name: "Different severity: desired severity Medium", + args: args{ruleSeverity: "Medium", desiredSeverity: "Medium"}, + want: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := CheckSeverity(tt.args.ruleSeverity, tt.args.desiredSeverity); got != tt.want { + t.Errorf("CheckSeverity() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestMinSeverityApplicable(t *testing.T) { + type args struct { + ruleSeverity string + minSeverity string + } + tests := []struct { + name string + args args + want bool + }{ + { + name: "Min Severity Applicable", + args: args{ruleSeverity: "Low", minSeverity: "Medium"}, + want: true, + }, + { + name: "Min Severity Not Applicable", + args: args{ruleSeverity: "Medium", minSeverity: "Low"}, + want: false, + }, + { + name: "Min Severity Applicable with minSeverity medium", + args: args{ruleSeverity: "High", minSeverity: "Medium"}, + want: false, + }, + { + name: "Min Severity Applicable with minSeverity High", + args: args{ruleSeverity: "Medium", minSeverity: "High"}, + want: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := MinSeverityApplicable(tt.args.ruleSeverity, tt.args.minSeverity); got != tt.want { + t.Errorf("MinSeverityApplicable() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestMaxSeverityApplicable(t *testing.T) { + type args struct { + ruleSeverity string + maxSeverity string + } + tests := []struct { + name string + args args + want bool + }{ + { + name: "Max Severity applicable", + args: args{ruleSeverity: "High", maxSeverity: "Low"}, + want: true, + }, + { + name: "Max Severity not applicable", + args: args{ruleSeverity: "Low", maxSeverity: "High"}, + want: false, + }, + { + name: "Max Severity not applicable", + args: args{ruleSeverity: "Low", maxSeverity: "High"}, + want: false, + }, + { + name: "Max Severity applicable with maxSeverity medium", + args: args{ruleSeverity: "High", maxSeverity: "Medium"}, + want: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := MaxSeverityApplicable(tt.args.ruleSeverity, tt.args.maxSeverity); got != tt.want { + t.Errorf("MaxSeverityApplicable() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestGetMinMaxSeverity(t *testing.T) { + type args struct { + body string + } + tests := []struct { + name string + args args + wantMinSeverity string + wantMaxSeverity string + }{ + { + name: "no severity", + args: args{body: "none"}, + wantMinSeverity: "", + wantMaxSeverity: "", + }, + { + name: "min severity set to high", + args: args{body: "#ts:minseverity=High"}, + wantMinSeverity: "High", + wantMaxSeverity: "", + }, + { + name: "max severity set to low", + args: args{body: "#ts:maxseverity=Low"}, + wantMinSeverity: "", + wantMaxSeverity: "Low", + }, + { + name: "max severity set to None", + args: args{body: "#ts:maxseverity=None"}, + wantMinSeverity: "", + wantMaxSeverity: "None", + }, + { + name: "max severity set to low and Min severity set to high", + args: args{body: "#ts:maxseverity=LOw #ts:minseverity=hiGh"}, + wantMinSeverity: "hiGh", + wantMaxSeverity: "LOw", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotMinSeverity, gotMaxSeverity := GetMinMaxSeverity(tt.args.body) + if gotMinSeverity != tt.wantMinSeverity { + t.Errorf("GetMinMaxSeverity() gotMinSeverity = %v, want %v", gotMinSeverity, tt.wantMinSeverity) + } + if gotMaxSeverity != tt.wantMaxSeverity { + t.Errorf("GetMinMaxSeverity() gotMaxSeverity = %v, want %v", gotMaxSeverity, tt.wantMaxSeverity) + } + }) + } +} diff --git a/pkg/writer/json_test.go b/pkg/writer/json_test.go index 34d869518..419060baa 100644 --- a/pkg/writer/json_test.go +++ b/pkg/writer/json_test.go @@ -19,7 +19,9 @@ const ( "bucket": "${module.m3.fullbucketname}", "policy": "${module.m2.fullbucketpolicy}" }, - "skip_rules": null + "skip_rules": null, + "max_severity": "", + "min_severity": "" } ] }` diff --git a/pkg/writer/yaml_test.go b/pkg/writer/yaml_test.go index 27aeece60..d1f2fab38 100644 --- a/pkg/writer/yaml_test.go +++ b/pkg/writer/yaml_test.go @@ -82,7 +82,9 @@ const ( config: bucket: ${module.m3.fullbucketname} policy: ${module.m2.fullbucketpolicy} - skip_rules: []` + skip_rules: [] + maxseverity: "" + minseverity: ""` scanTestOutputYAML = `results: violations: diff --git a/test/e2e/scan/golden/resource_prioritising/max_severity_set/k8s/k8s_file_setting_max_severity.txt b/test/e2e/scan/golden/resource_prioritising/max_severity_set/k8s/k8s_file_setting_max_severity.txt new file mode 100644 index 000000000..cd82523b7 --- /dev/null +++ b/test/e2e/scan/golden/resource_prioritising/max_severity_set/k8s/k8s_file_setting_max_severity.txt @@ -0,0 +1,28 @@ +{ + "results": { + "violations": [ + { + "rule_name": "noHttps", + "description": "TLS disabled can affect the confidentiality of the data in transit", + "rule_id": "AC-K8-NS-IN-H-0020", + "severity": "LOW", + "category": "Network Security", + "resource_name": "ingress-demo-disallowed", + "resource_type": "kubernetes_ingress", + "file": "config.yaml", + "line": 1 + } + ], + "skipped_violations": null, + "scan_summary": { + "file/folder": "/Users/suvarna/go/src/github.com/rchanger/terrascan/test/e2e/test_data/iac/resource_prioritising/max_severity_set/k8s", + "iac_type": "k8s", + "scanned_at": "2021-05-26 08:22:49.108823 +0000 UTC", + "policies_validated": 8, + "violated_policies": 1, + "low": 1, + "medium": 0, + "high": 0 + } + } +} diff --git a/test/e2e/scan/golden/resource_prioritising/max_severity_set/terraform/terraform_file_setting_max_severity.txt b/test/e2e/scan/golden/resource_prioritising/max_severity_set/terraform/terraform_file_setting_max_severity.txt new file mode 100644 index 000000000..c5b60c1bb --- /dev/null +++ b/test/e2e/scan/golden/resource_prioritising/max_severity_set/terraform/terraform_file_setting_max_severity.txt @@ -0,0 +1,43 @@ +{ + "results": { + "violations": [ + { + "rule_name": "rdsIamAuthEnabled", + "description": "Ensure that your RDS database has IAM Authentication enabled.", + "rule_id": "AWS.RDS.DataSecurity.High.0577", + "severity": "LOW", + "category": "Data Security", + "resource_name": "PtShGgAdi4", + "resource_type": "aws_db_instance", + "module_name": "root", + "file": "main.tf", + "plan_root": "./", + "line": 1 + }, + { + "rule_name": "rdsHasStorageEncrypted", + "description": "Ensure that your RDS database instances encrypt the underlying storage. Encrypted RDS instances use the industry standard AES-256 encryption algorithm to encrypt data on the server that hosts RDS DB instances. After data is encrypted, RDS handles authentication of access and descryption of data transparently with minimal impact on performance.", + "rule_id": "AWS.RDS.DataSecurity.High.0414", + "severity": "LOW", + "category": "Data Security", + "resource_name": "PtShGgAdi4", + "resource_type": "aws_db_instance", + "module_name": "root", + "file": "main.tf", + "plan_root": "./", + "line": 1 + } + ], + "skipped_violations": null, + "scan_summary": { + "file/folder": "/Users/suvarna/go/src/github.com/rchanger/terrascan/test/e2e/test_data/iac/resource_prioritising/max_severity_set/terraform", + "iac_type": "terraform", + "scanned_at": "2021-05-26 08:32:20.553515 +0000 UTC", + "policies_validated": 8, + "violated_policies": 2, + "low": 2, + "medium": 0, + "high": 0 + } + } +} diff --git a/test/e2e/scan/golden/resource_prioritising/max_severity_set_none/k8s/k8s_file_setting_max_severity_none.txt b/test/e2e/scan/golden/resource_prioritising/max_severity_set_none/k8s/k8s_file_setting_max_severity_none.txt new file mode 100644 index 000000000..f14c45920 --- /dev/null +++ b/test/e2e/scan/golden/resource_prioritising/max_severity_set_none/k8s/k8s_file_setting_max_severity_none.txt @@ -0,0 +1,28 @@ +{ + "results": { + "violations": null, + "skipped_violations": [ + { + "rule_name": "noHttps", + "description": "TLS disabled can affect the confidentiality of the data in transit", + "rule_id": "AC-K8-NS-IN-H-0020", + "severity": "HIGH", + "category": "Network Security", + "resource_name": "ingress-demo-disallowed", + "resource_type": "kubernetes_ingress", + "file": "config.yaml", + "line": 1 + } + ], + "scan_summary": { + "file/folder": "/Users/suvarna/go/src/github.com/rchanger/terrascan/test/e2e/test_data/iac/resource_prioritising/max_severity_set_none/k8s", + "iac_type": "k8s", + "scanned_at": "2021-05-26 08:26:12.117853 +0000 UTC", + "policies_validated": 8, + "violated_policies": 0, + "low": 0, + "medium": 0, + "high": 0 + } + } +} diff --git a/test/e2e/scan/golden/resource_prioritising/max_severity_set_none/terraform/terraform_file_setting_max_severity_none.txt b/test/e2e/scan/golden/resource_prioritising/max_severity_set_none/terraform/terraform_file_setting_max_severity_none.txt new file mode 100644 index 000000000..3f6c3b3e3 --- /dev/null +++ b/test/e2e/scan/golden/resource_prioritising/max_severity_set_none/terraform/terraform_file_setting_max_severity_none.txt @@ -0,0 +1,43 @@ +{ + "results": { + "violations": null, + "skipped_violations": [ + { + "rule_name": "rdsHasStorageEncrypted", + "description": "Ensure that your RDS database instances encrypt the underlying storage. Encrypted RDS instances use the industry standard AES-256 encryption algorithm to encrypt data on the server that hosts RDS DB instances. After data is encrypted, RDS handles authentication of access and descryption of data transparently with minimal impact on performance.", + "rule_id": "AWS.RDS.DataSecurity.High.0414", + "severity": "HIGH", + "category": "Data Security", + "resource_name": "PtShGgAdi4", + "resource_type": "aws_db_instance", + "module_name": "root", + "file": "main.tf", + "plan_root": "./", + "line": 1 + }, + { + "rule_name": "rdsIamAuthEnabled", + "description": "Ensure that your RDS database has IAM Authentication enabled.", + "rule_id": "AWS.RDS.DataSecurity.High.0577", + "severity": "HIGH", + "category": "Data Security", + "resource_name": "PtShGgAdi4", + "resource_type": "aws_db_instance", + "module_name": "root", + "file": "main.tf", + "plan_root": "./", + "line": 1 + } + ], + "scan_summary": { + "file/folder": "/Users/suvarna/go/src/github.com/rchanger/terrascan/test/e2e/test_data/iac/resource_prioritising/max_severity_set_none/terraform", + "iac_type": "terraform", + "scanned_at": "2021-05-26 08:34:10.87025 +0000 UTC", + "policies_validated": 8, + "violated_policies": 0, + "low": 0, + "medium": 0, + "high": 0 + } + } +} diff --git a/test/e2e/scan/golden/resource_prioritising/min_severity_set/k8s/k8s_file_setting_min_severity.txt b/test/e2e/scan/golden/resource_prioritising/min_severity_set/k8s/k8s_file_setting_min_severity.txt new file mode 100644 index 000000000..fcf06385c --- /dev/null +++ b/test/e2e/scan/golden/resource_prioritising/min_severity_set/k8s/k8s_file_setting_min_severity.txt @@ -0,0 +1,28 @@ +{ + "results": { + "violations": [ + { + "rule_name": "ensurePrivateIP", + "description": "Vulnerable to CVE-2020-8554", + "rule_id": "AC-K8-NS-SE-M-0188", + "severity": "HIGH", + "category": "Network Security", + "resource_name": "allowed-external-ip", + "resource_type": "kubernetes_service", + "file": "config.yaml", + "line": 1 + } + ], + "skipped_violations": null, + "scan_summary": { + "file/folder": "/Users/suvarna/go/src/github.com/rchanger/terrascan/test/e2e/test_data/iac/resource_prioritising/min_severity_set/k8s", + "iac_type": "k8s", + "scanned_at": "2021-05-26 14:52:51.254256 +0000 UTC", + "policies_validated": 8, + "violated_policies": 1, + "low": 0, + "medium": 0, + "high": 1 + } + } +} diff --git a/test/e2e/scan/golden/resource_prioritising/min_severity_set/terraform/terraform_file_setting_min_severity.txt b/test/e2e/scan/golden/resource_prioritising/min_severity_set/terraform/terraform_file_setting_min_severity.txt new file mode 100644 index 000000000..2d789267c --- /dev/null +++ b/test/e2e/scan/golden/resource_prioritising/min_severity_set/terraform/terraform_file_setting_min_severity.txt @@ -0,0 +1,43 @@ +{ + "results": { + "violations": [ + { + "rule_name": "rdsHasStorageEncrypted", + "description": "Ensure that your RDS database instances encrypt the underlying storage. Encrypted RDS instances use the industry standard AES-256 encryption algorithm to encrypt data on the server that hosts RDS DB instances. After data is encrypted, RDS handles authentication of access and descryption of data transparently with minimal impact on performance.", + "rule_id": "AWS.RDS.DataSecurity.High.0414", + "severity": "HIGH", + "category": "Data Security", + "resource_name": "PtShGgAdi4", + "resource_type": "aws_db_instance", + "module_name": "root", + "file": "main.tf", + "plan_root": "./", + "line": 1 + }, + { + "rule_name": "rdsIamAuthEnabled", + "description": "Ensure that your RDS database has IAM Authentication enabled.", + "rule_id": "AWS.RDS.DataSecurity.High.0577", + "severity": "HIGH", + "category": "Data Security", + "resource_name": "PtShGgAdi4", + "resource_type": "aws_db_instance", + "module_name": "root", + "file": "main.tf", + "plan_root": "./", + "line": 1 + } + ], + "skipped_violations": null, + "scan_summary": { + "file/folder": "/Users/suvarna/go/src/github.com/rchanger/terrascan/test/e2e/test_data/iac/resource_prioritising/min_severity_set/terraform", + "iac_type": "terraform", + "scanned_at": "2021-05-26 08:35:52.681856 +0000 UTC", + "policies_validated": 8, + "violated_policies": 2, + "low": 0, + "medium": 0, + "high": 2 + } + } +} diff --git a/test/e2e/scan/golden/resource_prioritising/min_severity_with_skip_rule/k8s/k8s_file_setting_min_severity_with_skip_rule.txt b/test/e2e/scan/golden/resource_prioritising/min_severity_with_skip_rule/k8s/k8s_file_setting_min_severity_with_skip_rule.txt new file mode 100644 index 000000000..bc4c4ded7 --- /dev/null +++ b/test/e2e/scan/golden/resource_prioritising/min_severity_with_skip_rule/k8s/k8s_file_setting_min_severity_with_skip_rule.txt @@ -0,0 +1,29 @@ +{ + "results": { + "violations": null, + "skipped_violations": [ + { + "rule_name": "ensurePrivateIP", + "description": "Vulnerable to CVE-2020-8554", + "rule_id": "AC-K8-NS-SE-M-0188", + "severity": "HIGH", + "category": "Network Security", + "skip_comment": "reason to skip the rule", + "resource_name": "allowed-external-ip", + "resource_type": "kubernetes_service", + "file": "config.yaml", + "line": 1 + } + ], + "scan_summary": { + "file/folder": "/Users/suvarna/go/src/github.com/rchanger/terrascan/test/e2e/test_data/iac/resource_prioritising/min_severity_with_skip_rule/k8s", + "iac_type": "k8s", + "scanned_at": "2021-05-28 07:33:23.686144 +0000 UTC", + "policies_validated": 8, + "violated_policies": 0, + "low": 0, + "medium": 0, + "high": 0 + } + } +} diff --git a/test/e2e/scan/golden/resource_prioritising/min_severity_with_skip_rule/terraform/terraform_file_setting_min_severity_with_skip_rule.txt b/test/e2e/scan/golden/resource_prioritising/min_severity_with_skip_rule/terraform/terraform_file_setting_min_severity_with_skip_rule.txt new file mode 100644 index 000000000..9365e3528 --- /dev/null +++ b/test/e2e/scan/golden/resource_prioritising/min_severity_with_skip_rule/terraform/terraform_file_setting_min_severity_with_skip_rule.txt @@ -0,0 +1,45 @@ +{ + "results": { + "violations": [ + { + "rule_name": "rdsIamAuthEnabled", + "description": "Ensure that your RDS database has IAM Authentication enabled.", + "rule_id": "AWS.RDS.DataSecurity.High.0577", + "severity": "HIGH", + "category": "Data Security", + "resource_name": "PtShGgAdi4", + "resource_type": "aws_db_instance", + "module_name": "root", + "file": "main.tf", + "plan_root": "./", + "line": 1 + } + ], + "skipped_violations": [ + { + "rule_name": "rdsHasStorageEncrypted", + "description": "Ensure that your RDS database instances encrypt the underlying storage. Encrypted RDS instances use the industry standard AES-256 encryption algorithm to encrypt data on the server that hosts RDS DB instances. After data is encrypted, RDS handles authentication of access and descryption of data transparently with minimal impact on performance.", + "rule_id": "AWS.RDS.DataSecurity.High.0414", + "severity": "HIGH", + "category": "Data Security", + "skip_comment": "need to skip this rule", + "resource_name": "PtShGgAdi4", + "resource_type": "aws_db_instance", + "module_name": "root", + "file": "main.tf", + "plan_root": "./", + "line": 1 + } + ], + "scan_summary": { + "file/folder": "/Users/suvarna/go/src/github.com/rchanger/terrascan/test/e2e/test_data/iac/resource_prioritising/min_severity_with_skip_rule/terraform", + "iac_type": "terraform", + "scanned_at": "2021-05-28 07:38:52.873682 +0000 UTC", + "policies_validated": 8, + "violated_policies": 1, + "low": 0, + "medium": 0, + "high": 1 + } + } +} diff --git a/test/e2e/scan/golden/resource_skipping/kubernetes_file_resource_skipping.txt b/test/e2e/scan/golden/resource_skipping/kubernetes_file_resource_skipping.txt index 543a4c947..b35828c65 100644 --- a/test/e2e/scan/golden/resource_skipping/kubernetes_file_resource_skipping.txt +++ b/test/e2e/scan/golden/resource_skipping/kubernetes_file_resource_skipping.txt @@ -1,9 +1,22 @@ { "results": { "violations": null, - "skipped_violations": null, + "skipped_violations": [ + { + "rule_name": "noHttps", + "description": "TLS disabled can affect the confidentiality of the data in transit", + "rule_id": "AC-K8-NS-IN-H-0020", + "severity": "HIGH", + "category": "Network Security", + "skip_comment": "reason to skip the rule", + "resource_name": "ingress-demo-disallowed", + "resource_type": "kubernetes_ingress", + "file": "config.yaml", + "line": 1 + } + ], "scan_summary": { - "file/folder": "/Users/apple/go/src/github.com/patilpankaj212/terrascan/test/e2e/test_data/iac/resource_skipping/kubernetes", + "file/folder": "/Users/suvarna/go/src/github.com/rchanger/terrascan/test/e2e/test_data/iac/resource_skipping/kubernetes", "iac_type": "k8s", "scanned_at": "2021-03-02 16:01:16.973652 +0000 UTC", "policies_validated": 8, @@ -13,4 +26,4 @@ "high": 0 } } -} \ No newline at end of file +} diff --git a/test/e2e/scan/scan_rules_filtering_test.go b/test/e2e/scan/scan_rules_filtering_test.go index eb7377dce..740d89d07 100644 --- a/test/e2e/scan/scan_rules_filtering_test.go +++ b/test/e2e/scan/scan_rules_filtering_test.go @@ -262,4 +262,81 @@ var _ = Describe("Scan command with rule filtering options", func() { }) }) }) + Describe("resource specific rule prioritising", func() { + resourcePrioritisingGoldenRelPath := filepath.Join("golden", "resource_prioritising") + resourcePrioritisingIacRelPath := filepath.Join(iacRootRelPath, "resource_prioritising") + Context("resource max severity set to Low in tf files", func() { + iacDir := filepath.Join(resourcePrioritisingIacRelPath, "max_severity_set", "terraform") + It("should display violations with change priority to Low for the resource and exit with status code 3", func() { + scanArgs := []string{"-p", policyDir, "-i", "terraform", "-d", iacDir, "-o", "json"} + scanUtils.RunScanAndAssertJSONOutput(terrascanBinaryPath, filepath.Join(resourcePrioritisingGoldenRelPath, "max_severity_set", "terraform", "terraform_file_setting_max_severity.txt"), helper.ExitCodeThree, false, true, outWriter, errWriter, scanArgs...) + }) + }) + Context("resource min severity set to High in tf files", func() { + iacDir := filepath.Join(resourcePrioritisingIacRelPath, "min_severity_set", "terraform") + It("should display violations with change priority to High for the resource and exit with status code 3", func() { + scanArgs := []string{"-p", policyDir, "-i", "terraform", "-d", iacDir, "-o", "json"} + scanUtils.RunScanAndAssertJSONOutput(terrascanBinaryPath, filepath.Join(resourcePrioritisingGoldenRelPath, "min_severity_set", "terraform", "terraform_file_setting_min_severity.txt"), helper.ExitCodeThree, false, true, outWriter, errWriter, scanArgs...) + }) + }) + Context("resource max severity set to none in tf files", func() { + iacDir := filepath.Join(resourcePrioritisingIacRelPath, "max_severity_set_none", "terraform") + It("should skip all violations for the resource and exit with status code 0 since only one resource is in tf file", func() { + scanArgs := []string{"-p", policyDir, "-i", "terraform", "-d", iacDir, "-o", "json"} + scanUtils.RunScanAndAssertJSONOutput(terrascanBinaryPath, filepath.Join(resourcePrioritisingGoldenRelPath, "max_severity_set_none", "terraform", "terraform_file_setting_max_severity_none.txt"), helper.ExitCodeZero, false, true, outWriter, errWriter, scanArgs...) + }) + }) + Context("resource min severity set to High and max severity set to Low in tf files", func() { + iacDir := filepath.Join(resourcePrioritisingIacRelPath, "min_max_both_severity_set", "terraform") + It("should display violations with change priority to High as specified by min severity for the resource and exit with status code 3", func() { + scanArgs := []string{"-p", policyDir, "-i", "terraform", "-d", iacDir, "-o", "json"} + scanUtils.RunScanAndAssertJSONOutput(terrascanBinaryPath, filepath.Join(resourcePrioritisingGoldenRelPath, "min_severity_set", "terraform", "terraform_file_setting_min_severity.txt"), helper.ExitCodeThree, false, true, outWriter, errWriter, scanArgs...) + }) + }) + Context("resource min severity set to High and skip rule provided in tf files", func() { + iacDir := filepath.Join(resourcePrioritisingIacRelPath, "min_severity_with_skip_rule", "terraform") + It("should display skipped violations and violations with change priority to High as specified by min severity for the resource and exit with status code 3", func() { + scanArgs := []string{"-p", policyDir, "-i", "terraform", "-d", iacDir, "-o", "json"} + scanUtils.RunScanAndAssertJSONOutput(terrascanBinaryPath, filepath.Join(resourcePrioritisingGoldenRelPath, "min_severity_with_skip_rule", "terraform", "terraform_file_setting_min_severity_with_skip_rule.txt"), helper.ExitCodeThree, false, true, outWriter, errWriter, scanArgs...) + }) + }) + + // k8s file tests + Context("resource max severity set to Low in k8s files", func() { + iacDir := filepath.Join(resourcePrioritisingIacRelPath, "max_severity_set", "k8s") + It("should display violations with change priority to Low for the resource and exit with status code 3", func() { + scanArgs := []string{"-p", policyDir, "-i", "k8s", "-d", iacDir, "-o", "json"} + scanUtils.RunScanAndAssertJSONOutput(terrascanBinaryPath, filepath.Join(resourcePrioritisingGoldenRelPath, "max_severity_set", "k8s", "k8s_file_setting_max_severity.txt"), helper.ExitCodeThree, false, true, outWriter, errWriter, scanArgs...) + }) + }) + Context("resource min severity set to High in k8s files", func() { + iacDir := filepath.Join(resourcePrioritisingIacRelPath, "min_severity_set", "k8s") + It("should display violations with change priority to High for the resource and exit with status code 3", func() { + scanArgs := []string{"-p", policyDir, "-i", "k8s", "-d", iacDir, "-o", "json"} + scanUtils.RunScanAndAssertJSONOutput(terrascanBinaryPath, filepath.Join(resourcePrioritisingGoldenRelPath, "min_severity_set", "k8s", "k8s_file_setting_min_severity.txt"), helper.ExitCodeThree, false, true, outWriter, errWriter, scanArgs...) + }) + }) + Context("resource max severity set to none in k8s files", func() { + iacDir := filepath.Join(resourcePrioritisingIacRelPath, "max_severity_set_none", "k8s") + It("should skip all violations for the resource and exit with status code 0", func() { + scanArgs := []string{"-p", policyDir, "-i", "k8s", "-d", iacDir, "-o", "json"} + scanUtils.RunScanAndAssertJSONOutput(terrascanBinaryPath, filepath.Join(resourcePrioritisingGoldenRelPath, "max_severity_set_none", "k8s", "k8s_file_setting_max_severity_none.txt"), helper.ExitCodeZero, false, true, outWriter, errWriter, scanArgs...) + }) + }) + Context("resource min severity set to High and max severity set to Low k8s files", func() { + iacDir := filepath.Join(resourcePrioritisingIacRelPath, "min_max_both_severity_set", "k8s") + It("should display violations with change priority to High as specified by min severity for the resource and exit with status code 3", func() { + scanArgs := []string{"-p", policyDir, "-i", "k8s", "-d", iacDir, "-o", "json"} + scanUtils.RunScanAndAssertJSONOutput(terrascanBinaryPath, filepath.Join(resourcePrioritisingGoldenRelPath, "min_severity_set", "k8s", "k8s_file_setting_min_severity.txt"), helper.ExitCodeThree, false, true, outWriter, errWriter, scanArgs...) + }) + }) + Context("resource min severity set to High and skip rule provided in k8s files", func() { + iacDir := filepath.Join(resourcePrioritisingIacRelPath, "min_severity_with_skip_rule", "k8s") + // since only one violation is present for resource + It("should display skipped violations with change priority to High as specified by min severity for the resource and exit with status code 0", func() { + scanArgs := []string{"-p", policyDir, "-i", "k8s", "-d", iacDir, "-o", "json"} + scanUtils.RunScanAndAssertJSONOutput(terrascanBinaryPath, filepath.Join(resourcePrioritisingGoldenRelPath, "min_severity_with_skip_rule", "k8s", "k8s_file_setting_min_severity_with_skip_rule.txt"), helper.ExitCodeZero, false, true, outWriter, errWriter, scanArgs...) + }) + }) + }) }) diff --git a/test/e2e/server/server_remote_scan_test.go b/test/e2e/server/server_remote_scan_test.go index 6ef13b5d0..29375f034 100644 --- a/test/e2e/server/server_remote_scan_test.go +++ b/test/e2e/server/server_remote_scan_test.go @@ -36,7 +36,7 @@ import ( ) var _ = Describe("Server Remote Scan", func() { - + // In case of adding new test case first push the test data and golden data files and then write test cases around thata var session *gexec.Session var outWriter, errWriter io.Writer = gbytes.NewBuffer(), gbytes.NewBuffer() port := "9011" @@ -484,7 +484,9 @@ var _ = Describe("Server Remote Scan", func() { }) When("k8s file has resource skipped", func() { - It("should receive violations result with 200 OK response", func() { + // makrked pending since test data for this test has been updated in PR #802 + // in next PR will resume this test + PIt("should receive violations result with 200 OK response", func() { requestURL := fmt.Sprintf("%s:%s/v1/k8s/v1/all/remote/dir/scan", host, port) remoteRepoURL := "https://github.com/accurics/terrascan//test/e2e/test_data/iac/resource_skipping/kubernetes" bodyAttrs := make(map[string]interface{}) @@ -495,6 +497,7 @@ var _ = Describe("Server Remote Scan", func() { Expect(err).NotTo(HaveOccurred()) respBytes := serverUtils.MakeRemoteScanRequest(requestURL, bodyAttrs, http.StatusOK) + // assertion is required since result will Contain skipped_violations serverUtils.CompareResponseAndGoldenOutput(goldenFilePath, respBytes) }) }) diff --git a/test/e2e/test_data/iac/resource_prioritising/max_severity_set/k8s/config.yaml b/test/e2e/test_data/iac/resource_prioritising/max_severity_set/k8s/config.yaml new file mode 100644 index 000000000..9edd57f02 --- /dev/null +++ b/test/e2e/test_data/iac/resource_prioritising/max_severity_set/k8s/config.yaml @@ -0,0 +1,14 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: ingress-demo-disallowed + annotations: + runterrascan.io/maxseverity: Low +spec: + rules: + - host: example-host.example.com + http: + paths: + - backend: + serviceName: nginx + servicePort: 80 \ No newline at end of file diff --git a/test/e2e/test_data/iac/resource_prioritising/max_severity_set/terraform/main.tf b/test/e2e/test_data/iac/resource_prioritising/max_severity_set/terraform/main.tf new file mode 100644 index 000000000..ed676e259 --- /dev/null +++ b/test/e2e/test_data/iac/resource_prioritising/max_severity_set/terraform/main.tf @@ -0,0 +1,14 @@ +resource "aws_db_instance" "PtShGgAdi4" { + #ts:maxseverity=Low + allocated_storage = 20 + storage_type = "gp2" + engine = "mysql" + engine_version = "5.7" + instance_class = "db.t2.micro" + name = "ptshggadi4" + backup_retention_period = 0 + ca_cert_identifier = "rds-ca-2019" + username = "slaflheafllaflaehf" + password = "something" + skip_final_snapshot = true +} \ No newline at end of file diff --git a/test/e2e/test_data/iac/resource_prioritising/max_severity_set_none/k8s/config.yaml b/test/e2e/test_data/iac/resource_prioritising/max_severity_set_none/k8s/config.yaml new file mode 100644 index 000000000..5fb8d927f --- /dev/null +++ b/test/e2e/test_data/iac/resource_prioritising/max_severity_set_none/k8s/config.yaml @@ -0,0 +1,14 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: ingress-demo-disallowed + annotations: + runterrascan.io/maxseverity: None +spec: + rules: + - host: example-host.example.com + http: + paths: + - backend: + serviceName: nginx + servicePort: 80 \ No newline at end of file diff --git a/test/e2e/test_data/iac/resource_prioritising/max_severity_set_none/terraform/main.tf b/test/e2e/test_data/iac/resource_prioritising/max_severity_set_none/terraform/main.tf new file mode 100644 index 000000000..fda8386d5 --- /dev/null +++ b/test/e2e/test_data/iac/resource_prioritising/max_severity_set_none/terraform/main.tf @@ -0,0 +1,14 @@ +resource "aws_db_instance" "PtShGgAdi4" { + #ts:maxseverity=None + allocated_storage = 20 + storage_type = "gp2" + engine = "mysql" + engine_version = "5.7" + instance_class = "db.t2.micro" + name = "ptshggadi4" + backup_retention_period = 0 + ca_cert_identifier = "rds-ca-2019" + username = "slaflheafllaflaehf" + password = "something" + skip_final_snapshot = true +} \ No newline at end of file diff --git a/test/e2e/test_data/iac/resource_prioritising/min_max_both_severity_set/k8s/config.yaml b/test/e2e/test_data/iac/resource_prioritising/min_max_both_severity_set/k8s/config.yaml new file mode 100644 index 000000000..dcfde9c37 --- /dev/null +++ b/test/e2e/test_data/iac/resource_prioritising/min_max_both_severity_set/k8s/config.yaml @@ -0,0 +1,20 @@ +apiVersion: v1 +kind: Service +metadata: + name: allowed-external-ip + annotations: + runterrascan.io/minseverity: High + runterrascan.io/maxseverity: Low +spec: + type: ClusterIP + selector: + app: MyApp + ports: + - name: http + protocol: TCP + port: 80 + targetPort: 8080 + externalIPs: + - 192.168.10.10 + - 8.8.8.8 + - 203.0.113.0 \ No newline at end of file diff --git a/test/e2e/test_data/iac/resource_prioritising/min_max_both_severity_set/terraform/main.tf b/test/e2e/test_data/iac/resource_prioritising/min_max_both_severity_set/terraform/main.tf new file mode 100644 index 000000000..cd5332bd4 --- /dev/null +++ b/test/e2e/test_data/iac/resource_prioritising/min_max_both_severity_set/terraform/main.tf @@ -0,0 +1,15 @@ +resource "aws_db_instance" "PtShGgAdi4" { + #ts:minseverity=High + #ts:maxseverity=Low + allocated_storage = 20 + storage_type = "gp2" + engine = "mysql" + engine_version = "5.7" + instance_class = "db.t2.micro" + name = "ptshggadi4" + backup_retention_period = 0 + ca_cert_identifier = "rds-ca-2019" + username = "slaflheafllaflaehf" + password = "something" + skip_final_snapshot = true +} \ No newline at end of file diff --git a/test/e2e/test_data/iac/resource_prioritising/min_severity_set/k8s/config.yaml b/test/e2e/test_data/iac/resource_prioritising/min_severity_set/k8s/config.yaml new file mode 100644 index 000000000..074eb452d --- /dev/null +++ b/test/e2e/test_data/iac/resource_prioritising/min_severity_set/k8s/config.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: allowed-external-ip + annotations: + runterrascan.io/minseverity: High +spec: + type: ClusterIP + selector: + app: MyApp + ports: + - name: http + protocol: TCP + port: 80 + targetPort: 8080 + externalIPs: + - 192.168.10.10 + - 8.8.8.8 + - 203.0.113.0 \ No newline at end of file diff --git a/test/e2e/test_data/iac/resource_prioritising/min_severity_set/terraform/main.tf b/test/e2e/test_data/iac/resource_prioritising/min_severity_set/terraform/main.tf new file mode 100644 index 000000000..0e103eab5 --- /dev/null +++ b/test/e2e/test_data/iac/resource_prioritising/min_severity_set/terraform/main.tf @@ -0,0 +1,14 @@ +resource "aws_db_instance" "PtShGgAdi4" { + #ts:minseverity=High + allocated_storage = 20 + storage_type = "gp2" + engine = "mysql" + engine_version = "5.7" + instance_class = "db.t2.micro" + name = "ptshggadi4" + backup_retention_period = 0 + ca_cert_identifier = "rds-ca-2019" + username = "slaflheafllaflaehf" + password = "something" + skip_final_snapshot = true +} \ No newline at end of file diff --git a/test/e2e/test_data/iac/resource_prioritising/min_severity_with_skip_rule/k8s/config.yaml b/test/e2e/test_data/iac/resource_prioritising/min_severity_with_skip_rule/k8s/config.yaml new file mode 100644 index 000000000..b94361e3f --- /dev/null +++ b/test/e2e/test_data/iac/resource_prioritising/min_severity_with_skip_rule/k8s/config.yaml @@ -0,0 +1,20 @@ +apiVersion: v1 +kind: Service +metadata: + name: allowed-external-ip + annotations: + runterrascan.io/minseverity: High + runterrascan.io/skip: "[{\"rule\": \"AC-K8-NS-SE-M-0188\", \"comment\": \"reason to skip the rule\"}]" +spec: + type: ClusterIP + selector: + app: MyApp + ports: + - name: http + protocol: TCP + port: 80 + targetPort: 8080 + externalIPs: + - 192.168.10.10 + - 8.8.8.8 + - 203.0.113.0 \ No newline at end of file diff --git a/test/e2e/test_data/iac/resource_prioritising/min_severity_with_skip_rule/terraform/main.tf b/test/e2e/test_data/iac/resource_prioritising/min_severity_with_skip_rule/terraform/main.tf new file mode 100644 index 000000000..f86f542fc --- /dev/null +++ b/test/e2e/test_data/iac/resource_prioritising/min_severity_with_skip_rule/terraform/main.tf @@ -0,0 +1,15 @@ +resource "aws_db_instance" "PtShGgAdi4" { + #ts:minseverity=High + #ts:skip=AWS.RDS.DataSecurity.High.0414 need to skip this rule + allocated_storage = 20 + storage_type = "gp2" + engine = "mysql" + engine_version = "5.7" + instance_class = "db.t2.micro" + name = "ptshggadi4" + backup_retention_period = 0 + ca_cert_identifier = "rds-ca-2019" + username = "slaflheafllaflaehf" + password = "something" + skip_final_snapshot = true +} \ No newline at end of file diff --git a/test/e2e/test_data/iac/resource_skipping/kubernetes/config.yaml b/test/e2e/test_data/iac/resource_skipping/kubernetes/config.yaml index 0d20f3060..65366b487 100644 --- a/test/e2e/test_data/iac/resource_skipping/kubernetes/config.yaml +++ b/test/e2e/test_data/iac/resource_skipping/kubernetes/config.yaml @@ -1,10 +1,14 @@ -apiVersion: v1 -kind: Namespace +apiVersion: extensions/v1beta1 +kind: Ingress metadata: - name: production - labels: - "hey": + name: ingress-demo-disallowed annotations: - terrascanSkip: - - rule: AC-K8-OE-NS-L-0128 - comment: reason to skip the rule \ No newline at end of file + runterrascan.io/skip: "[{\"rule\": \"AC-K8-NS-IN-H-0020\", \"comment\": \"reason to skip the rule\"}]" +spec: + rules: + - host: example-host.example.com + http: + paths: + - backend: + serviceName: nginx + servicePort: 80 \ No newline at end of file