From 7f757213cd5390b364d05599c315b278ffee00f1 Mon Sep 17 00:00:00 2001 From: Nikita Pivkin Date: Wed, 21 Aug 2024 17:44:50 +0600 Subject: [PATCH 1/4] refactor(checks): migrate AWS ecr, efs and eks to Rego Signed-off-by: Nikita Pivkin --- avd_docs/aws/ecr/AVD-AWS-0030/docs.md | 3 +- avd_docs/aws/ecr/AVD-AWS-0031/docs.md | 4 +- avd_docs/aws/ecr/AVD-AWS-0032/docs.md | 3 +- avd_docs/aws/ecr/AVD-AWS-0033/docs.md | 3 +- avd_docs/aws/efs/AVD-AWS-0037/docs.md | 3 +- avd_docs/aws/eks/AVD-AWS-0038/docs.md | 3 +- avd_docs/aws/eks/AVD-AWS-0039/docs.md | 3 +- avd_docs/aws/eks/AVD-AWS-0040/docs.md | 3 +- avd_docs/aws/eks/AVD-AWS-0041/docs.md | 3 +- checks/cloud/aws/ecr/enable_image_scans.go | 3 +- checks/cloud/aws/ecr/enable_image_scans.rego | 41 ++++ .../cloud/aws/ecr/enable_image_scans_test.go | 71 ------ .../aws/ecr/enable_image_scans_test.rego | 18 ++ .../aws/ecr/enforce_immutable_repository.go | 3 +- .../aws/ecr/enforce_immutable_repository.rego | 42 ++++ .../ecr/enforce_immutable_repository_test.go | 65 ------ .../enforce_immutable_repository_test.rego | 18 ++ checks/cloud/aws/ecr/no_public_access.go | 3 +- checks/cloud/aws/ecr/no_public_access.rego | 57 +++++ checks/cloud/aws/ecr/no_public_access_test.go | 138 ----------- .../cloud/aws/ecr/no_public_access_test.rego | 35 +++ .../cloud/aws/ecr/repository_customer_key.go | 3 +- .../aws/ecr/repository_customer_key.rego | 49 ++++ .../aws/ecr/repository_customer_key_test.go | 88 ------- .../aws/ecr/repository_customer_key_test.rego | 30 +++ .../aws/efs/enable_at_rest_encryption.go | 3 +- .../aws/efs/enable_at_rest_encryption.rego | 40 ++++ .../aws/efs/enable_at_rest_encryption_test.go | 63 ----- .../efs/enable_at_rest_encryption_test.rego | 18 ++ .../aws/eks/enable_control_plane_logging.go | 3 +- .../aws/eks/enable_control_plane_logging.rego | 61 +++++ .../eks/enable_control_plane_logging_test.go | 95 -------- .../enable_control_plane_logging_test.rego | 39 ++++ checks/cloud/aws/eks/encrypt_secrets.go | 3 +- checks/cloud/aws/eks/encrypt_secrets.rego | 47 ++++ checks/cloud/aws/eks/encrypt_secrets_test.go | 89 -------- .../cloud/aws/eks/encrypt_secrets_test.rego | 33 +++ .../cloud/aws/eks/no_public_cluster_access.go | 3 +- .../aws/eks/no_public_cluster_access.rego | 37 +++ .../aws/eks/no_public_cluster_access_test.go | 63 ----- .../eks/no_public_cluster_access_test.rego | 18 ++ .../eks/no_public_cluster_access_to_cidr.go | 3 +- .../eks/no_public_cluster_access_to_cidr.rego | 40 ++++ .../no_public_cluster_access_to_cidr_test.go | 83 ------- ...no_public_cluster_access_to_cidr_test.rego | 33 +++ test/rego/aws_ecr_test.go | 216 ++++++++++++++++++ test/rego/aws_efs_test.go | 35 +++ test/rego/aws_eks_test.go | 185 +++++++++++++++ test/rego/rego_checks_test.go | 11 +- 49 files changed, 1135 insertions(+), 778 deletions(-) create mode 100644 checks/cloud/aws/ecr/enable_image_scans.rego delete mode 100644 checks/cloud/aws/ecr/enable_image_scans_test.go create mode 100644 checks/cloud/aws/ecr/enable_image_scans_test.rego create mode 100644 checks/cloud/aws/ecr/enforce_immutable_repository.rego delete mode 100644 checks/cloud/aws/ecr/enforce_immutable_repository_test.go create mode 100644 checks/cloud/aws/ecr/enforce_immutable_repository_test.rego create mode 100644 checks/cloud/aws/ecr/no_public_access.rego delete mode 100644 checks/cloud/aws/ecr/no_public_access_test.go create mode 100644 checks/cloud/aws/ecr/no_public_access_test.rego create mode 100644 checks/cloud/aws/ecr/repository_customer_key.rego delete mode 100644 checks/cloud/aws/ecr/repository_customer_key_test.go create mode 100644 checks/cloud/aws/ecr/repository_customer_key_test.rego create mode 100644 checks/cloud/aws/efs/enable_at_rest_encryption.rego delete mode 100644 checks/cloud/aws/efs/enable_at_rest_encryption_test.go create mode 100644 checks/cloud/aws/efs/enable_at_rest_encryption_test.rego create mode 100644 checks/cloud/aws/eks/enable_control_plane_logging.rego delete mode 100644 checks/cloud/aws/eks/enable_control_plane_logging_test.go create mode 100644 checks/cloud/aws/eks/enable_control_plane_logging_test.rego create mode 100644 checks/cloud/aws/eks/encrypt_secrets.rego delete mode 100644 checks/cloud/aws/eks/encrypt_secrets_test.go create mode 100644 checks/cloud/aws/eks/encrypt_secrets_test.rego create mode 100644 checks/cloud/aws/eks/no_public_cluster_access.rego delete mode 100644 checks/cloud/aws/eks/no_public_cluster_access_test.go create mode 100644 checks/cloud/aws/eks/no_public_cluster_access_test.rego create mode 100644 checks/cloud/aws/eks/no_public_cluster_access_to_cidr.rego delete mode 100644 checks/cloud/aws/eks/no_public_cluster_access_to_cidr_test.go create mode 100644 checks/cloud/aws/eks/no_public_cluster_access_to_cidr_test.rego create mode 100644 test/rego/aws_ecr_test.go create mode 100644 test/rego/aws_efs_test.go create mode 100644 test/rego/aws_eks_test.go diff --git a/avd_docs/aws/ecr/AVD-AWS-0030/docs.md b/avd_docs/aws/ecr/AVD-AWS-0030/docs.md index cacc98e8..ab63da31 100644 --- a/avd_docs/aws/ecr/AVD-AWS-0030/docs.md +++ b/avd_docs/aws/ecr/AVD-AWS-0030/docs.md @@ -1,8 +1,9 @@ Repository image scans should be enabled to ensure vulnerable software can be discovered and remediated as soon as possible. + ### Impact -The ability to scan images is not being used and vulnerabilities will not be highlighted + {{ remediationActions }} diff --git a/avd_docs/aws/ecr/AVD-AWS-0031/docs.md b/avd_docs/aws/ecr/AVD-AWS-0031/docs.md index f7d30790..e6251d5d 100644 --- a/avd_docs/aws/ecr/AVD-AWS-0031/docs.md +++ b/avd_docs/aws/ecr/AVD-AWS-0031/docs.md @@ -1,10 +1,10 @@ ECR images should be set to IMMUTABLE to prevent code injection through image mutation. - This can be done by setting image_tab_mutability to IMMUTABLE + ### Impact -Image tags could be overwritten with compromised images + {{ remediationActions }} diff --git a/avd_docs/aws/ecr/AVD-AWS-0032/docs.md b/avd_docs/aws/ecr/AVD-AWS-0032/docs.md index b2078b0a..483f4b36 100644 --- a/avd_docs/aws/ecr/AVD-AWS-0032/docs.md +++ b/avd_docs/aws/ecr/AVD-AWS-0032/docs.md @@ -1,8 +1,9 @@ Allowing public access to the ECR repository risks leaking sensitive of abusable information + ### Impact -Risk of potential data leakage of sensitive artifacts + {{ remediationActions }} diff --git a/avd_docs/aws/ecr/AVD-AWS-0033/docs.md b/avd_docs/aws/ecr/AVD-AWS-0033/docs.md index a6e1af2c..25ca75f1 100644 --- a/avd_docs/aws/ecr/AVD-AWS-0033/docs.md +++ b/avd_docs/aws/ecr/AVD-AWS-0033/docs.md @@ -1,8 +1,9 @@ Images in the ECR repository are encrypted by default using AWS managed encryption keys. To increase control of the encryption and control the management of factors like key rotation, use a Customer Managed Key. + ### Impact -Using AWS managed keys does not allow for fine grained control + {{ remediationActions }} diff --git a/avd_docs/aws/efs/AVD-AWS-0037/docs.md b/avd_docs/aws/efs/AVD-AWS-0037/docs.md index c4667a47..5242c766 100644 --- a/avd_docs/aws/efs/AVD-AWS-0037/docs.md +++ b/avd_docs/aws/efs/AVD-AWS-0037/docs.md @@ -1,8 +1,9 @@ If your organization is subject to corporate or regulatory policies that require encryption of data and metadata at rest, we recommend creating a file system that is encrypted at rest, and mounting your file system using encryption of data in transit. + ### Impact -Data can be read from the EFS if compromised + {{ remediationActions }} diff --git a/avd_docs/aws/eks/AVD-AWS-0038/docs.md b/avd_docs/aws/eks/AVD-AWS-0038/docs.md index 79bc0e01..ae15d0ed 100644 --- a/avd_docs/aws/eks/AVD-AWS-0038/docs.md +++ b/avd_docs/aws/eks/AVD-AWS-0038/docs.md @@ -1,8 +1,9 @@ By default cluster control plane logging is not turned on. Logging is available for audit, api, authenticator, controllerManager and scheduler. All logging should be turned on for cluster control plane. + ### Impact -Logging provides valuable information about access and usage + {{ remediationActions }} diff --git a/avd_docs/aws/eks/AVD-AWS-0039/docs.md b/avd_docs/aws/eks/AVD-AWS-0039/docs.md index de61dab4..42437b50 100644 --- a/avd_docs/aws/eks/AVD-AWS-0039/docs.md +++ b/avd_docs/aws/eks/AVD-AWS-0039/docs.md @@ -1,8 +1,9 @@ EKS cluster resources should have the encryption_config block set with protection of the secrets resource. + ### Impact -EKS secrets could be read if compromised + {{ remediationActions }} diff --git a/avd_docs/aws/eks/AVD-AWS-0040/docs.md b/avd_docs/aws/eks/AVD-AWS-0040/docs.md index b4464df0..96743956 100644 --- a/avd_docs/aws/eks/AVD-AWS-0040/docs.md +++ b/avd_docs/aws/eks/AVD-AWS-0040/docs.md @@ -1,8 +1,9 @@ EKS clusters are available publicly by default, this should be explicitly disabled in the vpc_config of the EKS cluster resource. + ### Impact -EKS can be access from the internet + {{ remediationActions }} diff --git a/avd_docs/aws/eks/AVD-AWS-0041/docs.md b/avd_docs/aws/eks/AVD-AWS-0041/docs.md index 19a023a3..9e1ba77a 100644 --- a/avd_docs/aws/eks/AVD-AWS-0041/docs.md +++ b/avd_docs/aws/eks/AVD-AWS-0041/docs.md @@ -1,8 +1,9 @@ EKS Clusters have public access cidrs set to 0.0.0.0/0 by default which is wide open to the internet. This should be explicitly set to a more specific private CIDR range + ### Impact -EKS can be accessed from the internet + {{ remediationActions }} diff --git a/checks/cloud/aws/ecr/enable_image_scans.go b/checks/cloud/aws/ecr/enable_image_scans.go index 2cfb3e11..1eae943c 100755 --- a/checks/cloud/aws/ecr/enable_image_scans.go +++ b/checks/cloud/aws/ecr/enable_image_scans.go @@ -33,7 +33,8 @@ var CheckEnableImageScans = rules.Register( Links: cloudFormationEnableImageScansLinks, RemediationMarkdown: cloudFormationEnableImageScansRemediationMarkdown, }, - Severity: severity.High, + Severity: severity.High, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, repo := range s.AWS.ECR.Repositories { diff --git a/checks/cloud/aws/ecr/enable_image_scans.rego b/checks/cloud/aws/ecr/enable_image_scans.rego new file mode 100644 index 00000000..c36535be --- /dev/null +++ b/checks/cloud/aws/ecr/enable_image_scans.rego @@ -0,0 +1,41 @@ +# METADATA +# title: ECR repository has image scans disabled. +# description: | +# Repository image scans should be enabled to ensure vulnerable software can be discovered and remediated as soon as possible. +# scope: package +# schemas: +# - input: schema["cloud"] +# related_resources: +# - https://docs.aws.amazon.com/AmazonECR/latest/userguide/image-scanning.html +# custom: +# id: AVD-AWS-0030 +# avd_id: AVD-AWS-0030 +# provider: aws +# service: ecr +# severity: HIGH +# short_code: enable-image-scans +# recommended_action: Enable ECR image scanning +# input: +# selector: +# - type: cloud +# subtypes: +# - service: ecr +# provider: aws +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecr_repository#image_scanning_configuration +# good_examples: checks/cloud/aws/ecr/enable_image_scans.tf.go +# bad_examples: checks/cloud/aws/ecr/enable_image_scans.tf.go +# cloudformation: +# good_examples: checks/cloud/aws/ecr/enable_image_scans.cf.go +# bad_examples: checks/cloud/aws/ecr/enable_image_scans.cf.go +package builtin.aws.ecr.aws0030 + +import rego.v1 + +deny contains res if { + some repo in input.aws.ecr.repositories + repo.imagescanning.scanonpush.value == false + + res := result.new("Image scanning is not enabled", repo.imagescanning.scanonpush) +} diff --git a/checks/cloud/aws/ecr/enable_image_scans_test.go b/checks/cloud/aws/ecr/enable_image_scans_test.go deleted file mode 100644 index 0cf5df66..00000000 --- a/checks/cloud/aws/ecr/enable_image_scans_test.go +++ /dev/null @@ -1,71 +0,0 @@ -package ecr - -import ( - "testing" - - trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/ecr" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/stretchr/testify/assert" -) - -func TestCheckEnableImageScans(t *testing.T) { - tests := []struct { - name string - input ecr.ECR - expected bool - }{ - { - name: "ECR repository with image scans disabled", - input: ecr.ECR{ - Repositories: []ecr.Repository{ - { - Metadata: trivyTypes.NewTestMetadata(), - ImageScanning: ecr.ImageScanning{ - Metadata: trivyTypes.NewTestMetadata(), - ScanOnPush: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: true, - }, - { - name: "ECR repository with image scans enabled", - input: ecr.ECR{ - Repositories: []ecr.Repository{ - { - Metadata: trivyTypes.NewTestMetadata(), - ImageScanning: ecr.ImageScanning{ - Metadata: trivyTypes.NewTestMetadata(), - ScanOnPush: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.AWS.ECR = test.input - results := CheckEnableImageScans.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckEnableImageScans.LongID() { - found = true - } - } - if test.expected { - assert.True(t, found, "Rule should have been found") - } else { - assert.False(t, found, "Rule should not have been found") - } - }) - } -} diff --git a/checks/cloud/aws/ecr/enable_image_scans_test.rego b/checks/cloud/aws/ecr/enable_image_scans_test.rego new file mode 100644 index 00000000..ae0c46de --- /dev/null +++ b/checks/cloud/aws/ecr/enable_image_scans_test.rego @@ -0,0 +1,18 @@ +package builtin.aws.ecr.aws0030_test + +import rego.v1 + +import data.builtin.aws.ecr.aws0030 as check +import data.lib.test + +test_allow_image_scanning_enabled if { + inp := {"aws": {"ecr": {"repositories": [{"imagescanning": {"scanonpush": {"value": true}}}]}}} + + test.assert_empty(check.deny) with input as inp +} + +test_deny_image_scanning_disabled if { + inp := {"aws": {"ecr": {"repositories": [{"imagescanning": {"scanonpush": {"value": false}}}]}}} + + test.assert_equal_message("Image scanning is not enabled", check.deny) with input as inp +} diff --git a/checks/cloud/aws/ecr/enforce_immutable_repository.go b/checks/cloud/aws/ecr/enforce_immutable_repository.go index 6ebcfadc..dd9f2f41 100755 --- a/checks/cloud/aws/ecr/enforce_immutable_repository.go +++ b/checks/cloud/aws/ecr/enforce_immutable_repository.go @@ -35,7 +35,8 @@ This can be done by setting image_tab_mutability to IMMUTABLE Links: cloudFormationEnforceImmutableRepositoryLinks, RemediationMarkdown: cloudFormationEnforceImmutableRepositoryRemediationMarkdown, }, - Severity: severity.High, + Severity: severity.High, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, repo := range s.AWS.ECR.Repositories { diff --git a/checks/cloud/aws/ecr/enforce_immutable_repository.rego b/checks/cloud/aws/ecr/enforce_immutable_repository.rego new file mode 100644 index 00000000..1a89d859 --- /dev/null +++ b/checks/cloud/aws/ecr/enforce_immutable_repository.rego @@ -0,0 +1,42 @@ +# METADATA +# title: ECR images tags shouldn't be mutable. +# description: | +# ECR images should be set to IMMUTABLE to prevent code injection through image mutation. +# This can be done by setting image_tab_mutability to IMMUTABLE +# scope: package +# schemas: +# - input: schema["cloud"] +# related_resources: +# - https://sysdig.com/blog/toctou-tag-mutability/ +# custom: +# id: AVD-AWS-0031 +# avd_id: AVD-AWS-0031 +# provider: aws +# service: ecr +# severity: HIGH +# short_code: enforce-immutable-repository +# recommended_action: Only use immutable images in ECR +# input: +# selector: +# - type: cloud +# subtypes: +# - service: ecr +# provider: aws +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecr_repository +# good_examples: checks/cloud/aws/ecr/enforce_immutable_repository.tf.go +# bad_examples: checks/cloud/aws/ecr/enforce_immutable_repository.tf.go +# cloudformation: +# good_examples: checks/cloud/aws/ecr/enforce_immutable_repository.cf.go +# bad_examples: checks/cloud/aws/ecr/enforce_immutable_repository.cf.go +package builtin.aws.ecr.aws0031 + +import rego.v1 + +deny contains res if { + some repo in input.aws.ecr.repositories + repo.imagetagsimmutable.value == false + + res := result.new("Repository tags are mutable.", repo.imagetagsimmutable) +} diff --git a/checks/cloud/aws/ecr/enforce_immutable_repository_test.go b/checks/cloud/aws/ecr/enforce_immutable_repository_test.go deleted file mode 100644 index 8ced0b65..00000000 --- a/checks/cloud/aws/ecr/enforce_immutable_repository_test.go +++ /dev/null @@ -1,65 +0,0 @@ -package ecr - -import ( - "testing" - - trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/ecr" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/stretchr/testify/assert" -) - -func TestCheckEnforceImmutableRepository(t *testing.T) { - tests := []struct { - name string - input ecr.ECR - expected bool - }{ - { - name: "ECR mutable image tags", - input: ecr.ECR{ - Repositories: []ecr.Repository{ - { - Metadata: trivyTypes.NewTestMetadata(), - ImageTagsImmutable: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - }, - }, - }, - expected: true, - }, - { - name: "ECR immutable image tags", - input: ecr.ECR{ - Repositories: []ecr.Repository{ - { - Metadata: trivyTypes.NewTestMetadata(), - ImageTagsImmutable: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - }, - }, - }, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.AWS.ECR = test.input - results := CheckEnforceImmutableRepository.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckEnforceImmutableRepository.LongID() { - found = true - } - } - if test.expected { - assert.True(t, found, "Rule should have been found") - } else { - assert.False(t, found, "Rule should not have been found") - } - }) - } -} diff --git a/checks/cloud/aws/ecr/enforce_immutable_repository_test.rego b/checks/cloud/aws/ecr/enforce_immutable_repository_test.rego new file mode 100644 index 00000000..7068fa3d --- /dev/null +++ b/checks/cloud/aws/ecr/enforce_immutable_repository_test.rego @@ -0,0 +1,18 @@ +package builtin.aws.ecr.aws0031_test + +import rego.v1 + +import data.builtin.aws.ecr.aws0031 as check +import data.lib.test + +test_allow_immutable_repository if { + inp := {"aws": {"ecr": {"repositories": [{"imagetagsimmutable": {"value": true}}]}}} + + test.assert_empty(check.deny) with input as inp +} + +test_deny_immutable_repository if { + inp := {"aws": {"ecr": {"repositories": [{"imagetagsimmutable": {"value": false}}]}}} + + test.assert_equal_message("Repository tags are mutable.", check.deny) with input as inp +} diff --git a/checks/cloud/aws/ecr/no_public_access.go b/checks/cloud/aws/ecr/no_public_access.go index dbefbaa0..06139428 100755 --- a/checks/cloud/aws/ecr/no_public_access.go +++ b/checks/cloud/aws/ecr/no_public_access.go @@ -39,7 +39,8 @@ var CheckNoPublicAccess = rules.Register( Links: cloudFormationNoPublicAccessLinks, RemediationMarkdown: cloudFormationNoPublicAccessRemediationMarkdown, }, - Severity: severity.High, + Severity: severity.High, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, repo := range s.AWS.ECR.Repositories { diff --git a/checks/cloud/aws/ecr/no_public_access.rego b/checks/cloud/aws/ecr/no_public_access.rego new file mode 100644 index 00000000..f378703e --- /dev/null +++ b/checks/cloud/aws/ecr/no_public_access.rego @@ -0,0 +1,57 @@ +# METADATA +# title: ECR repository policy must block public access +# description: | +# Allowing public access to the ECR repository risks leaking sensitive of abusable information +# scope: package +# schemas: +# - input: schema["cloud"] +# related_resources: +# - https://docs.aws.amazon.com/AmazonECR/latest/public/public-repository-policies.html +# custom: +# id: AVD-AWS-0032 +# avd_id: AVD-AWS-0032 +# provider: aws +# service: ecr +# severity: HIGH +# short_code: no-public-access +# recommended_action: Do not allow public access in the policy +# input: +# selector: +# - type: cloud +# subtypes: +# - service: ecr +# provider: aws +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecr_repository_policy#policy +# good_examples: checks/cloud/aws/ecr/no_public_access.tf.go +# bad_examples: checks/cloud/aws/ecr/no_public_access.tf.go +# cloudformation: +# good_examples: checks/cloud/aws/ecr/no_public_access.cf.go +# bad_examples: checks/cloud/aws/ecr/no_public_access.cf.go +package builtin.aws.ecr.aws0032 + +import rego.v1 + +deny contains res if { + some repo in input.aws.ecr.repositories + some policy in repo.policies + value := json.unmarshal(policy.document.value) + some statement in value.Statement + has_ecr_action(statement) + has_public_access(statement) + res := result.new("Policy provides public access to the ECR repository.", policy.document) +} + +has_ecr_action(statement) if { + some action in statement.Action + startswith(action, "ecr:") +} + +has_public_access(statement) if { + statement.Principal == "*" +} + +has_public_access(statement) if { + "*" in statement.Principal.AWS +} diff --git a/checks/cloud/aws/ecr/no_public_access_test.go b/checks/cloud/aws/ecr/no_public_access_test.go deleted file mode 100644 index f114083e..00000000 --- a/checks/cloud/aws/ecr/no_public_access_test.go +++ /dev/null @@ -1,138 +0,0 @@ -package ecr - -import ( - "testing" - - "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/ecr" - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/iam" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/liamg/iamgo" - - "github.com/stretchr/testify/assert" -) - -func TestCheckNoPublicAccess(t *testing.T) { - tests := []struct { - name string - input ecr.ECR - expected bool - }{ - { - name: "ECR repository policy with wildcard principal", - input: ecr.ECR{ - Repositories: []ecr.Repository{ - { - Metadata: types.NewTestMetadata(), - Policies: func() []iam.Policy { - - sb := iamgo.NewStatementBuilder() - sb.WithSid("new policy") - sb.WithEffect("Allow") - sb.WithAllPrincipals(true) - sb.WithActions([]string{ - "ecr:GetDownloadUrlForLayer", - "ecr:BatchGetImage", - "ecr:BatchCheckLayerAvailability", - "ecr:PutImage", - "ecr:InitiateLayerUpload", - "ecr:UploadLayerPart", - "ecr:CompleteLayerUpload", - "ecr:DescribeRepositories", - "ecr:GetRepositoryPolicy", - "ecr:ListImages", - "ecr:DeleteRepository", - "ecr:BatchDeleteImage", - "ecr:SetRepositoryPolicy", - "ecr:DeleteRepositoryPolicy", - }) - - builder := iamgo.NewPolicyBuilder() - builder.WithVersion("2021-10-07") - builder.WithStatement(sb.Build()) - - return []iam.Policy{ - { - Document: iam.Document{ - Metadata: types.NewTestMetadata(), - Parsed: builder.Build(), - }, - }, - } - }(), - }, - }, - }, - expected: true, - }, - { - name: "ECR repository policy with specific principal", - input: ecr.ECR{ - Repositories: []ecr.Repository{ - { - Metadata: types.NewTestMetadata(), - Policies: func() []iam.Policy { - - sb := iamgo.NewStatementBuilder() - sb.WithSid("new policy") - sb.WithEffect("Allow") - sb.WithAWSPrincipals([]string{"arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"}) - sb.WithActions([]string{ - "ecr:GetDownloadUrlForLayer", - "ecr:BatchGetImage", - "ecr:BatchCheckLayerAvailability", - "ecr:PutImage", - "ecr:InitiateLayerUpload", - "ecr:UploadLayerPart", - "ecr:CompleteLayerUpload", - "ecr:DescribeRepositories", - "ecr:GetRepositoryPolicy", - "ecr:ListImages", - "ecr:DeleteRepository", - "ecr:BatchDeleteImage", - "ecr:SetRepositoryPolicy", - "ecr:DeleteRepositoryPolicy", - }) - - builder := iamgo.NewPolicyBuilder() - builder.WithVersion("2021-10-07") - builder.WithStatement(sb.Build()) - - return []iam.Policy{ - { - Document: iam.Document{ - Metadata: types.NewTestMetadata(), - Parsed: builder.Build(), - }, - }, - } - }(), - }, - }, - }, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.AWS.ECR = test.input - results := CheckNoPublicAccess.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckNoPublicAccess.LongID() { - found = true - } - } - if test.expected { - assert.True(t, found, "Rule should have been found") - } else { - assert.False(t, found, "Rule should not have been found") - } - }) - } -} diff --git a/checks/cloud/aws/ecr/no_public_access_test.rego b/checks/cloud/aws/ecr/no_public_access_test.rego new file mode 100644 index 00000000..f8434395 --- /dev/null +++ b/checks/cloud/aws/ecr/no_public_access_test.rego @@ -0,0 +1,35 @@ +package builtin.aws.ecr.aws0032_test + +import rego.v1 + +import data.builtin.aws.ecr.aws0032 as check +import data.lib.test + +test_allow_without_public_access if { + inp := {"aws": {"ecr": {"repositories": [{"policies": [{"document": {"value": json.marshal({"Statement": [{ + "Action": ["ecr:*"], + "Principal": {"AWS": "arn:aws:iam::123456789012:root"}, + "Effect": "Allow", + }]})}}]}]}}} + + test.assert_empty(check.deny) with input as inp +} + +test_deny_with_public_access_all if { + inp := {"aws": {"ecr": {"repositories": [{"policies": [{"document": {"value": json.marshal({"Statement": [{ + "Action": ["ecr:*"], + "Principal": "*", + }]})}}]}]}}} + + test.assert_equal_message("Policy provides public access to the ECR repository", check.deny) with input as inp +} + +test_deny_with_public_acces_any if { + inp := {"aws": {"ecr": {"repositories": [{"policies": [{"document": {"value": json.marshal({"Statement": [{ + "Action": ["ecr:*"], + "Principal": {"AWS": ["*"]}, + "Effect": "Allow", + }]})}}]}]}}} + + test.assert_equal_message("Policy provides public access to the ECR repository", check.deny) with input as inp +} diff --git a/checks/cloud/aws/ecr/repository_customer_key.go b/checks/cloud/aws/ecr/repository_customer_key.go index 59d33c95..56bf29de 100755 --- a/checks/cloud/aws/ecr/repository_customer_key.go +++ b/checks/cloud/aws/ecr/repository_customer_key.go @@ -34,7 +34,8 @@ var CheckRepositoryCustomerKey = rules.Register( Links: cloudFormationRepositoryCustomerKeyLinks, RemediationMarkdown: cloudFormationRepositoryCustomerKeyRemediationMarkdown, }, - Severity: severity.Low, + Severity: severity.Low, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, repo := range s.AWS.ECR.Repositories { diff --git a/checks/cloud/aws/ecr/repository_customer_key.rego b/checks/cloud/aws/ecr/repository_customer_key.rego new file mode 100644 index 00000000..ece32597 --- /dev/null +++ b/checks/cloud/aws/ecr/repository_customer_key.rego @@ -0,0 +1,49 @@ +# METADATA +# title: ECR Repository should use customer managed keys to allow more control +# description: | +# Images in the ECR repository are encrypted by default using AWS managed encryption keys. To increase control of the encryption and control the management of factors like key rotation, use a Customer Managed Key. +# scope: package +# schemas: +# - input: schema["cloud"] +# related_resources: +# - https://docs.aws.amazon.com/AmazonECR/latest/userguide/encryption-at-rest.html +# custom: +# id: AVD-AWS-0033 +# avd_id: AVD-AWS-0033 +# provider: aws +# service: ecr +# severity: LOW +# short_code: repository-customer-key +# recommended_action: Use customer managed keys +# input: +# selector: +# - type: cloud +# subtypes: +# - service: ecr +# provider: aws +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecr_repository#encryption_configuration +# good_examples: checks/cloud/aws/ecr/repository_customer_key.tf.go +# bad_examples: checks/cloud/aws/ecr/repository_customer_key.tf.go +# cloudformation: +# good_examples: checks/cloud/aws/ecr/repository_customer_key.cf.go +# bad_examples: checks/cloud/aws/ecr/repository_customer_key.cf.go +package builtin.aws.ecr.aws0033 + +import rego.v1 + +deny contains res if { + some repo in input.aws.ecr.repositories + not is_encyption_type_kms(repo.encryption.type) + res := result.new("Repository is not encrypted using KMS.", repo.encryption.type) +} + +deny contains res if { + some repo in input.aws.ecr.repositories + is_encyption_type_kms(repo.encryption.type) + repo.encryption.kmskeyid.value == "" + res := result.new("Repository encryption does not use a customer managed KMS key.", repo.encryption.kmskeyid) +} + +is_encyption_type_kms(typ) if typ.value == "KMS" diff --git a/checks/cloud/aws/ecr/repository_customer_key_test.go b/checks/cloud/aws/ecr/repository_customer_key_test.go deleted file mode 100644 index 62168c80..00000000 --- a/checks/cloud/aws/ecr/repository_customer_key_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package ecr - -import ( - "testing" - - trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/ecr" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/stretchr/testify/assert" -) - -func TestCheckRepositoryCustomerKey(t *testing.T) { - tests := []struct { - name string - input ecr.ECR - expected bool - }{ - { - name: "ECR repository not using KMS encryption", - input: ecr.ECR{ - Repositories: []ecr.Repository{ - { - Metadata: trivyTypes.NewTestMetadata(), - Encryption: ecr.Encryption{ - Metadata: trivyTypes.NewTestMetadata(), - Type: trivyTypes.String(ecr.EncryptionTypeAES256, trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: true, - }, - { - name: "ECR repository using KMS encryption but missing key", - input: ecr.ECR{ - Repositories: []ecr.Repository{ - { - Metadata: trivyTypes.NewTestMetadata(), - Encryption: ecr.Encryption{ - Metadata: trivyTypes.NewTestMetadata(), - Type: trivyTypes.String(ecr.EncryptionTypeKMS, trivyTypes.NewTestMetadata()), - KMSKeyID: trivyTypes.String("", trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: true, - }, - { - name: "ECR repository encrypted with KMS key", - input: ecr.ECR{ - Repositories: []ecr.Repository{ - { - Metadata: trivyTypes.NewTestMetadata(), - Encryption: ecr.Encryption{ - Metadata: trivyTypes.NewTestMetadata(), - Type: trivyTypes.String(ecr.EncryptionTypeKMS, trivyTypes.NewTestMetadata()), - KMSKeyID: trivyTypes.String("some-kms-key", trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.AWS.ECR = test.input - results := CheckRepositoryCustomerKey.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckRepositoryCustomerKey.LongID() { - found = true - } - } - if test.expected { - assert.True(t, found, "Rule should have been found") - } else { - assert.False(t, found, "Rule should not have been found") - } - }) - } -} diff --git a/checks/cloud/aws/ecr/repository_customer_key_test.rego b/checks/cloud/aws/ecr/repository_customer_key_test.rego new file mode 100644 index 00000000..8c91a517 --- /dev/null +++ b/checks/cloud/aws/ecr/repository_customer_key_test.rego @@ -0,0 +1,30 @@ +package builtin.aws.ecr.aws0033_test + +import rego.v1 + +import data.builtin.aws.ecr.aws0033 as check +import data.lib.test + +test_allow_repo_with_kms if { + inp := {"aws": {"ecr": {"repositories": [{"encryption": { + "type": {"value": "KMS"}, + "kmskeyid": {"value": "key"}, + }}]}}} + + test.assert_empty(check.deny) with input as inp +} + +test_deny_repo_without_kms_encryption if { + inp := {"aws": {"ecr": {"repositories": [{"encryption": {"type": {"value": "AES256"}}}]}}} + + test.assert_equal_message("Repository is not encrypted using KMS.", check.deny) with input as inp +} + +test_deny_repo_with_kms_encryption_without_key if { + inp := {"aws": {"ecr": {"repositories": [{"encryption": { + "type": {"value": "KMS"}, + "kmskeyid": {"value": ""}, + }}]}}} + + test.assert_equal_message("Repository encryption does not use a customer managed KMS key.", check.deny) with input as inp +} diff --git a/checks/cloud/aws/efs/enable_at_rest_encryption.go b/checks/cloud/aws/efs/enable_at_rest_encryption.go index d654348a..2a07bdd7 100755 --- a/checks/cloud/aws/efs/enable_at_rest_encryption.go +++ b/checks/cloud/aws/efs/enable_at_rest_encryption.go @@ -33,7 +33,8 @@ var CheckEnableAtRestEncryption = rules.Register( Links: cloudFormationEnableAtRestEncryptionLinks, RemediationMarkdown: cloudFormationEnableAtRestEncryptionRemediationMarkdown, }, - Severity: severity.High, + Severity: severity.High, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, fs := range s.AWS.EFS.FileSystems { diff --git a/checks/cloud/aws/efs/enable_at_rest_encryption.rego b/checks/cloud/aws/efs/enable_at_rest_encryption.rego new file mode 100644 index 00000000..fd5b0365 --- /dev/null +++ b/checks/cloud/aws/efs/enable_at_rest_encryption.rego @@ -0,0 +1,40 @@ +# METADATA +# title: EFS Encryption has not been enabled +# description: | +# If your organization is subject to corporate or regulatory policies that require encryption of data and metadata at rest, we recommend creating a file system that is encrypted at rest, and mounting your file system using encryption of data in transit. +# scope: package +# schemas: +# - input: schema["cloud"] +# related_resources: +# - https://docs.aws.amazon.com/efs/latest/ug/encryption.html +# custom: +# id: AVD-AWS-0037 +# avd_id: AVD-AWS-0037 +# provider: aws +# service: efs +# severity: HIGH +# short_code: enable-at-rest-encryption +# recommended_action: Enable encryption for EFS +# input: +# selector: +# - type: cloud +# subtypes: +# - service: efs +# provider: aws +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/efs_file_system +# good_examples: checks/cloud/aws/efs/enable_at_rest_encryption.tf.go +# bad_examples: checks/cloud/aws/efs/enable_at_rest_encryption.tf.go +# cloudformation: +# good_examples: checks/cloud/aws/efs/enable_at_rest_encryption.cf.go +# bad_examples: checks/cloud/aws/efs/enable_at_rest_encryption.cf.go +package builtin.aws.efs.aws0037 + +import rego.v1 + +deny contains res if { + some fs in input.aws.efs.filesystems + fs.encrypted.value == false + res := result.new("File system is not encrypted.", fs.encrypted) +} diff --git a/checks/cloud/aws/efs/enable_at_rest_encryption_test.go b/checks/cloud/aws/efs/enable_at_rest_encryption_test.go deleted file mode 100644 index e2f74dae..00000000 --- a/checks/cloud/aws/efs/enable_at_rest_encryption_test.go +++ /dev/null @@ -1,63 +0,0 @@ -package efs - -import ( - "testing" - - trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/efs" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/stretchr/testify/assert" -) - -func TestCheckEnableAtRestEncryption(t *testing.T) { - tests := []struct { - name string - input efs.EFS - expected bool - }{ - { - name: "positive result", - input: efs.EFS{ - FileSystems: []efs.FileSystem{ - { - Metadata: trivyTypes.NewTestMetadata(), - Encrypted: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - }}, - }, - expected: true, - }, - { - name: "negative result", - input: efs.EFS{ - FileSystems: []efs.FileSystem{ - { - Metadata: trivyTypes.NewTestMetadata(), - Encrypted: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - }}, - }, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.AWS.EFS = test.input - results := CheckEnableAtRestEncryption.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckEnableAtRestEncryption.LongID() { - found = true - } - } - if test.expected { - assert.True(t, found, "Rule should have been found") - } else { - assert.False(t, found, "Rule should not have been found") - } - }) - } -} diff --git a/checks/cloud/aws/efs/enable_at_rest_encryption_test.rego b/checks/cloud/aws/efs/enable_at_rest_encryption_test.rego new file mode 100644 index 00000000..95e6627a --- /dev/null +++ b/checks/cloud/aws/efs/enable_at_rest_encryption_test.rego @@ -0,0 +1,18 @@ +package builtin.aws.efs.aws0037_test + +import rego.v1 + +import data.builtin.aws.efs.aws0037 as check +import data.lib.test + +test_allow_fs_encrypted if { + inp := {"aws": {"efs": {"filesystems": [{"encrypted": {"value": true}}]}}} + + test.assert_empty(check.deny) with input as inp +} + +test_deny_fs_unencrypted if { + inp := {"aws": {"efs": {"filesystems": [{"encrypted": {"value": false}}]}}} + + test.assert_equal_message("File system is not encrypted.", check.deny) with input as inp +} diff --git a/checks/cloud/aws/eks/enable_control_plane_logging.go b/checks/cloud/aws/eks/enable_control_plane_logging.go index 407f92bf..9589a8c1 100755 --- a/checks/cloud/aws/eks/enable_control_plane_logging.go +++ b/checks/cloud/aws/eks/enable_control_plane_logging.go @@ -27,7 +27,8 @@ var CheckEnableControlPlaneLogging = rules.Register( Links: terraformEnableControlPlaneLoggingLinks, RemediationMarkdown: terraformEnableControlPlaneLoggingRemediationMarkdown, }, - Severity: severity.Medium, + Severity: severity.Medium, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, cluster := range s.AWS.EKS.Clusters { diff --git a/checks/cloud/aws/eks/enable_control_plane_logging.rego b/checks/cloud/aws/eks/enable_control_plane_logging.rego new file mode 100644 index 00000000..926bac94 --- /dev/null +++ b/checks/cloud/aws/eks/enable_control_plane_logging.rego @@ -0,0 +1,61 @@ +# METADATA +# title: EKS Clusters should have cluster control plane logging turned on +# description: | +# By default cluster control plane logging is not turned on. Logging is available for audit, api, authenticator, controllerManager and scheduler. All logging should be turned on for cluster control plane. +# scope: package +# schemas: +# - input: schema["cloud"] +# related_resources: +# - https://docs.aws.amazon.com/eks/latest/userguide/control-plane-logs.html +# custom: +# id: AVD-AWS-0038 +# avd_id: AVD-AWS-0038 +# provider: aws +# service: eks +# severity: MEDIUM +# short_code: enable-control-plane-logging +# recommended_action: Enable logging for the EKS control plane +# input: +# selector: +# - type: cloud +# subtypes: +# - service: eks +# provider: aws +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_cluster#enabled_cluster_log_types +# good_examples: checks/cloud/aws/eks/enable_control_plane_logging.tf.go +# bad_examples: checks/cloud/aws/eks/enable_control_plane_logging.tf.go +package builtin.aws.eks.aws0038 + +import rego.v1 + +deny contains res if { + some cluster in input.aws.eks.clusters + cluster.logging.api.value == false + res := result.new("Control plane API logging is not enabled.", cluster.logging.api) +} + +deny contains res if { + some cluster in input.aws.eks.clusters + cluster.logging.audit.value == false + res := result.new("Control plane audit logging is not enabled.", cluster.logging.audit) +} + +deny contains res if { + some cluster in input.aws.eks.clusters + cluster.logging.authenticator.value == false + res := result.new("Control plane authenticator logging is not enabled.", cluster.logging.authenticator) +} + +deny contains res if { + some cluster in input.aws.eks.clusters + cluster.logging.controllermanager.value == false + res := result.new("Control plane controller manager logging is not enabled.", cluster.logging.controllermanager) +} + +deny contains res if { + some cluster in input.aws.eks.clusters + cluster.logging.scheduler.value == false + res := result.new("Control plane scheduler logging is not enabled.", cluster.logging.scheduler) +} diff --git a/checks/cloud/aws/eks/enable_control_plane_logging_test.go b/checks/cloud/aws/eks/enable_control_plane_logging_test.go deleted file mode 100644 index 324735fc..00000000 --- a/checks/cloud/aws/eks/enable_control_plane_logging_test.go +++ /dev/null @@ -1,95 +0,0 @@ -package eks - -import ( - "testing" - - trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/eks" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/stretchr/testify/assert" -) - -func TestCheckEnableControlPlaneLogging(t *testing.T) { - tests := []struct { - name string - input eks.EKS - expected bool - }{ - { - name: "EKS cluster with all cluster logging disabled", - input: eks.EKS{ - Clusters: []eks.Cluster{ - { - Metadata: trivyTypes.NewTestMetadata(), - Logging: eks.Logging{ - API: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - Audit: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - Authenticator: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - ControllerManager: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - Scheduler: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: true, - }, - { - name: "EKS cluster with only some cluster logging enabled", - input: eks.EKS{ - Clusters: []eks.Cluster{ - { - Metadata: trivyTypes.NewTestMetadata(), - Logging: eks.Logging{ - API: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - Audit: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - Authenticator: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - ControllerManager: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - Scheduler: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: true, - }, - { - name: "EKS cluster with all cluster logging enabled", - input: eks.EKS{ - Clusters: []eks.Cluster{ - { - Metadata: trivyTypes.NewTestMetadata(), - Logging: eks.Logging{ - API: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - Audit: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - Authenticator: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - ControllerManager: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - Scheduler: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.AWS.EKS = test.input - results := CheckEnableControlPlaneLogging.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckEnableControlPlaneLogging.LongID() { - found = true - } - } - if test.expected { - assert.True(t, found, "Rule should have been found") - } else { - assert.False(t, found, "Rule should not have been found") - } - }) - } -} diff --git a/checks/cloud/aws/eks/enable_control_plane_logging_test.rego b/checks/cloud/aws/eks/enable_control_plane_logging_test.rego new file mode 100644 index 00000000..fb3a7996 --- /dev/null +++ b/checks/cloud/aws/eks/enable_control_plane_logging_test.rego @@ -0,0 +1,39 @@ +package builtin.aws.eks.aws0038_test + +import rego.v1 + +import data.builtin.aws.eks.aws0038 as check +import data.lib.test + +test_allow_all_logging_enabled if { + inp := {"aws": {"eks": {"clusters": [{"logging": { + "audit": {"value": true}, + "authenticator": {"value": true}, + "controllermanager": {"value": true}, + "scheduler": {"value": true}, + }}]}}} + + test.assert_empty(check.deny) with input as inp +} + +test_deny_all_logging_disabled if { + inp := {"aws": {"eks": {"clusters": [{"logging": { + "audit": {"value": false}, + "authenticator": {"value": false}, + "controllermanager": {"value": false}, + "scheduler": {"value": false}, + }}]}}} + + test.assert_count(check.deny, 4) with input as inp +} + +test_deny_one_logging_disabled if { + inp := {"aws": {"eks": {"clusters": [{"logging": { + "audit": {"value": false}, + "authenticator": {"value": true}, + "controllermanager": {"value": true}, + "scheduler": {"value": true}, + }}]}}} + + test.assert_equal_message("Control plane audit logging is not enabled.", check.deny) with input as inp +} diff --git a/checks/cloud/aws/eks/encrypt_secrets.go b/checks/cloud/aws/eks/encrypt_secrets.go index 809090bd..d70739ed 100755 --- a/checks/cloud/aws/eks/encrypt_secrets.go +++ b/checks/cloud/aws/eks/encrypt_secrets.go @@ -33,7 +33,8 @@ var CheckEncryptSecrets = rules.Register( Links: cloudFormationEncryptSecretsLinks, RemediationMarkdown: cloudFormationEncryptSecretsRemediationMarkdown, }, - Severity: severity.High, + Severity: severity.High, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, cluster := range s.AWS.EKS.Clusters { diff --git a/checks/cloud/aws/eks/encrypt_secrets.rego b/checks/cloud/aws/eks/encrypt_secrets.rego new file mode 100644 index 00000000..11a2a56f --- /dev/null +++ b/checks/cloud/aws/eks/encrypt_secrets.rego @@ -0,0 +1,47 @@ +# METADATA +# title: EKS should have the encryption of secrets enabled +# description: | +# EKS cluster resources should have the encryption_config block set with protection of the secrets resource. +# scope: package +# schemas: +# - input: schema["cloud"] +# related_resources: +# - https://aws.amazon.com/about-aws/whats-new/2020/03/amazon-eks-adds-envelope-encryption-for-secrets-with-aws-kms/ +# custom: +# id: AVD-AWS-0039 +# avd_id: AVD-AWS-0039 +# provider: aws +# service: eks +# severity: HIGH +# short_code: encrypt-secrets +# recommended_action: Enable encryption of EKS secrets +# input: +# selector: +# - type: cloud +# subtypes: +# - service: eks +# provider: aws +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_cluster#encryption_config +# good_examples: checks/cloud/aws/eks/encrypt_secrets.tf.go +# bad_examples: checks/cloud/aws/eks/encrypt_secrets.tf.go +# cloudformation: +# good_examples: checks/cloud/aws/eks/encrypt_secrets.cf.go +# bad_examples: checks/cloud/aws/eks/encrypt_secrets.cf.go +package builtin.aws.eks.aws0039 + +import rego.v1 + +deny contains res if { + some cluster in input.aws.eks.clusters + cluster.encryption.secrets.value == false + res := result.new("Cluster does not have secret encryption enabled.", cluster.encryption.secrets) +} + +deny contains res if { + some cluster in input.aws.eks.clusters + cluster.encryption.secrets.value == true + cluster.encryption.kmskeyid.value == "" + res := result.new("Cluster encryption requires a KMS key ID, which is missing", cluster.encryption.kmskeyid) +} diff --git a/checks/cloud/aws/eks/encrypt_secrets_test.go b/checks/cloud/aws/eks/encrypt_secrets_test.go deleted file mode 100644 index 38534f85..00000000 --- a/checks/cloud/aws/eks/encrypt_secrets_test.go +++ /dev/null @@ -1,89 +0,0 @@ -package eks - -import ( - "testing" - - trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/eks" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/stretchr/testify/assert" -) - -func TestCheckEncryptSecrets(t *testing.T) { - tests := []struct { - name string - input eks.EKS - expected bool - }{ - { - name: "EKS Cluster with no secrets in the resources attribute", - input: eks.EKS{ - Clusters: []eks.Cluster{ - { - Metadata: trivyTypes.NewTestMetadata(), - Encryption: eks.Encryption{ - Metadata: trivyTypes.NewTestMetadata(), - Secrets: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - KMSKeyID: trivyTypes.String("", trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: true, - }, - { - name: "EKS Cluster with secrets in the resources attribute but no KMS key", - input: eks.EKS{ - Clusters: []eks.Cluster{ - { - Metadata: trivyTypes.NewTestMetadata(), - Encryption: eks.Encryption{ - Metadata: trivyTypes.NewTestMetadata(), - Secrets: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - KMSKeyID: trivyTypes.String("", trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: true, - }, - { - name: "EKS Cluster with secrets in the resources attribute and a KMS key", - input: eks.EKS{ - Clusters: []eks.Cluster{ - { - Metadata: trivyTypes.NewTestMetadata(), - Encryption: eks.Encryption{ - Metadata: trivyTypes.NewTestMetadata(), - Secrets: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - KMSKeyID: trivyTypes.String("some-arn", trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.AWS.EKS = test.input - results := CheckEncryptSecrets.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckEncryptSecrets.LongID() { - found = true - } - } - if test.expected { - assert.True(t, found, "Rule should have been found") - } else { - assert.False(t, found, "Rule should not have been found") - } - }) - } -} diff --git a/checks/cloud/aws/eks/encrypt_secrets_test.rego b/checks/cloud/aws/eks/encrypt_secrets_test.rego new file mode 100644 index 00000000..53b8bc74 --- /dev/null +++ b/checks/cloud/aws/eks/encrypt_secrets_test.rego @@ -0,0 +1,33 @@ +package builtin.aws.eks.aws0039_test + +import rego.v1 + +import data.builtin.aws.eks.aws0039 as check +import data.lib.test + +test_deny_without_secrets_and_kms if { + inp := {"aws": {"eks": {"clusters": [{"encryption": { + "kmskeyid": {"value": ""}, + "secrets": {"value": false}, + }}]}}} + + test.assert_equal_message("Cluster does not have secret encryption enabled.", check.deny) with input as inp +} + +test_deny_with_secrets_but_no_kms if { + inp := {"aws": {"eks": {"clusters": [{"encryption": { + "kmskeyid": {"value": ""}, + "secrets": {"value": true}, + }}]}}} + + test.assert_equal_message("Cluster encryption requires a KMS key ID, which is missing", check.deny) with input as inp +} + +test_allow_with_secrets_and_kms if { + inp := {"aws": {"eks": {"clusters": [{"encryption": { + "kmskeyid": {"value": "test"}, + "secrets": {"value": true}, + }}]}}} + + test.assert_empty(check.deny) with input as inp +} diff --git a/checks/cloud/aws/eks/no_public_cluster_access.go b/checks/cloud/aws/eks/no_public_cluster_access.go index 6c406503..8c79c344 100755 --- a/checks/cloud/aws/eks/no_public_cluster_access.go +++ b/checks/cloud/aws/eks/no_public_cluster_access.go @@ -27,7 +27,8 @@ var CheckNoPublicClusterAccess = rules.Register( Links: terraformNoPublicClusterAccessLinks, RemediationMarkdown: terraformNoPublicClusterAccessRemediationMarkdown, }, - Severity: severity.Critical, + Severity: severity.Critical, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, cluster := range s.AWS.EKS.Clusters { diff --git a/checks/cloud/aws/eks/no_public_cluster_access.rego b/checks/cloud/aws/eks/no_public_cluster_access.rego new file mode 100644 index 00000000..3d8c284c --- /dev/null +++ b/checks/cloud/aws/eks/no_public_cluster_access.rego @@ -0,0 +1,37 @@ +# METADATA +# title: EKS Clusters should have the public access disabled +# description: | +# EKS clusters are available publicly by default, this should be explicitly disabled in the vpc_config of the EKS cluster resource. +# scope: package +# schemas: +# - input: schema["cloud"] +# related_resources: +# - https://docs.aws.amazon.com/eks/latest/userguide/cluster-endpoint.html +# custom: +# id: AVD-AWS-0040 +# avd_id: AVD-AWS-0040 +# provider: aws +# service: eks +# severity: CRITICAL +# short_code: no-public-cluster-access +# recommended_action: Don't enable public access to EKS Clusters +# input: +# selector: +# - type: cloud +# subtypes: +# - service: eks +# provider: aws +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_cluster#endpoint_public_access +# good_examples: checks/cloud/aws/eks/no_public_cluster_access.tf.go +# bad_examples: checks/cloud/aws/eks/no_public_cluster_access.tf.go +package builtin.aws.eks.aws0040 + +import rego.v1 + +deny contains res if { + some cluster in input.aws.eks.clusters + cluster.publicaccessenabled.value == true + res := result.new("Public cluster access is enabled.", cluster.publicaccessenabled) +} diff --git a/checks/cloud/aws/eks/no_public_cluster_access_test.go b/checks/cloud/aws/eks/no_public_cluster_access_test.go deleted file mode 100644 index 5d5c8f9d..00000000 --- a/checks/cloud/aws/eks/no_public_cluster_access_test.go +++ /dev/null @@ -1,63 +0,0 @@ -package eks - -import ( - "testing" - - trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/eks" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/stretchr/testify/assert" -) - -func TestCheckNoPublicClusterAccess(t *testing.T) { - tests := []struct { - name string - input eks.EKS - expected bool - }{ - { - name: "EKS Cluster with public access enabled", - input: eks.EKS{ - Clusters: []eks.Cluster{ - { - PublicAccessEnabled: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - }, - }, - }, - expected: true, - }, - { - name: "EKS Cluster with public access disabled", - input: eks.EKS{ - Clusters: []eks.Cluster{ - { - PublicAccessEnabled: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - }, - }, - }, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.AWS.EKS = test.input - results := CheckNoPublicClusterAccess.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckNoPublicClusterAccess.LongID() { - found = true - } - } - if test.expected { - assert.True(t, found, "Rule should have been found") - } else { - assert.False(t, found, "Rule should not have been found") - } - }) - } -} diff --git a/checks/cloud/aws/eks/no_public_cluster_access_test.rego b/checks/cloud/aws/eks/no_public_cluster_access_test.rego new file mode 100644 index 00000000..5b910e62 --- /dev/null +++ b/checks/cloud/aws/eks/no_public_cluster_access_test.rego @@ -0,0 +1,18 @@ +package builtin.aws.eks.aws0040_test + +import rego.v1 + +import data.builtin.aws.eks.aws0040 as check +import data.lib.test + +test_deny_public_access_enabled if { + inp := {"aws": {"eks": {"clusters": [{"publicaccessenabled": {"value": true}}]}}} + + test.assert_equal_message("public access should be enabled", check.deny) with input as inp +} + +test_allow_public_access_disabled if { + inp := {"aws": {"eks": {"clusters": [{"publicaccessenabled": {"value": false}}]}}} + + test.assert_empty(check.deny) with input as inp +} diff --git a/checks/cloud/aws/eks/no_public_cluster_access_to_cidr.go b/checks/cloud/aws/eks/no_public_cluster_access_to_cidr.go index 8f478094..8e1f6a20 100755 --- a/checks/cloud/aws/eks/no_public_cluster_access_to_cidr.go +++ b/checks/cloud/aws/eks/no_public_cluster_access_to_cidr.go @@ -35,7 +35,8 @@ var CheckNoPublicClusterAccessToCidr = rules.Register( Links: terraformNoPublicClusterAccessToCidrLinks, RemediationMarkdown: terraformNoPublicClusterAccessToCidrRemediationMarkdown, }, - Severity: severity.Critical, + Severity: severity.Critical, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, cluster := range s.AWS.EKS.Clusters { diff --git a/checks/cloud/aws/eks/no_public_cluster_access_to_cidr.rego b/checks/cloud/aws/eks/no_public_cluster_access_to_cidr.rego new file mode 100644 index 00000000..1669736a --- /dev/null +++ b/checks/cloud/aws/eks/no_public_cluster_access_to_cidr.rego @@ -0,0 +1,40 @@ +# METADATA +# title: EKS cluster should not have open CIDR range for public access +# description: | +# EKS Clusters have public access cidrs set to 0.0.0.0/0 by default which is wide open to the internet. This should be explicitly set to a more specific private CIDR range +# scope: package +# schemas: +# - input: schema["cloud"] +# related_resources: +# - https://docs.aws.amazon.com/eks/latest/userguide/create-public-private-vpc.html +# custom: +# id: AVD-AWS-0041 +# avd_id: AVD-AWS-0041 +# provider: aws +# service: eks +# severity: CRITICAL +# short_code: no-public-cluster-access-to-cidr +# recommended_action: Don't enable public access to EKS Clusters +# input: +# selector: +# - type: cloud +# subtypes: +# - service: eks +# provider: aws +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_cluster#vpc_config +# good_examples: checks/cloud/aws/eks/no_public_cluster_access_to_cidr.tf.go +# bad_examples: checks/cloud/aws/eks/no_public_cluster_access_to_cidr.tf.go +package builtin.aws.eks.aws0041 + +import rego.v1 + +deny contains res if { + some cluster in input.aws.eks.clusters + cluster.publicaccessenabled.value == true + some c in cluster.publicaccesscidrs + cidr.is_public(c.value) + message := sprintf("Cluster allows access from a public CIDR: %s", [c.value]) + res := result.new(message, c) +} diff --git a/checks/cloud/aws/eks/no_public_cluster_access_to_cidr_test.go b/checks/cloud/aws/eks/no_public_cluster_access_to_cidr_test.go deleted file mode 100644 index 390a0952..00000000 --- a/checks/cloud/aws/eks/no_public_cluster_access_to_cidr_test.go +++ /dev/null @@ -1,83 +0,0 @@ -package eks - -import ( - "testing" - - trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/aws/eks" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/stretchr/testify/assert" -) - -func TestCheckNoPublicClusterAccessToCidr(t *testing.T) { - tests := []struct { - name string - input eks.EKS - expected bool - }{ - { - name: "EKS Cluster with public access CIDRs actively set to open", - input: eks.EKS{ - Clusters: []eks.Cluster{ - { - PublicAccessEnabled: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - PublicAccessCIDRs: []trivyTypes.StringValue{ - trivyTypes.String("0.0.0.0/0", trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: true, - }, - { - name: "EKS Cluster with public access enabled but private CIDRs", - input: eks.EKS{ - Clusters: []eks.Cluster{ - { - PublicAccessEnabled: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - PublicAccessCIDRs: []trivyTypes.StringValue{ - trivyTypes.String("10.2.0.0/8", trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: false, - }, - { - name: "EKS Cluster with public access disabled and private CIDRs", - input: eks.EKS{ - Clusters: []eks.Cluster{ - { - PublicAccessEnabled: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - PublicAccessCIDRs: []trivyTypes.StringValue{ - trivyTypes.String("10.2.0.0/8", trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.AWS.EKS = test.input - results := CheckNoPublicClusterAccessToCidr.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckNoPublicClusterAccessToCidr.LongID() { - found = true - } - } - if test.expected { - assert.True(t, found, "Rule should have been found") - } else { - assert.False(t, found, "Rule should not have been found") - } - }) - } -} diff --git a/checks/cloud/aws/eks/no_public_cluster_access_to_cidr_test.rego b/checks/cloud/aws/eks/no_public_cluster_access_to_cidr_test.rego new file mode 100644 index 00000000..095b8aa2 --- /dev/null +++ b/checks/cloud/aws/eks/no_public_cluster_access_to_cidr_test.rego @@ -0,0 +1,33 @@ +package builtin.aws.eks.aws0041_test + +import rego.v1 + +import data.builtin.aws.eks.aws0041 as check +import data.lib.test + +test_deny_eks_cluster_with_public_access_enabled_and_public_cidr if { + inp := {"aws": {"eks": {"clusters": [{ + "publicaccessenabled": {"value": true}, + "publicaccesscidrs": [{"value": "0.0.0.0/0"}], + }]}}} + + test.assert_equal_message("Cluster allows access from a public CIDR: 0.0.0.0/0", check.deny) with input as inp +} + +test_allow_eks_cluster_without_public_access_enabled_and_private_cidr if { + inp := {"aws": {"eks": {"clusters": [{ + "publicaccessenabled": {"value": true}, + "publicaccesscidrs": [{"value": "10.2.0.0/8"}], + }]}}} + + test.assert_empty(check.deny) with input as inp +} + +test_allow_eks_cluster_without_public_access_disabled_and_private_cidr if { + inp := {"aws": {"eks": {"clusters": [{ + "publicaccessenabled": {"value": false}, + "publicaccesscidrs": [{"value": "10.2.0.0/8"}], + }]}}} + + test.assert_empty(check.deny) with input as inp +} diff --git a/test/rego/aws_ecr_test.go b/test/rego/aws_ecr_test.go new file mode 100644 index 00000000..bc681601 --- /dev/null +++ b/test/rego/aws_ecr_test.go @@ -0,0 +1,216 @@ +package test + +import ( + "github.com/aquasecurity/trivy/pkg/iac/providers/aws" + "github.com/aquasecurity/trivy/pkg/iac/providers/aws/ecr" + "github.com/aquasecurity/trivy/pkg/iac/providers/aws/iam" + "github.com/aquasecurity/trivy/pkg/iac/state" + trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" + "github.com/liamg/iamgo" +) + +var awsEcrTestCases = testCases{ + "AVD-AWS-0030": { + { + name: "ECR repository with image scans disabled", + input: state.State{AWS: aws.AWS{ECR: ecr.ECR{ + Repositories: []ecr.Repository{ + { + Metadata: trivyTypes.NewTestMetadata(), + ImageScanning: ecr.ImageScanning{ + Metadata: trivyTypes.NewTestMetadata(), + ScanOnPush: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), + }, + }, + }, + }}}, + expected: true, + }, + { + name: "ECR repository with image scans enabled", + input: state.State{AWS: aws.AWS{ECR: ecr.ECR{ + Repositories: []ecr.Repository{ + { + Metadata: trivyTypes.NewTestMetadata(), + ImageScanning: ecr.ImageScanning{ + Metadata: trivyTypes.NewTestMetadata(), + ScanOnPush: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), + }, + }, + }, + }}}, + expected: false, + }, + }, + "AVD-AWS-0031": { + { + name: "ECR mutable image tags", + input: state.State{AWS: aws.AWS{ECR: ecr.ECR{ + Repositories: []ecr.Repository{ + { + Metadata: trivyTypes.NewTestMetadata(), + ImageTagsImmutable: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), + }, + }, + }}}, + expected: true, + }, + { + name: "ECR immutable image tags", + input: state.State{AWS: aws.AWS{ECR: ecr.ECR{ + Repositories: []ecr.Repository{ + { + Metadata: trivyTypes.NewTestMetadata(), + ImageTagsImmutable: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), + }, + }, + }}}, + expected: false, + }, + }, + "AVD-AWS-0032": { + { + name: "ECR repository policy with wildcard principal", + input: state.State{AWS: aws.AWS{ECR: ecr.ECR{ + Repositories: []ecr.Repository{ + { + Metadata: trivyTypes.NewTestMetadata(), + Policies: func() []iam.Policy { + + sb := iamgo.NewStatementBuilder() + sb.WithSid("new policy") + sb.WithEffect("Allow") + sb.WithAllPrincipals(true) + sb.WithActions([]string{ + "ecr:GetDownloadUrlForLayer", + "ecr:BatchGetImage", + "ecr:BatchCheckLayerAvailability", + "ecr:PutImage", + "ecr:InitiateLayerUpload", + "ecr:UploadLayerPart", + "ecr:CompleteLayerUpload", + "ecr:DescribeRepositories", + "ecr:GetRepositoryPolicy", + "ecr:ListImages", + "ecr:DeleteRepository", + "ecr:BatchDeleteImage", + "ecr:SetRepositoryPolicy", + "ecr:DeleteRepositoryPolicy", + }) + + builder := iamgo.NewPolicyBuilder() + builder.WithVersion("2021-10-07") + builder.WithStatement(sb.Build()) + + return []iam.Policy{ + { + Document: iam.Document{ + Metadata: trivyTypes.NewTestMetadata(), + Parsed: builder.Build(), + }, + }, + } + }(), + }, + }, + }}}, + expected: true, + }, + { + name: "ECR repository policy with specific principal", + input: state.State{AWS: aws.AWS{ECR: ecr.ECR{ + Repositories: []ecr.Repository{ + { + Metadata: trivyTypes.NewTestMetadata(), + Policies: func() []iam.Policy { + + sb := iamgo.NewStatementBuilder() + sb.WithSid("new policy") + sb.WithEffect("Allow") + sb.WithAWSPrincipals([]string{"arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"}) + sb.WithActions([]string{ + "ecr:GetDownloadUrlForLayer", + "ecr:BatchGetImage", + "ecr:BatchCheckLayerAvailability", + "ecr:PutImage", + "ecr:InitiateLayerUpload", + "ecr:UploadLayerPart", + "ecr:CompleteLayerUpload", + "ecr:DescribeRepositories", + "ecr:GetRepositoryPolicy", + "ecr:ListImages", + "ecr:DeleteRepository", + "ecr:BatchDeleteImage", + "ecr:SetRepositoryPolicy", + "ecr:DeleteRepositoryPolicy", + }) + + builder := iamgo.NewPolicyBuilder() + builder.WithVersion("2021-10-07") + builder.WithStatement(sb.Build()) + + return []iam.Policy{ + { + Document: iam.Document{ + Metadata: trivyTypes.NewTestMetadata(), + Parsed: builder.Build(), + }, + }, + } + }(), + }, + }, + }}}, + expected: false, + }, + }, + "AVD-AWS-0033": { + { + name: "ECR repository not using KMS encryption", + input: state.State{AWS: aws.AWS{ECR: ecr.ECR{ + Repositories: []ecr.Repository{ + { + Metadata: trivyTypes.NewTestMetadata(), + Encryption: ecr.Encryption{ + Metadata: trivyTypes.NewTestMetadata(), + Type: trivyTypes.String(ecr.EncryptionTypeAES256, trivyTypes.NewTestMetadata()), + }, + }, + }, + }}}, + expected: true, + }, + { + name: "ECR repository using KMS encryption but missing key", + input: state.State{AWS: aws.AWS{ECR: ecr.ECR{ + Repositories: []ecr.Repository{ + { + Metadata: trivyTypes.NewTestMetadata(), + Encryption: ecr.Encryption{ + Metadata: trivyTypes.NewTestMetadata(), + Type: trivyTypes.String(ecr.EncryptionTypeKMS, trivyTypes.NewTestMetadata()), + KMSKeyID: trivyTypes.String("", trivyTypes.NewTestMetadata()), + }, + }, + }, + }}}, + expected: true, + }, + { + name: "ECR repository encrypted with KMS key", + input: state.State{AWS: aws.AWS{ECR: ecr.ECR{ + Repositories: []ecr.Repository{ + { + Metadata: trivyTypes.NewTestMetadata(), + Encryption: ecr.Encryption{ + Metadata: trivyTypes.NewTestMetadata(), + Type: trivyTypes.String(ecr.EncryptionTypeKMS, trivyTypes.NewTestMetadata()), + KMSKeyID: trivyTypes.String("some-kms-key", trivyTypes.NewTestMetadata()), + }, + }, + }, + }}}, + expected: false, + }, + }, +} diff --git a/test/rego/aws_efs_test.go b/test/rego/aws_efs_test.go new file mode 100644 index 00000000..149fb4ec --- /dev/null +++ b/test/rego/aws_efs_test.go @@ -0,0 +1,35 @@ +package test + +import ( + "github.com/aquasecurity/trivy/pkg/iac/providers/aws" + "github.com/aquasecurity/trivy/pkg/iac/providers/aws/efs" + "github.com/aquasecurity/trivy/pkg/iac/state" + trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" +) + +var awsEfsTestCases = testCases{ + "AVD-AWS-0037": { + { + name: "positive result", + input: state.State{AWS: aws.AWS{EFS: efs.EFS{ + FileSystems: []efs.FileSystem{ + { + Metadata: trivyTypes.NewTestMetadata(), + Encrypted: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), + }}, + }}}, + expected: true, + }, + { + name: "negative result", + input: state.State{AWS: aws.AWS{EFS: efs.EFS{ + FileSystems: []efs.FileSystem{ + { + Metadata: trivyTypes.NewTestMetadata(), + Encrypted: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), + }}, + }}}, + expected: false, + }, + }, +} diff --git a/test/rego/aws_eks_test.go b/test/rego/aws_eks_test.go new file mode 100644 index 00000000..0c9cc2e8 --- /dev/null +++ b/test/rego/aws_eks_test.go @@ -0,0 +1,185 @@ +package test + +import ( + "github.com/aquasecurity/trivy/pkg/iac/providers/aws" + "github.com/aquasecurity/trivy/pkg/iac/providers/aws/eks" + "github.com/aquasecurity/trivy/pkg/iac/state" + trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" +) + +var awsEksTestCases = testCases{ + "AVD-AWS-0038": { + { + name: "EKS cluster with all cluster logging disabled", + input: state.State{AWS: aws.AWS{EKS: eks.EKS{ + Clusters: []eks.Cluster{ + { + Metadata: trivyTypes.NewTestMetadata(), + Logging: eks.Logging{ + API: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), + Audit: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), + Authenticator: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), + ControllerManager: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), + Scheduler: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), + }, + }, + }, + }}}, + expected: true, + }, + { + name: "EKS cluster with only some cluster logging enabled", + input: state.State{AWS: aws.AWS{EKS: eks.EKS{ + Clusters: []eks.Cluster{ + { + Metadata: trivyTypes.NewTestMetadata(), + Logging: eks.Logging{ + API: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), + Audit: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), + Authenticator: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), + ControllerManager: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), + Scheduler: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), + }, + }, + }, + }}}, + expected: true, + }, + { + name: "EKS cluster with all cluster logging enabled", + input: state.State{AWS: aws.AWS{EKS: eks.EKS{ + Clusters: []eks.Cluster{ + { + Metadata: trivyTypes.NewTestMetadata(), + Logging: eks.Logging{ + API: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), + Audit: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), + Authenticator: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), + ControllerManager: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), + Scheduler: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), + }, + }, + }, + }}}, + expected: false, + }, + }, + "AVD-AWS-0039": { + { + name: "EKS Cluster with no secrets in the resources attribute", + input: state.State{AWS: aws.AWS{EKS: eks.EKS{ + Clusters: []eks.Cluster{ + { + Metadata: trivyTypes.NewTestMetadata(), + Encryption: eks.Encryption{ + Metadata: trivyTypes.NewTestMetadata(), + Secrets: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), + KMSKeyID: trivyTypes.String("", trivyTypes.NewTestMetadata()), + }, + }, + }, + }}}, + expected: true, + }, + { + name: "EKS Cluster with secrets in the resources attribute but no KMS key", + input: state.State{AWS: aws.AWS{EKS: eks.EKS{ + Clusters: []eks.Cluster{ + { + Metadata: trivyTypes.NewTestMetadata(), + Encryption: eks.Encryption{ + Metadata: trivyTypes.NewTestMetadata(), + Secrets: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), + KMSKeyID: trivyTypes.String("", trivyTypes.NewTestMetadata()), + }, + }, + }, + }}}, + expected: true, + }, + { + name: "EKS Cluster with secrets in the resources attribute and a KMS key", + input: state.State{AWS: aws.AWS{EKS: eks.EKS{ + Clusters: []eks.Cluster{ + { + Metadata: trivyTypes.NewTestMetadata(), + Encryption: eks.Encryption{ + Metadata: trivyTypes.NewTestMetadata(), + Secrets: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), + KMSKeyID: trivyTypes.String("some-arn", trivyTypes.NewTestMetadata()), + }, + }, + }, + }}}, + expected: false, + }, + }, + "AVD-AWS-0041": { + { + name: "EKS Cluster with public access CIDRs actively set to open", + input: state.State{AWS: aws.AWS{EKS: eks.EKS{ + Clusters: []eks.Cluster{ + { + PublicAccessEnabled: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), + PublicAccessCIDRs: []trivyTypes.StringValue{ + trivyTypes.String("0.0.0.0/0", trivyTypes.NewTestMetadata()), + }, + }, + }, + }}}, + expected: true, + }, + { + name: "EKS Cluster with public access enabled but private CIDRs", + input: state.State{AWS: aws.AWS{EKS: eks.EKS{ + Clusters: []eks.Cluster{ + { + PublicAccessEnabled: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), + PublicAccessCIDRs: []trivyTypes.StringValue{ + trivyTypes.String("10.2.0.0/8", trivyTypes.NewTestMetadata()), + }, + }, + }, + }}}, + expected: false, + }, + { + name: "EKS Cluster with public access disabled and private CIDRs", + input: state.State{AWS: aws.AWS{EKS: eks.EKS{ + Clusters: []eks.Cluster{ + { + PublicAccessEnabled: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), + PublicAccessCIDRs: []trivyTypes.StringValue{ + trivyTypes.String("10.2.0.0/8", trivyTypes.NewTestMetadata()), + }, + }, + }, + }}}, + expected: false, + }, + }, + "AVD-AWS-0040": { + { + name: "EKS Cluster with public access enabled", + input: state.State{AWS: aws.AWS{EKS: eks.EKS{ + Clusters: []eks.Cluster{ + { + PublicAccessEnabled: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), + }, + }, + }}}, + expected: true, + }, + { + name: "EKS Cluster with public access disabled", + input: state.State{AWS: aws.AWS{EKS: eks.EKS{ + Clusters: []eks.Cluster{ + { + PublicAccessEnabled: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), + }, + }, + }}}, + expected: false, + }, + }, +} diff --git a/test/rego/rego_checks_test.go b/test/rego/rego_checks_test.go index f01e2613..a3ceda39 100644 --- a/test/rego/rego_checks_test.go +++ b/test/rego/rego_checks_test.go @@ -54,7 +54,10 @@ func TestRegoChecks(t *testing.T) { awsDocumentDBTestCases, awsDynamodbTestCases, awsS3TestCases, - + awsEcrTestCases, + awsEfsTestCases, + awsEksTestCases, + azureDataFactoryTestCases, azureDataLakeTestCases, azureKeyVaultTestCases, @@ -62,13 +65,13 @@ func TestRegoChecks(t *testing.T) { azureAuthorizationTestCases, azureContainerTestCases, - googleDnsTestCases, + googleDnsTestCases, googleKmsTestCases, googleBigQueryTestCases, - githubTestCases, + githubTestCases, - nifcloudDnsTestCases, + nifcloudDnsTestCases, nifcloudNetworkTestCases, nifcloudSslCertificateTestCases, ) From 3020e37c2b9d0baad5801196d26b4df289a5929a Mon Sep 17 00:00:00 2001 From: Nikita Pivkin Date: Thu, 22 Aug 2024 16:33:40 +0600 Subject: [PATCH 2/4] test: initialise tests in each test file Signed-off-by: Nikita Pivkin --- test/rego/aws_access_analyzer_test.go | 4 ++ test/rego/aws_athena_test.go | 4 ++ test/rego/aws_cloudtrail_test.go | 4 ++ test/rego/aws_codebuild_test.go | 4 ++ test/rego/aws_config_test.go | 4 ++ test/rego/aws_document_db_test.go | 4 ++ test/rego/aws_dynamodb_test.go | 4 ++ test/rego/aws_s3_test.go | 4 ++ test/rego/azure_appservice_test.go | 4 ++ test/rego/azure_authorization_test.go | 4 ++ test/rego/azure_compute_test.go | 4 ++ test/rego/azure_container_test.go | 4 ++ test/rego/azure_database_test.go | 4 ++ test/rego/azure_datafactory_test.go | 4 ++ test/rego/azure_datalake_test.go | 4 ++ test/rego/azure_keyvault_test.go | 4 ++ test/rego/azure_monitor_test.go | 4 ++ test/rego/azure_network_test.go | 4 ++ test/rego/azure_securitycenter_test.go | 4 ++ test/rego/azure_synapse_test.go | 4 ++ test/rego/digitalocean_spaces_test.go | 4 ++ test/rego/github_test.go | 4 ++ test/rego/google_bigquery_test.go | 4 ++ test/rego/google_dns_test.go | 4 ++ test/rego/google_kms_test.go | 4 ++ test/rego/nifcloud_dns_test.go | 4 ++ test/rego/nifcloud_network_test.go | 4 ++ test/rego/nifcloud_sslcertificate_test.go | 4 ++ test/rego/oracle_test.go | 4 ++ test/rego/rego_checks_test.go | 83 +++++++---------------- 30 files changed, 142 insertions(+), 57 deletions(-) diff --git a/test/rego/aws_access_analyzer_test.go b/test/rego/aws_access_analyzer_test.go index 3b73af68..ac51db58 100644 --- a/test/rego/aws_access_analyzer_test.go +++ b/test/rego/aws_access_analyzer_test.go @@ -7,6 +7,10 @@ import ( trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" ) +func init() { + addTests(awsAccessAnalyzerTestCases) +} + var awsAccessAnalyzerTestCases = testCases{ "AVD-AWS-0175": { // TODO: Trivy does not export empty structures into Rego diff --git a/test/rego/aws_athena_test.go b/test/rego/aws_athena_test.go index 82bf1a5e..aa52037c 100644 --- a/test/rego/aws_athena_test.go +++ b/test/rego/aws_athena_test.go @@ -7,6 +7,10 @@ import ( trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" ) +func init() { + addTests(awsAthenaTestCases) +} + var awsAthenaTestCases = testCases{ "AVD-AWS-0006": { { diff --git a/test/rego/aws_cloudtrail_test.go b/test/rego/aws_cloudtrail_test.go index 0b5ac9f8..325fda1a 100644 --- a/test/rego/aws_cloudtrail_test.go +++ b/test/rego/aws_cloudtrail_test.go @@ -8,6 +8,10 @@ import ( trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" ) +func init() { + addTests(awsCloudTrailTestCases) +} + var awsCloudTrailTestCases = testCases{ "AVD-AWS-0014": { { diff --git a/test/rego/aws_codebuild_test.go b/test/rego/aws_codebuild_test.go index 991e0dc1..1b157bf4 100644 --- a/test/rego/aws_codebuild_test.go +++ b/test/rego/aws_codebuild_test.go @@ -7,6 +7,10 @@ import ( trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" ) +func init() { + addTests(awsCodeBuildTestCases) +} + var awsCodeBuildTestCases = testCases{ "AVD-AWS-0018": { { diff --git a/test/rego/aws_config_test.go b/test/rego/aws_config_test.go index 48939c4f..2ba2945a 100644 --- a/test/rego/aws_config_test.go +++ b/test/rego/aws_config_test.go @@ -7,6 +7,10 @@ import ( trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" ) +func init() { + addTests(awsConfigTestCases) +} + var awsConfigTestCases = testCases{ "AVD-AWS-0019": { { diff --git a/test/rego/aws_document_db_test.go b/test/rego/aws_document_db_test.go index b5a24cc9..a8ae49eb 100644 --- a/test/rego/aws_document_db_test.go +++ b/test/rego/aws_document_db_test.go @@ -7,6 +7,10 @@ import ( trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" ) +func init() { + addTests(awsDocumentDBTestCases) +} + var awsDocumentDBTestCases = testCases{ "AVD-AWS-0020": { { diff --git a/test/rego/aws_dynamodb_test.go b/test/rego/aws_dynamodb_test.go index 5400c1c2..7e630709 100644 --- a/test/rego/aws_dynamodb_test.go +++ b/test/rego/aws_dynamodb_test.go @@ -7,6 +7,10 @@ import ( trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" ) +func init() { + addTests(awsDynamodbTestCases) +} + var awsDynamodbTestCases = testCases{ "AVD-AWS-0023": { { diff --git a/test/rego/aws_s3_test.go b/test/rego/aws_s3_test.go index f7fae0b7..8045a09a 100644 --- a/test/rego/aws_s3_test.go +++ b/test/rego/aws_s3_test.go @@ -8,6 +8,10 @@ import ( trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" ) +func init() { + addTests(awsS3TestCases) +} + var awsS3TestCases = testCases{ "AVD-AWS-0086": { { diff --git a/test/rego/azure_appservice_test.go b/test/rego/azure_appservice_test.go index f423b0f3..a0d6a250 100644 --- a/test/rego/azure_appservice_test.go +++ b/test/rego/azure_appservice_test.go @@ -7,6 +7,10 @@ import ( trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" ) +func init() { + addTests(azureAppServiceTestCases) +} + var azureAppServiceTestCases = testCases{ "AVD-AZU-0002": { { diff --git a/test/rego/azure_authorization_test.go b/test/rego/azure_authorization_test.go index ba0a3cda..833fc7df 100644 --- a/test/rego/azure_authorization_test.go +++ b/test/rego/azure_authorization_test.go @@ -7,6 +7,10 @@ import ( trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" ) +func init() { + addTests(azureAuthorizationTestCases) +} + var azureAuthorizationTestCases = testCases{ "AVD-AZU-0030": { { diff --git a/test/rego/azure_compute_test.go b/test/rego/azure_compute_test.go index 2d71e68d..5cb3ef48 100644 --- a/test/rego/azure_compute_test.go +++ b/test/rego/azure_compute_test.go @@ -7,6 +7,10 @@ import ( trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" ) +func init() { + addTests(azureComputeTestCases) +} + var azureComputeTestCases = testCases{ "AVD-AZU-0039": { { diff --git a/test/rego/azure_container_test.go b/test/rego/azure_container_test.go index 8714be20..3f6e181c 100644 --- a/test/rego/azure_container_test.go +++ b/test/rego/azure_container_test.go @@ -7,6 +7,10 @@ import ( trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" ) +func init() { + addTests(azureContainerTestCases) +} + var azureContainerTestCases = testCases{ "AVD-AZU-0043": { { diff --git a/test/rego/azure_database_test.go b/test/rego/azure_database_test.go index 743c50a8..5ef6dd41 100644 --- a/test/rego/azure_database_test.go +++ b/test/rego/azure_database_test.go @@ -7,6 +7,10 @@ import ( trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" ) +func init() { + addTests(azureDatabaseTestCases) +} + var azureDatabaseTestCases = testCases{ "AVD-AZU-0028": { { diff --git a/test/rego/azure_datafactory_test.go b/test/rego/azure_datafactory_test.go index 7d6108eb..41d1c027 100644 --- a/test/rego/azure_datafactory_test.go +++ b/test/rego/azure_datafactory_test.go @@ -7,6 +7,10 @@ import ( trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" ) +func init() { + addTests(azureDataFactoryTestCases) +} + var azureDataFactoryTestCases = testCases{ "AVD-AZU-0035": { { diff --git a/test/rego/azure_datalake_test.go b/test/rego/azure_datalake_test.go index 3dedf9f1..967598a5 100644 --- a/test/rego/azure_datalake_test.go +++ b/test/rego/azure_datalake_test.go @@ -7,6 +7,10 @@ import ( trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" ) +func init() { + addTests(azureDataLakeTestCases) +} + var azureDataLakeTestCases = testCases{ "AVD-AZU-0036": { { diff --git a/test/rego/azure_keyvault_test.go b/test/rego/azure_keyvault_test.go index 7717815b..4010fb08 100644 --- a/test/rego/azure_keyvault_test.go +++ b/test/rego/azure_keyvault_test.go @@ -9,6 +9,10 @@ import ( trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" ) +func init() { + addTests(azureKeyVaultTestCases) +} + var azureKeyVaultTestCases = testCases{ "AVD-AZU-0015": { { diff --git a/test/rego/azure_monitor_test.go b/test/rego/azure_monitor_test.go index f650a49b..9c0cb73d 100644 --- a/test/rego/azure_monitor_test.go +++ b/test/rego/azure_monitor_test.go @@ -7,6 +7,10 @@ import ( trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" ) +func init() { + addTests(azureMonitorTestCases) +} + var azureMonitorTestCases = testCases{ "AVD-AZU-0031": { { diff --git a/test/rego/azure_network_test.go b/test/rego/azure_network_test.go index 3318f9f8..a1c5a818 100644 --- a/test/rego/azure_network_test.go +++ b/test/rego/azure_network_test.go @@ -7,6 +7,10 @@ import ( trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" ) +func init() { + addTests(azureNetworkTestCases) +} + var azureNetworkTestCases = testCases{ "AVD-AZU-0048": { { diff --git a/test/rego/azure_securitycenter_test.go b/test/rego/azure_securitycenter_test.go index 277fb22a..19b84ff1 100644 --- a/test/rego/azure_securitycenter_test.go +++ b/test/rego/azure_securitycenter_test.go @@ -7,6 +7,10 @@ import ( trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" ) +func init() { + addTests(azureSecurityCenterTestCases) +} + var azureSecurityCenterTestCases = testCases{ "AVD-AZU-0044": { { diff --git a/test/rego/azure_synapse_test.go b/test/rego/azure_synapse_test.go index 9e50650b..ad7074cc 100644 --- a/test/rego/azure_synapse_test.go +++ b/test/rego/azure_synapse_test.go @@ -7,6 +7,10 @@ import ( trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" ) +func init() { + addTests(azureSynapseTestCases) +} + var azureSynapseTestCases = testCases{ "AVD-AZU-0034": { { diff --git a/test/rego/digitalocean_spaces_test.go b/test/rego/digitalocean_spaces_test.go index ed03b7f3..c7298629 100644 --- a/test/rego/digitalocean_spaces_test.go +++ b/test/rego/digitalocean_spaces_test.go @@ -7,6 +7,10 @@ import ( trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" ) +func init() { + addTests(digitalOceanSpacesTestCases) +} + var digitalOceanSpacesTestCases = testCases{ "AVD-DIG-0006": { { diff --git a/test/rego/github_test.go b/test/rego/github_test.go index 907e9f2e..23b07b62 100644 --- a/test/rego/github_test.go +++ b/test/rego/github_test.go @@ -6,6 +6,10 @@ import ( trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" ) +func init() { + addTests(githubTestCases) +} + var githubTestCases = testCases{ "AVD-GIT-0004": { { diff --git a/test/rego/google_bigquery_test.go b/test/rego/google_bigquery_test.go index 6e33b3cc..6dc1b3e9 100644 --- a/test/rego/google_bigquery_test.go +++ b/test/rego/google_bigquery_test.go @@ -7,6 +7,10 @@ import ( trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" ) +func init() { + addTests(googleBigQueryTestCases) +} + var googleBigQueryTestCases = testCases{ "AVD-GCP-0046": { { diff --git a/test/rego/google_dns_test.go b/test/rego/google_dns_test.go index f598cbc1..e8680a9e 100644 --- a/test/rego/google_dns_test.go +++ b/test/rego/google_dns_test.go @@ -7,6 +7,10 @@ import ( trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" ) +func init() { + addTests(googleDnsTestCases) +} + var googleDnsTestCases = testCases{ "AVD-GCP-0013": { { diff --git a/test/rego/google_kms_test.go b/test/rego/google_kms_test.go index 6c0165d5..d91b0d6e 100644 --- a/test/rego/google_kms_test.go +++ b/test/rego/google_kms_test.go @@ -7,6 +7,10 @@ import ( trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" ) +func init() { + addTests(googleKmsTestCases) +} + var googleKmsTestCases = testCases{ "AVD-GCP-0065": { { diff --git a/test/rego/nifcloud_dns_test.go b/test/rego/nifcloud_dns_test.go index d7b8c30d..82e9115c 100644 --- a/test/rego/nifcloud_dns_test.go +++ b/test/rego/nifcloud_dns_test.go @@ -7,6 +7,10 @@ import ( trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" ) +func init() { + addTests(nifcloudDnsTestCases) +} + var nifcloudDnsTestCases = testCases{ "AVD-NIF-0007": { { diff --git a/test/rego/nifcloud_network_test.go b/test/rego/nifcloud_network_test.go index fd6897e5..19cb1005 100644 --- a/test/rego/nifcloud_network_test.go +++ b/test/rego/nifcloud_network_test.go @@ -7,6 +7,10 @@ import ( trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" ) +func init() { + addTests(nifcloudNetworkTestCases) +} + var nifcloudNetworkTestCases = testCases{ "AVD-NIF-0016": { { diff --git a/test/rego/nifcloud_sslcertificate_test.go b/test/rego/nifcloud_sslcertificate_test.go index e57784dd..3e2f740a 100644 --- a/test/rego/nifcloud_sslcertificate_test.go +++ b/test/rego/nifcloud_sslcertificate_test.go @@ -9,6 +9,10 @@ import ( trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" ) +func init() { + addTests(nifcloudSslCertificateTestCases) +} + var nifcloudSslCertificateTestCases = testCases{ "AVD-NIF-0006": { { diff --git a/test/rego/oracle_test.go b/test/rego/oracle_test.go index 17dfcee5..7f237498 100644 --- a/test/rego/oracle_test.go +++ b/test/rego/oracle_test.go @@ -6,6 +6,10 @@ import ( trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" ) +func init() { + addTests(oracleTestCases) +} + var oracleTestCases = testCases{ "AVD-OCI-0001": { { diff --git a/test/rego/rego_checks_test.go b/test/rego/rego_checks_test.go index 16538613..0b23a313 100644 --- a/test/rego/rego_checks_test.go +++ b/test/rego/rego_checks_test.go @@ -16,26 +16,6 @@ import ( "github.com/stretchr/testify/require" ) -func scanState(t *testing.T, regoScanner *rego.Scanner, s state.State, checkID string, expected bool) { - results, err := regoScanner.ScanInput(context.TODO(), rego.Input{ - Contents: s.ToRego(), - }) - require.NoError(t, err) - - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().AVDID == checkID { - found = true - } - } - - if expected { - assert.True(t, found, "Rule should have been found") - } else { - assert.False(t, found, "Rule should not have been found") - } -} - type testCase struct { name string input state.State @@ -44,44 +24,13 @@ type testCase struct { type testCases map[string][]testCase -func TestRegoChecks(t *testing.T) { - tests := lo.Assign( - awsAccessAnalyzerTestCases, - awsAthenaTestCases, - awsCloudTrailTestCases, - awsCodeBuildTestCases, - awsConfigTestCases, - awsDocumentDBTestCases, - awsDynamodbTestCases, - awsS3TestCases, - - azureMonitorTestCases, - azureNetworkTestCases, - azureSynapseTestCases, - azureSecurityCenterTestCases, - azureDataFactoryTestCases, - azureDataLakeTestCases, - azureKeyVaultTestCases, - azureAppServiceTestCases, - azureAuthorizationTestCases, - azureContainerTestCases, - azureDatabaseTestCases, - azureComputeTestCases, - - googleDnsTestCases, - googleKmsTestCases, - googleBigQueryTestCases, - - githubTestCases, - - nifcloudDnsTestCases, - nifcloudNetworkTestCases, - nifcloudSslCertificateTestCases, - - digitalOceanSpacesTestCases, - oracleTestCases, - ) +var tests = make(testCases) + +func addTests(tc testCases) { + tests = lo.Assign(tests, tc) +} +func TestRegoChecks(t *testing.T) { regoScanner := rego.NewScanner(trivyTypes.SourceCloud) err := regoScanner.LoadPolicies(true, false, checks.EmbeddedPolicyFileSystem, []string{"."}, nil) require.NoError(t, err) @@ -100,6 +49,26 @@ func TestRegoChecks(t *testing.T) { } } +func scanState(t *testing.T, regoScanner *rego.Scanner, s state.State, checkID string, expected bool) { + results, err := regoScanner.ScanInput(context.TODO(), rego.Input{ + Contents: s.ToRego(), + }) + require.NoError(t, err) + + var found bool + for _, result := range results { + if result.Status() == scan.StatusFailed && result.Rule().AVDID == checkID { + found = true + } + } + + if expected { + assert.True(t, found, "Rule should have been found") + } else { + assert.False(t, found, "Rule should not have been found") + } +} + func getMigratedChecksIDs() []string { allChecks := rules.GetRegistered() From 4f4537cff6e7c47da81d4542fc5f3a3ffc727340 Mon Sep 17 00:00:00 2001 From: Nikita Pivkin Date: Thu, 22 Aug 2024 18:57:52 +0600 Subject: [PATCH 3/4] test(bundle): use only canary Trivy Signed-off-by: Nikita Pivkin --- scripts/verify-bundle.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/verify-bundle.go b/scripts/verify-bundle.go index b730d4af..fdf30ced 100644 --- a/scripts/verify-bundle.go +++ b/scripts/verify-bundle.go @@ -15,7 +15,7 @@ import ( var bundlePath = "bundle.tar.gz" var OrasPush = []string{"--config", "/dev/null:application/vnd.cncf.openpolicyagent.config.v1+json", fmt.Sprintf("%s:application/vnd.cncf.openpolicyagent.layer.v1.tar+gzip", bundlePath)} -var supportedTrivyVersions = []string{"latest", "canary"} // TODO: add more versions +var supportedTrivyVersions = []string{"canary"} // TODO: add more versions func createRegistryContainer(ctx context.Context) (testcontainers.Container, string) { reqReg := testcontainers.ContainerRequest{ From ab5a4abd7e294a124ee866dfd1285fcc49f932d3 Mon Sep 17 00:00:00 2001 From: Simar Date: Mon, 26 Aug 2024 16:57:05 -0600 Subject: [PATCH 4/4] update docs --- avd_docs/aws/ecr/AVD-AWS-0031/docs.md | 1 - checks/cloud/aws/ecr/enforce_immutable_repository.rego | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/avd_docs/aws/ecr/AVD-AWS-0031/docs.md b/avd_docs/aws/ecr/AVD-AWS-0031/docs.md index e0e43dcc..5101dca3 100644 --- a/avd_docs/aws/ecr/AVD-AWS-0031/docs.md +++ b/avd_docs/aws/ecr/AVD-AWS-0031/docs.md @@ -1,6 +1,5 @@ ECR images should be set to IMMUTABLE to prevent code injection through image mutation. - This can be done by setting image_tag_mutability to IMMUTABLE diff --git a/checks/cloud/aws/ecr/enforce_immutable_repository.rego b/checks/cloud/aws/ecr/enforce_immutable_repository.rego index 1a89d859..5f70f0b7 100644 --- a/checks/cloud/aws/ecr/enforce_immutable_repository.rego +++ b/checks/cloud/aws/ecr/enforce_immutable_repository.rego @@ -2,7 +2,7 @@ # title: ECR images tags shouldn't be mutable. # description: | # ECR images should be set to IMMUTABLE to prevent code injection through image mutation. -# This can be done by setting image_tab_mutability to IMMUTABLE +# This can be done by setting image_tag_mutability to IMMUTABLE # scope: package # schemas: # - input: schema["cloud"]