diff --git a/.github/workflows/test-go.yaml b/.github/workflows/test-go.yaml index 22b02d51..2da4b969 100644 --- a/.github/workflows/test-go.yaml +++ b/.github/workflows/test-go.yaml @@ -20,3 +20,17 @@ jobs: - name: Run tests run: make test shell: bash + + integration: + name: Integration Test + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: ${{ env.GO_VERSION }} + + - name: Run integration tests + run: make test-integration + diff --git a/Makefile b/Makefile index 698b7c45..1353b715 100644 --- a/Makefile +++ b/Makefile @@ -6,6 +6,10 @@ REGISTRY_PORT=5111 test: go test -v ./... +.PHONY: integration-test +test-integration: + go test -v -timeout 5m -tags=integration ./integration/... + .PHONY: rego rego: fmt-rego test-rego @@ -33,7 +37,7 @@ outdated-api-updated: sed -i.bak "s|recommendedVersions :=.*|recommendedVersions := $(OUTDATE_API_DATA)|" $(DYNAMIC_REGO_FOLDER)/outdated_api.rego && rm $(DYNAMIC_REGO_FOLDER)/outdated_api.rego.bak .PHONY: docs -docs: +docs: fmt-examples go run ./cmd/avd_generator .PHONY: docs-test diff --git a/avd_docs/aws/cloudtrail/AVD-AWS-0015/Terraform.md b/avd_docs/aws/cloudtrail/AVD-AWS-0015/Terraform.md index e5ca53d5..e6375a20 100644 --- a/avd_docs/aws/cloudtrail/AVD-AWS-0015/Terraform.md +++ b/avd_docs/aws/cloudtrail/AVD-AWS-0015/Terraform.md @@ -2,10 +2,18 @@ Use Customer managed key ```hcl +resource "aws_kms_key" "trail" { + enable_key_rotation = true +} +resource "aws_kms_alias" "trail" { + name = "alias/trail" + target_key_id = aws_kms_key.trail.key_id +} + resource "aws_cloudtrail" "good_example" { is_multi_region_trail = true enable_log_file_validation = true - kms_key_id = var.kms_id + kms_key_id = aws_kms_alias.trail.arn event_selector { read_write_type = "All" diff --git a/avd_docs/aws/cloudwatch/AVD-AWS-0017/Terraform.md b/avd_docs/aws/cloudwatch/AVD-AWS-0017/Terraform.md index c8054250..37e84fb4 100644 --- a/avd_docs/aws/cloudwatch/AVD-AWS-0017/Terraform.md +++ b/avd_docs/aws/cloudwatch/AVD-AWS-0017/Terraform.md @@ -2,10 +2,18 @@ Enable CMK encryption of CloudWatch Log Groups ```hcl +resource "aws_kms_key" "cloudwatch" { + enable_key_rotation = true +} + +resource "aws_kms_alias" "cloudwatch" { + name = "alias/cloudwatch" + target_key_id = aws_kms_key.cloudwatch.key_id +} resource "aws_cloudwatch_log_group" "good_example" { name = "good_example" - kms_key_id = aws_kms_key.log_key.arn + kms_key_id = aws_kms_alias.cloudwatch.arn } ``` diff --git a/avd_docs/aws/ec2/AVD-AWS-0027/CloudFormation.md b/avd_docs/aws/ec2/AVD-AWS-0027/CloudFormation.md index 98036223..74ab110f 100644 --- a/avd_docs/aws/ec2/AVD-AWS-0027/CloudFormation.md +++ b/avd_docs/aws/ec2/AVD-AWS-0027/CloudFormation.md @@ -13,12 +13,19 @@ Resources: ``` ```yaml Resources: + MyKey: + Type: AWS::KMS::Key + Properties: + KeyPolicy: + Version: "2012-10-17" + Id: key-default-1 + GoodExample: DeletionPolicy: Snapshot Type: AWS::EC2::Volume Properties: Encrypted: true - KmsKeyId: MyStack:Key + KmsKeyId: !Ref MyKey Size: 100 ``` diff --git a/avd_docs/aws/ec2/AVD-AWS-0101/Terraform.md b/avd_docs/aws/ec2/AVD-AWS-0101/Terraform.md index 0daac1fc..8e88428c 100644 --- a/avd_docs/aws/ec2/AVD-AWS-0101/Terraform.md +++ b/avd_docs/aws/ec2/AVD-AWS-0101/Terraform.md @@ -3,6 +3,9 @@ Create a non-default vpc for resources to be created in ```hcl # no aws default vpc present +resource "aws_vpc" "main" { + cidr_block = "10.0.0.0/16" +} ``` #### Remediation Links diff --git a/avd_docs/aws/ec2/AVD-AWS-0102/CloudFormation.md b/avd_docs/aws/ec2/AVD-AWS-0102/CloudFormation.md index 53211631..29cf67ca 100644 --- a/avd_docs/aws/ec2/AVD-AWS-0102/CloudFormation.md +++ b/avd_docs/aws/ec2/AVD-AWS-0102/CloudFormation.md @@ -16,7 +16,7 @@ Resources: Rule: Type: AWS::EC2::NetworkAclEntry Properties: - NetworkAclId: null + NetworkAclId: !Ref NetworkACL Protocol: 6 Ref: NetworkACL RuleAction: allow diff --git a/avd_docs/aws/ec2/AVD-AWS-0105/CloudFormation.md b/avd_docs/aws/ec2/AVD-AWS-0105/CloudFormation.md index 72edb44b..a9043ea0 100644 --- a/avd_docs/aws/ec2/AVD-AWS-0105/CloudFormation.md +++ b/avd_docs/aws/ec2/AVD-AWS-0105/CloudFormation.md @@ -19,6 +19,9 @@ Resources: NetworkAclId: !Ref NetworkACL Protocol: 6 RuleAction: allow + PortRange: + From: 22 + To: 22 ``` diff --git a/avd_docs/aws/ec2/AVD-AWS-0107/CloudFormation.md b/avd_docs/aws/ec2/AVD-AWS-0107/CloudFormation.md index 3ab2fff4..d150b2e5 100644 --- a/avd_docs/aws/ec2/AVD-AWS-0107/CloudFormation.md +++ b/avd_docs/aws/ec2/AVD-AWS-0107/CloudFormation.md @@ -10,6 +10,8 @@ Resources: SecurityGroupIngress: - CidrIp: 127.0.0.1/32 IpProtocol: "6" + FromPort: 22 + ToPort: 22 ``` diff --git a/avd_docs/aws/ec2/AVD-AWS-0107/Terraform.md b/avd_docs/aws/ec2/AVD-AWS-0107/Terraform.md index 223f880b..9f4a3efb 100644 --- a/avd_docs/aws/ec2/AVD-AWS-0107/Terraform.md +++ b/avd_docs/aws/ec2/AVD-AWS-0107/Terraform.md @@ -8,9 +8,9 @@ resource "aws_security_group_rule" "good_example" { } ``` ```hcl -resource "aws_security_group_rule" "allow_partner_rsync" { +resource "aws_security_group_rule" "example" { type = "ingress" - security_group_id = aws_security_group.….id + security_group_id = "sg-123456" from_port = 22 to_port = 22 protocol = "tcp" diff --git a/avd_docs/aws/ecs/AVD-AWS-0036/Terraform.md b/avd_docs/aws/ecs/AVD-AWS-0036/Terraform.md index 8819538d..c24e3095 100644 --- a/avd_docs/aws/ecs/AVD-AWS-0036/Terraform.md +++ b/avd_docs/aws/ecs/AVD-AWS-0036/Terraform.md @@ -8,7 +8,7 @@ resource "aws_ecs_task_definition" "good_example" { { "name": "my_service", "essential": true, - "memory": 256, + "memory": "256", "environment": [ { "name": "ENVIRONMENT", "value": "development" } ] diff --git a/avd_docs/aws/eks/AVD-AWS-0039/CloudFormation.md b/avd_docs/aws/eks/AVD-AWS-0039/CloudFormation.md index abf243c2..1ae88b33 100644 --- a/avd_docs/aws/eks/AVD-AWS-0039/CloudFormation.md +++ b/avd_docs/aws/eks/AVD-AWS-0039/CloudFormation.md @@ -7,10 +7,10 @@ Resources: Type: AWS::EKS::Cluster Properties: EncryptionConfig: - Provider: - KeyArn: alias/eks-kms - Resources: - - secrets + - Provider: + KeyArn: alias/eks-kms + Resources: + - secrets Name: goodExample ResourcesVpcConfig: SecurityGroupIds: diff --git a/avd_docs/aws/eks/AVD-AWS-0039/Terraform.md b/avd_docs/aws/eks/AVD-AWS-0039/Terraform.md index 92ba367a..791bdf66 100644 --- a/avd_docs/aws/eks/AVD-AWS-0039/Terraform.md +++ b/avd_docs/aws/eks/AVD-AWS-0039/Terraform.md @@ -2,11 +2,15 @@ Enable encryption of EKS secrets ```hcl +resource "aws_kms_key" "eks" { + enable_key_rotation = true +} + resource "aws_eks_cluster" "good_example" { encryption_config { resources = ["secrets"] provider { - key_arn = var.kms_arn + key_arn = aws_kms_key.eks.arn } } diff --git a/avd_docs/aws/iam/AVD-AWS-0141/Terraform.md b/avd_docs/aws/iam/AVD-AWS-0141/Terraform.md index e4fdabe6..10d8df4e 100644 --- a/avd_docs/aws/iam/AVD-AWS-0141/Terraform.md +++ b/avd_docs/aws/iam/AVD-AWS-0141/Terraform.md @@ -2,8 +2,12 @@ Use lower privileged accounts instead, so only required privileges are available. ```hcl -resource "aws_iam_access_key" "good_example" { - user = "lowprivuser" +resource "aws_iam_user" "test" { + name = "lowprivuser" +} + +resource "aws_iam_access_key" "test" { + user = aws_iam_user.test.name } ``` diff --git a/avd_docs/aws/lambda/AVD-AWS-0066/CloudFormation.md b/avd_docs/aws/lambda/AVD-AWS-0066/CloudFormation.md index 705033c3..5d00b515 100644 --- a/avd_docs/aws/lambda/AVD-AWS-0066/CloudFormation.md +++ b/avd_docs/aws/lambda/AVD-AWS-0066/CloudFormation.md @@ -10,17 +10,9 @@ Resources: S3Bucket: my-bucket S3Key: function.zip Handler: index.handler - Role: arn:aws:iam::123456789012:role/lambda-role Runtime: nodejs12.x - Timeout: 5 TracingConfig: Mode: Active - VpcConfig: - SecurityGroupIds: - - sg-085912345678492fb - SubnetIds: - - subnet-071f712345678e7c8 - - subnet-07fd123456788a036 ``` diff --git a/avd_docs/aws/redshift/AVD-AWS-0085/CloudFormation.md b/avd_docs/aws/redshift/AVD-AWS-0085/CloudFormation.md index b8106555..ea702474 100644 --- a/avd_docs/aws/redshift/AVD-AWS-0085/CloudFormation.md +++ b/avd_docs/aws/redshift/AVD-AWS-0085/CloudFormation.md @@ -6,7 +6,11 @@ AWSTemplateFormatVersion: "2010-09-09" Description: Good example of redshift sgr -Resources: null +myCluster: + Type: AWS::Redshift::Cluster + + Properties: + DBName: mydb ``` diff --git a/avd_docs/aws/s3/AVD-AWS-0089/CloudFormation.md b/avd_docs/aws/s3/AVD-AWS-0089/CloudFormation.md index 0ab131bb..f947ace5 100644 --- a/avd_docs/aws/s3/AVD-AWS-0089/CloudFormation.md +++ b/avd_docs/aws/s3/AVD-AWS-0089/CloudFormation.md @@ -7,27 +7,13 @@ Resources: Type: AWS::S3::Bucket Properties: LoggingConfiguration: - DestinationBucketName: logging-bucket + DestinationBucketName: !Ref TestLoggingBucket LogFilePrefix: accesslogs/ -``` -```yaml -Resources: - GoodExample: + + TestLoggingBucket: Type: AWS::S3::Bucket Properties: - AccessControl: Private - BucketName: my-s3-bucket-${BucketSuffix} - LoggingConfiguration: - DestinationBucketName: - - EnvironmentMapping - - s3 - - logging - LogFilePrefix: s3-logs/AWSLogs/${AWS::AccountId}/my-s3-bucket-${BucketSuffix} - PublicAccessBlockConfiguration: - BlockPublicAcls: true - BlockPublicPolicy: true - IgnorePublicAcls: true - RestrictPublicBuckets: true + AccessControl: LogDeliveryWrite ``` diff --git a/avd_docs/aws/s3/AVD-AWS-0089/Terraform.md b/avd_docs/aws/s3/AVD-AWS-0089/Terraform.md index 7331fa16..db28e3f0 100644 --- a/avd_docs/aws/s3/AVD-AWS-0089/Terraform.md +++ b/avd_docs/aws/s3/AVD-AWS-0089/Terraform.md @@ -2,24 +2,42 @@ Add a logging block to the resource to enable access logging ```hcl -resource "aws_s3_bucket" "good_example" { +resource "aws_s3_bucket" "this" { + bucket = "test-bucket" logging { - target_bucket = "target-bucket" + target_bucket = aws_s3_bucket.log_bucket.id + target_prefix = "log/" } } + +resource "aws_s3_bucket" "log_bucket" { + bucket = "test-log-bucket" +} + +resource "aws_s3_bucket_acl" "log_bucket" { + acl = "log-delivery-write" + bucket = aws_s3_bucket.log_bucket.id +} ``` ```hcl -resource "aws_s3_bucket" "example" { - bucket = "yournamehere" - - # ... other configuration ... +resource "aws_s3_bucket" "this" { + bucket = "test-bucket" } -resource "aws_s3_bucket_logging" "example" { - bucket = aws_s3_bucket.example.id +resource "aws_s3_bucket_logging" "this" { + bucket = aws_s3_bucket.this.id target_bucket = aws_s3_bucket.log_bucket.id target_prefix = "log/" } + +resource "aws_s3_bucket" "log_bucket" { + bucket = "test-log-bucket" +} + +resource "aws_s3_bucket_acl" "log_bucket" { + acl = "log-delivery-write" + bucket = aws_s3_bucket.log_bucket.id +} ``` #### Remediation Links diff --git a/avd_docs/aws/sns/AVD-AWS-0095/CloudFormation.md b/avd_docs/aws/sns/AVD-AWS-0095/CloudFormation.md index 30520d22..00b76e3e 100644 --- a/avd_docs/aws/sns/AVD-AWS-0095/CloudFormation.md +++ b/avd_docs/aws/sns/AVD-AWS-0095/CloudFormation.md @@ -4,7 +4,7 @@ Turn on SNS Topic encryption ```yaml Resources: GoodTopic: - Type: AWS::SQS::Topic + Type: AWS::SNS::Topic Properties: KmsMasterKeyId: some-key TopicName: blah diff --git a/avd_docs/azure/keyvault/AVD-AZU-0017/Terraform.md b/avd_docs/azure/keyvault/AVD-AZU-0017/Terraform.md index 21e0d987..055789a4 100644 --- a/avd_docs/azure/keyvault/AVD-AZU-0017/Terraform.md +++ b/avd_docs/azure/keyvault/AVD-AZU-0017/Terraform.md @@ -20,6 +20,7 @@ resource "azuread_application" "myapp" { resource "azuread_application_password" "myapp" { application_object_id = azuread_application.myapp.object_id + end_date = "2024-12-18T00:00:00Z" } resource "azurerm_key_vault_secret" "myapp_pass" { diff --git a/avd_docs/google/storage/AVD-GCP-0001/Terraform.md b/avd_docs/google/storage/AVD-GCP-0001/Terraform.md index 4ffa5c07..271319cc 100644 --- a/avd_docs/google/storage/AVD-GCP-0001/Terraform.md +++ b/avd_docs/google/storage/AVD-GCP-0001/Terraform.md @@ -2,8 +2,13 @@ Restrict public access to the bucket. ```hcl +resource "google_storage_bucket" "test" { + name = "test" + location = "US" +} + resource "google_storage_bucket_iam_binding" "binding" { - bucket = google_storage_bucket.default.name + bucket = google_storage_bucket.test.name role = "roles/storage.admin" members = [ "user:jane@example.com", diff --git a/avd_docs/nifcloud/computing/AVD-NIF-0001/Terraform.md b/avd_docs/nifcloud/computing/AVD-NIF-0001/Terraform.md index 7eafa1cd..04a46692 100644 --- a/avd_docs/nifcloud/computing/AVD-NIF-0001/Terraform.md +++ b/avd_docs/nifcloud/computing/AVD-NIF-0001/Terraform.md @@ -2,15 +2,15 @@ Set a more restrictive cidr range ```hcl -resource "nifcloud_security_group_rule" "good_example" { - type = "IN" - cidr_ip = "10.0.0.0/16" +resource "nifcloud_security_group_rule" "example" { + group_name = "allowtcp" + availability_zone = "east-11" } ``` ```hcl -resource "nifcloud_security_group_rule" "allow_partner_rsync" { +resource "nifcloud_security_group_rule" "example" { type = "IN" - security_group_names = [nifcloud_security_group.….group_name] + security_group_names = [nifcloud_security_group.example.group_name] from_port = 22 to_port = 22 protocol = "TCP" diff --git a/checks/cloud/aws/apigateway/no_public_access.rego b/checks/cloud/aws/apigateway/no_public_access.rego index d17645e1..3f8a119b 100644 --- a/checks/cloud/aws/apigateway/no_public_access.rego +++ b/checks/cloud/aws/apigateway/no_public_access.rego @@ -37,7 +37,7 @@ deny contains res if { isManaged(api) some method in api.resources[_].methods method_is_not_option(method) - apikey_is_not_required(api) + apikey_is_not_required(method) method.authorizationtype.value == authorization_none res := result.new("Authorization is not enabled for this method.", method.authorizationtype) } diff --git a/checks/cloud/aws/cloudtrail/encryption_customer_key.yaml b/checks/cloud/aws/cloudtrail/encryption_customer_key.yaml index 0ddc5086..605cd396 100644 --- a/checks/cloud/aws/cloudtrail/encryption_customer_key.yaml +++ b/checks/cloud/aws/cloudtrail/encryption_customer_key.yaml @@ -25,10 +25,18 @@ cloudformation: terraform: good: - |- + resource "aws_kms_key" "trail" { + enable_key_rotation = true + } + resource "aws_kms_alias" "trail" { + name = "alias/trail" + target_key_id = aws_kms_key.trail.key_id + } + resource "aws_cloudtrail" "good_example" { is_multi_region_trail = true enable_log_file_validation = true - kms_key_id = var.kms_id + kms_key_id = aws_kms_alias.trail.arn event_selector { read_write_type = "All" diff --git a/checks/cloud/aws/cloudwatch/log_group_customer_key.yaml b/checks/cloud/aws/cloudwatch/log_group_customer_key.yaml index a81f086d..8569c87b 100644 --- a/checks/cloud/aws/cloudwatch/log_group_customer_key.yaml +++ b/checks/cloud/aws/cloudwatch/log_group_customer_key.yaml @@ -20,10 +20,18 @@ cloudformation: terraform: good: - |- + resource "aws_kms_key" "cloudwatch" { + enable_key_rotation = true + } + + resource "aws_kms_alias" "cloudwatch" { + name = "alias/cloudwatch" + target_key_id = aws_kms_key.cloudwatch.key_id + } resource "aws_cloudwatch_log_group" "good_example" { name = "good_example" - kms_key_id = aws_kms_key.log_key.arn + kms_key_id = aws_kms_alias.cloudwatch.arn } bad: - |- diff --git a/checks/cloud/aws/ec2/encryption_customer_key.yaml b/checks/cloud/aws/ec2/encryption_customer_key.yaml index b2134873..8a2fc99e 100644 --- a/checks/cloud/aws/ec2/encryption_customer_key.yaml +++ b/checks/cloud/aws/ec2/encryption_customer_key.yaml @@ -11,12 +11,19 @@ cloudformation: Size: 100 - |- Resources: + MyKey: + Type: AWS::KMS::Key + Properties: + KeyPolicy: + Version: "2012-10-17" + Id: key-default-1 + GoodExample: DeletionPolicy: Snapshot Type: AWS::EC2::Volume Properties: Encrypted: true - KmsKeyId: MyStack:Key + KmsKeyId: !Ref MyKey Size: 100 bad: - |- diff --git a/checks/cloud/aws/ec2/no_default_vpc.yaml b/checks/cloud/aws/ec2/no_default_vpc.yaml index e29bccd4..e52e9168 100644 --- a/checks/cloud/aws/ec2/no_default_vpc.yaml +++ b/checks/cloud/aws/ec2/no_default_vpc.yaml @@ -2,6 +2,9 @@ terraform: good: - |- # no aws default vpc present + resource "aws_vpc" "main" { + cidr_block = "10.0.0.0/16" + } bad: - |- resource "aws_default_vpc" "default" { diff --git a/checks/cloud/aws/ec2/no_excessive_port_access.yaml b/checks/cloud/aws/ec2/no_excessive_port_access.yaml index 61326eee..4530c389 100644 --- a/checks/cloud/aws/ec2/no_excessive_port_access.yaml +++ b/checks/cloud/aws/ec2/no_excessive_port_access.yaml @@ -15,7 +15,7 @@ cloudformation: Rule: Type: AWS::EC2::NetworkAclEntry Properties: - NetworkAclId: null + NetworkAclId: !Ref NetworkACL Protocol: 6 Ref: NetworkACL RuleAction: allow @@ -35,7 +35,7 @@ cloudformation: Rule: Type: AWS::EC2::NetworkAclEntry Properties: - NetworkAclId: null + NetworkAclId: !Ref NetworkACL Protocol: -1 Ref: NetworkACL RuleAction: allow diff --git a/checks/cloud/aws/ec2/no_public_ingress_acl.yaml b/checks/cloud/aws/ec2/no_public_ingress_acl.yaml index 746fc877..f92494d1 100644 --- a/checks/cloud/aws/ec2/no_public_ingress_acl.yaml +++ b/checks/cloud/aws/ec2/no_public_ingress_acl.yaml @@ -18,6 +18,9 @@ cloudformation: NetworkAclId: !Ref NetworkACL Protocol: 6 RuleAction: allow + PortRange: + From: 22 + To: 22 bad: - |- AWSTemplateFormatVersion: "2010-09-09" @@ -37,6 +40,9 @@ cloudformation: NetworkAclId: !Ref NetworkACL Protocol: 6 RuleAction: allow + PortRange: + From: 22 + To: 22 terraform: good: - |- diff --git a/checks/cloud/aws/ec2/no_public_ingress_sgr.yaml b/checks/cloud/aws/ec2/no_public_ingress_sgr.yaml index 67f93e12..6153e4f9 100644 --- a/checks/cloud/aws/ec2/no_public_ingress_sgr.yaml +++ b/checks/cloud/aws/ec2/no_public_ingress_sgr.yaml @@ -9,6 +9,8 @@ cloudformation: SecurityGroupIngress: - CidrIp: 127.0.0.1/32 IpProtocol: "6" + FromPort: 22 + ToPort: 22 bad: - |- Resources: @@ -19,6 +21,8 @@ cloudformation: SecurityGroupIngress: - CidrIp: 0.0.0.0/0 IpProtocol: "6" + FromPort: 22 + ToPort: 22 terraform: good: - |- @@ -27,9 +31,9 @@ terraform: cidr_blocks = ["10.0.0.0/16"] } - |- - resource "aws_security_group_rule" "allow_partner_rsync" { + resource "aws_security_group_rule" "example" { type = "ingress" - security_group_id = aws_security_group.….id + security_group_id = "sg-123456" from_port = 22 to_port = 22 protocol = "tcp" @@ -42,5 +46,8 @@ terraform: - |- resource "aws_security_group_rule" "bad_example" { type = "ingress" + from_port = 22 + to_port = 22 + protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } diff --git a/checks/cloud/aws/ecs/no_plaintext_secrets.rego b/checks/cloud/aws/ecs/no_plaintext_secrets.rego index 6d2d3bf1..7192b748 100644 --- a/checks/cloud/aws/ecs/no_plaintext_secrets.rego +++ b/checks/cloud/aws/ecs/no_plaintext_secrets.rego @@ -37,7 +37,7 @@ import rego.v1 deny contains res if { some container in input.aws.ecs.taskdefinitions[_].containerdefinitions some env in container.environment - scan_result := squealer.scan_string(env.value) + scan_result := scan_env_value(env) scan_result.transgressionFound res := result.new( sprintf("Container definition contains a potentially sensitive in environment variable %q: %s", [env.name, scan_result.description]), @@ -45,6 +45,16 @@ deny contains res if { ) } +scan_env_value(env) := scan_result if { + is_string(env.value) + scan_result := squealer.scan_string(env.value) +} + +scan_env_value(env) := scan_result if { + not is_string(env.value) + scan_result := squealer.scan_string(env.value.value) +} + deny contains res if { some container in input.aws.ecs.taskdefinitions[_].containerdefinitions some env in container.environment @@ -56,12 +66,12 @@ deny contains res if { } is_sensitive_attr(attr) if { - attrl := lower(attr) + attrl := lower(attr.value) attrl in sensitive_attribute_tokens } is_sensitive_attr(attr) if { - attrl := lower(attr) + attrl := lower(attr.value) not attrl in sensitive_attribute_tokens some token in sensitive_attribute_tokens contains(attrl, token) diff --git a/checks/cloud/aws/ecs/no_plaintext_secrets.yaml b/checks/cloud/aws/ecs/no_plaintext_secrets.yaml index 663ffc85..9eb63e93 100644 --- a/checks/cloud/aws/ecs/no_plaintext_secrets.yaml +++ b/checks/cloud/aws/ecs/no_plaintext_secrets.yaml @@ -87,7 +87,7 @@ terraform: { "name": "my_service", "essential": true, - "memory": 256, + "memory": "256", "environment": [ { "name": "ENVIRONMENT", "value": "development" } ] @@ -104,7 +104,7 @@ terraform: { "name": "my_service", "essential": true, - "memory": 256, + "memory": "256", "environment": [ { "name": "ENVIRONMENT", "value": "development" }, { "name": "DATABASE_PASSWORD", "value": "oh no D:"} diff --git a/checks/cloud/aws/ecs/no_plaintext_secrets_test.rego b/checks/cloud/aws/ecs/no_plaintext_secrets_test.rego index 7d452262..32891ca2 100644 --- a/checks/cloud/aws/ecs/no_plaintext_secrets_test.rego +++ b/checks/cloud/aws/ecs/no_plaintext_secrets_test.rego @@ -8,12 +8,12 @@ import data.lib.test test_deny_definiton_with_plaintext_sensitive_information if { inp := {"aws": {"ecs": {"taskdefinitions": [{"containerdefinitions": [{"environment": [ { - "name": "ENVIRONMENT", - "value": "development", + "name": {"value": "ENVIRONMENT"}, + "value": {"value": "development"}, }, { - "name": "DATABASE_PASSWORD", - "value": "password123", + "name": {"value": "DATABASE_PASSWORD"}, + "value": {"value": "password123"}, }, ]}]}]}}} @@ -22,8 +22,8 @@ test_deny_definiton_with_plaintext_sensitive_information if { test_allow_task_without_sensitive_information if { inp := {"aws": {"ecs": {"taskdefinitions": [{"containerdefinitions": [{"environment": [{ - "Name": "ENVIRONMENT", - "Value": "development", + "name": {"value": "ENVIRONMENT"}, + "value": {"value": "development"}, }]}]}]}}} test.assert_empty(check.deny) with input as inp diff --git a/checks/cloud/aws/eks/encrypt_secrets.yaml b/checks/cloud/aws/eks/encrypt_secrets.yaml index cb85f505..24d03d72 100644 --- a/checks/cloud/aws/eks/encrypt_secrets.yaml +++ b/checks/cloud/aws/eks/encrypt_secrets.yaml @@ -6,10 +6,10 @@ cloudformation: Type: AWS::EKS::Cluster Properties: EncryptionConfig: - Provider: - KeyArn: alias/eks-kms - Resources: - - secrets + - Provider: + KeyArn: alias/eks-kms + Resources: + - secrets Name: goodExample ResourcesVpcConfig: SecurityGroupIds: @@ -37,11 +37,15 @@ cloudformation: terraform: good: - |- + resource "aws_kms_key" "eks" { + enable_key_rotation = true + } + resource "aws_eks_cluster" "good_example" { encryption_config { resources = ["secrets"] provider { - key_arn = var.kms_arn + key_arn = aws_kms_key.eks.arn } } diff --git a/checks/cloud/aws/iam/no_root_access_keys.yaml b/checks/cloud/aws/iam/no_root_access_keys.yaml index 7216b605..814e9924 100644 --- a/checks/cloud/aws/iam/no_root_access_keys.yaml +++ b/checks/cloud/aws/iam/no_root_access_keys.yaml @@ -1,11 +1,19 @@ terraform: good: - |- - resource "aws_iam_access_key" "good_example" { - user = "lowprivuser" + resource "aws_iam_user" "test" { + name = "lowprivuser" + } + + resource "aws_iam_access_key" "test" { + user = aws_iam_user.test.name } bad: - |- - resource "aws_iam_access_key" "good_example" { - user = "root" + resource "aws_iam_user" "test" { + name = "root" + } + + resource "aws_iam_access_key" "test" { + user = aws_iam_user.test.name } diff --git a/checks/cloud/aws/lambda/enable_tracing.rego b/checks/cloud/aws/lambda/enable_tracing.rego index 96e7bb09..5657e376 100644 --- a/checks/cloud/aws/lambda/enable_tracing.rego +++ b/checks/cloud/aws/lambda/enable_tracing.rego @@ -33,8 +33,16 @@ package builtin.aws.lambda.aws0066 import rego.v1 +import data.lib.cloud.metadata +import data.lib.cloud.value + deny contains res if { some func in input.aws.lambda.functions - func.tracing.mode.value != "Active" - res := result.new("Function does not have tracing enabled.", func.tracing.mode) + not is_active_mode(func) + res := result.new( + "Function does not have tracing enabled.", + metadata.obj_by_path(func, ["tracing", "mode"]), + ) } + +is_active_mode(func) if func.tracing.mode.value == "Active" diff --git a/checks/cloud/aws/lambda/enable_tracing.yaml b/checks/cloud/aws/lambda/enable_tracing.yaml index 2f0becae..ac52da95 100644 --- a/checks/cloud/aws/lambda/enable_tracing.yaml +++ b/checks/cloud/aws/lambda/enable_tracing.yaml @@ -9,17 +9,9 @@ cloudformation: S3Bucket: my-bucket S3Key: function.zip Handler: index.handler - Role: arn:aws:iam::123456789012:role/lambda-role Runtime: nodejs12.x - Timeout: 5 TracingConfig: Mode: Active - VpcConfig: - SecurityGroupIds: - - sg-085912345678492fb - SubnetIds: - - subnet-071f712345678e7c8 - - subnet-07fd123456788a036 bad: - |- Resources: @@ -30,15 +22,7 @@ cloudformation: S3Bucket: my-bucket S3Key: function.zip Handler: index.handler - Role: arn:aws:iam::123456789012:role/lambda-role Runtime: nodejs12.x - Timeout: 5 - VpcConfig: - SecurityGroupIds: - - sg-085912345678492fb - SubnetIds: - - subnet-071f712345678e7c8 - - subnet-07fd123456788a036 terraform: good: - |- diff --git a/checks/cloud/aws/redshift/no_classic_resources.yaml b/checks/cloud/aws/redshift/no_classic_resources.yaml index 73e93596..dbe53d5b 100644 --- a/checks/cloud/aws/redshift/no_classic_resources.yaml +++ b/checks/cloud/aws/redshift/no_classic_resources.yaml @@ -5,7 +5,11 @@ cloudformation: Description: Good example of redshift sgr - Resources: null + myCluster: + Type: AWS::Redshift::Cluster + + Properties: + DBName: mydb bad: - |- AWSTemplateFormatVersion: "2010-09-09" @@ -13,7 +17,7 @@ cloudformation: Description: Bad example of redshift sgr Resources: - Queue: + SecGroup: Type: AWS::Redshift::ClusterSecurityGroup Properties: Description: "" diff --git a/checks/cloud/aws/s3/enable_bucket_encryption.yaml b/checks/cloud/aws/s3/enable_bucket_encryption.yaml index 78187f70..59db0d1c 100644 --- a/checks/cloud/aws/s3/enable_bucket_encryption.yaml +++ b/checks/cloud/aws/s3/enable_bucket_encryption.yaml @@ -15,13 +15,6 @@ cloudformation: Resources: BadExample: Type: AWS::S3::Bucket - Properties: - BucketEncryption: - ServerSideEncryptionConfiguration: - - BucketKeyEnabled: false - ServerSideEncryptionByDefault: - KMSMasterKeyID: alias/alias-name - SSEAlgorithm: aws:kms terraform: good: - |- diff --git a/checks/cloud/aws/s3/enable_bucket_logging.yaml b/checks/cloud/aws/s3/enable_bucket_logging.yaml index 5bcf3bc7..1287f7e2 100644 --- a/checks/cloud/aws/s3/enable_bucket_logging.yaml +++ b/checks/cloud/aws/s3/enable_bucket_logging.yaml @@ -6,26 +6,13 @@ cloudformation: Type: AWS::S3::Bucket Properties: LoggingConfiguration: - DestinationBucketName: logging-bucket + DestinationBucketName: !Ref TestLoggingBucket LogFilePrefix: accesslogs/ - - |- - Resources: - GoodExample: + + TestLoggingBucket: Type: AWS::S3::Bucket Properties: - AccessControl: Private - BucketName: my-s3-bucket-${BucketSuffix} - LoggingConfiguration: - DestinationBucketName: - - EnvironmentMapping - - s3 - - logging - LogFilePrefix: s3-logs/AWSLogs/${AWS::AccountId}/my-s3-bucket-${BucketSuffix} - PublicAccessBlockConfiguration: - BlockPublicAcls: true - BlockPublicPolicy: true - IgnorePublicAcls: true - RestrictPublicBuckets: true + AccessControl: LogDeliveryWrite bad: - |- Resources: @@ -35,25 +22,43 @@ cloudformation: terraform: good: - |- - resource "aws_s3_bucket" "good_example" { + resource "aws_s3_bucket" "this" { + bucket = "test-bucket" logging { - target_bucket = "target-bucket" + target_bucket = aws_s3_bucket.log_bucket.id + target_prefix = "log/" } } - - |- - resource "aws_s3_bucket" "example" { - bucket = "yournamehere" - # ... other configuration ... + resource "aws_s3_bucket" "log_bucket" { + bucket = "test-log-bucket" } - resource "aws_s3_bucket_logging" "example" { - bucket = aws_s3_bucket.example.id + resource "aws_s3_bucket_acl" "log_bucket" { + acl = "log-delivery-write" + bucket = aws_s3_bucket.log_bucket.id + } + - |- + resource "aws_s3_bucket" "this" { + bucket = "test-bucket" + } + + resource "aws_s3_bucket_logging" "this" { + bucket = aws_s3_bucket.this.id target_bucket = aws_s3_bucket.log_bucket.id target_prefix = "log/" } + + resource "aws_s3_bucket" "log_bucket" { + bucket = "test-log-bucket" + } + + resource "aws_s3_bucket_acl" "log_bucket" { + acl = "log-delivery-write" + bucket = aws_s3_bucket.log_bucket.id + } bad: - |- - resource "aws_s3_bucket" "bad_example" { - + resource "aws_s3_bucket" "this" { + bucket = "test-bucket" } diff --git a/checks/cloud/aws/s3/no_public_access_with_acl.rego b/checks/cloud/aws/s3/no_public_access_with_acl.rego index 5590c0ed..4f1caea6 100644 --- a/checks/cloud/aws/s3/no_public_access_with_acl.rego +++ b/checks/cloud/aws/s3/no_public_access_with_acl.rego @@ -49,6 +49,8 @@ deny contains res if { some bucket in input.aws.s3.buckets s3.bucket_has_public_exposure_acl(bucket) bucket.acl.value != "authenticated-read" + + # TODO: check private? res := result.new( sprintf("Bucket has a public ACL: %q", [bucket.acl.value]), bucket.acl, diff --git a/checks/cloud/aws/s3/no_public_buckets.yaml b/checks/cloud/aws/s3/no_public_buckets.yaml index 2180a21f..772a65a0 100644 --- a/checks/cloud/aws/s3/no_public_buckets.yaml +++ b/checks/cloud/aws/s3/no_public_buckets.yaml @@ -14,10 +14,9 @@ cloudformation: - |- Resources: BadExample: + Type: AWS::S3::Bucket Properties: AccessControl: AuthenticatedRead - - Type: AWS::S3::Bucket terraform: good: - |- diff --git a/checks/cloud/aws/sam/api_use_secure_tls_policy.rego b/checks/cloud/aws/sam/api_use_secure_tls_policy.rego index 56520e57..4483fbaa 100644 --- a/checks/cloud/aws/sam/api_use_secure_tls_policy.rego +++ b/checks/cloud/aws/sam/api_use_secure_tls_policy.rego @@ -28,13 +28,15 @@ package builtin.aws.sam.aws0112 import rego.v1 +import data.lib.cloud.metadata + deny contains res if { some api in input.aws.sam.apis - not is_secure_tls_policy(api.domainconfiguration) + not is_secure_tls_policy(api) res := result.new( "Domain name is configured with an outdated TLS policy.", - api.domainconfiguration.securitypolicy, + metadata.obj_by_path(api, ["domainconfiguration", "securitypolicy"]), ) } -is_secure_tls_policy(domainconfiguration) if domainconfiguration.securitypolicy.value == "TLS_1_2" +is_secure_tls_policy(api) if api.domainconfiguration.securitypolicy.value == "TLS_1_2" diff --git a/checks/cloud/aws/sns/enable_topic_encryption.yaml b/checks/cloud/aws/sns/enable_topic_encryption.yaml index f9a762d2..43531fc6 100644 --- a/checks/cloud/aws/sns/enable_topic_encryption.yaml +++ b/checks/cloud/aws/sns/enable_topic_encryption.yaml @@ -3,7 +3,7 @@ cloudformation: - |- Resources: GoodTopic: - Type: AWS::SQS::Topic + Type: AWS::SNS::Topic Properties: KmsMasterKeyId: some-key TopicName: blah diff --git a/checks/cloud/azure/database/no_public_firewall_access.yaml b/checks/cloud/azure/database/no_public_firewall_access.yaml index 1f2f3990..384cdcc5 100644 --- a/checks/cloud/azure/database/no_public_firewall_access.yaml +++ b/checks/cloud/azure/database/no_public_firewall_access.yaml @@ -10,12 +10,18 @@ terraform: } bad: - |- - resource "azurerm_sql_firewall_rule" "bad_example" { - name = "bad_rule" - resource_group_name = azurerm_resource_group.example.name - server_name = azurerm_sql_server.example.name - start_ip_address = "0.0.0.0" - end_ip_address = "255.255.255.255" + resource "azurerm_resource_group" "example" { + name = "api-rg-pro" + location = "West Europe" + } + + resource "azurerm_postgresql_server" "example" { + name = "example-postgre-server" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + sku_name = "GP_Gen5_2" + version = "11" + ssl_enforcement_enabled = true } resource "azurerm_postgresql_firewall_rule" "bad_example" { @@ -25,3 +31,26 @@ terraform: start_ip_address = "0.0.0.0" end_ip_address = "255.255.255.255" } + - |- + resource "azurerm_sql_firewall_rule" "bad_example" { + name = "bad_rule" + resource_group_name = azurerm_resource_group.example.name + server_name = azurerm_sql_server.example.name + start_ip_address = "0.0.0.0" + end_ip_address = "255.255.255.255" + } + + + resource "azurerm_resource_group" "example" { + name = "example-resources" + location = "West Europe" + } + + resource "azurerm_sql_server" "example" { + name = "mysqlserver" + resource_group_name = azurerm_resource_group.example.name + location = azurerm_resource_group.example.location + version = "12.0" + administrator_login = "4dm1n157r470r" + administrator_login_password = "4-v3ry-53cr37-p455w0rd" + } diff --git a/checks/cloud/azure/keyvault/ensure_secret_expiry.yaml b/checks/cloud/azure/keyvault/ensure_secret_expiry.yaml index 45e0c411..25aa6cae 100644 --- a/checks/cloud/azure/keyvault/ensure_secret_expiry.yaml +++ b/checks/cloud/azure/keyvault/ensure_secret_expiry.yaml @@ -18,6 +18,7 @@ terraform: resource "azuread_application_password" "myapp" { application_object_id = azuread_application.myapp.object_id + end_date = "2024-12-18T00:00:00Z" } resource "azurerm_key_vault_secret" "myapp_pass" { diff --git a/checks/cloud/google/storage/no_public_access.yaml b/checks/cloud/google/storage/no_public_access.yaml index f80cfcf4..67e2a02e 100644 --- a/checks/cloud/google/storage/no_public_access.yaml +++ b/checks/cloud/google/storage/no_public_access.yaml @@ -1,8 +1,13 @@ terraform: good: - |- + resource "google_storage_bucket" "test" { + name = "test" + location = "US" + } + resource "google_storage_bucket_iam_binding" "binding" { - bucket = google_storage_bucket.default.name + bucket = google_storage_bucket.test.name role = "roles/storage.admin" members = [ "user:jane@example.com", @@ -10,8 +15,13 @@ terraform: } bad: - |- + resource "google_storage_bucket" "test" { + name = "test" + location = "US" + } + resource "google_storage_bucket_iam_binding" "binding" { - bucket = google_storage_bucket.default.name + bucket = google_storage_bucket.test.name role = "roles/storage.admin" members = [ "allAuthenticatedUsers", diff --git a/checks/cloud/nifcloud/computing/no_public_ingress_sgr.yaml b/checks/cloud/nifcloud/computing/no_public_ingress_sgr.yaml index 092aa869..beae1605 100644 --- a/checks/cloud/nifcloud/computing/no_public_ingress_sgr.yaml +++ b/checks/cloud/nifcloud/computing/no_public_ingress_sgr.yaml @@ -1,14 +1,14 @@ terraform: good: - |- - resource "nifcloud_security_group_rule" "good_example" { - type = "IN" - cidr_ip = "10.0.0.0/16" + resource "nifcloud_security_group_rule" "example" { + group_name = "allowtcp" + availability_zone = "east-11" } - |- - resource "nifcloud_security_group_rule" "allow_partner_rsync" { + resource "nifcloud_security_group_rule" "example" { type = "IN" - security_group_names = [nifcloud_security_group.….group_name] + security_group_names = [nifcloud_security_group.example.group_name] from_port = 22 to_port = 22 protocol = "TCP" diff --git a/checks/kubernetes/network/no_public_egress.yaml b/checks/kubernetes/network/no_public_egress.yaml index af3a1373..ffd77667 100644 --- a/checks/kubernetes/network/no_public_egress.yaml +++ b/checks/kubernetes/network/no_public_egress.yaml @@ -123,4 +123,3 @@ terraform: policy_types = ["Ingress", "Egress"] } } - diff --git a/checks/kubernetes/network/no_public_ingress.yaml b/checks/kubernetes/network/no_public_ingress.yaml index 607ad63a..6c2542d5 100644 --- a/checks/kubernetes/network/no_public_ingress.yaml +++ b/checks/kubernetes/network/no_public_ingress.yaml @@ -61,7 +61,6 @@ terraform: policy_types = ["Ingress", "Egress"] } } - bad: - |- resource "kubernetes_network_policy" "bad_example" { diff --git a/go.mod b/go.mod index c272ab62..77b61f7c 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.22.9 toolchain go1.23.0 require ( - github.com/aquasecurity/trivy v0.57.1-0.20241127185709-c238c515b831 + github.com/aquasecurity/trivy v0.57.1-0.20241202232542-54130dcc1d77 github.com/aws-cloudformation/rain v1.19.0 github.com/hashicorp/hcl/v2 v2.23.0 github.com/liamg/iamgo v0.0.9 diff --git a/go.sum b/go.sum index d0105d97..9bd6c626 100644 --- a/go.sum +++ b/go.sum @@ -351,6 +351,8 @@ github.com/aquasecurity/tml v0.6.1 h1:y2ZlGSfrhnn7t4ZJ/0rotuH+v5Jgv6BDDO5jB6A9gw github.com/aquasecurity/tml v0.6.1/go.mod h1:OnYMWY5lvI9ejU7yH9LCberWaaTBW7hBFsITiIMY2yY= github.com/aquasecurity/trivy v0.57.1-0.20241127185709-c238c515b831 h1:Ol9LT6V3KXCwaJE6lyeOR+3NGgDyA0HOXvPtumz/dxA= github.com/aquasecurity/trivy v0.57.1-0.20241127185709-c238c515b831/go.mod h1:fURPZjqUDH08tYy/2EhU4k0uAOzXcPAJeM2O0Z6k0nU= +github.com/aquasecurity/trivy v0.57.1-0.20241202232542-54130dcc1d77 h1:asWezOVucyj/9U+XUYgp/T952z1rpS1o1Kd+KyZD1C0= +github.com/aquasecurity/trivy v0.57.1-0.20241202232542-54130dcc1d77/go.mod h1:ZFPGXENLDMCKV7uXY3G1dloqMki9SZBHZldFo2aqupA= github.com/aquasecurity/trivy-db v0.0.0-20241120092622-333d808d7e45 h1:ljinbg7JTQvdnzuRsPYS6btA51SyGYWKCQInxSIwbRw= github.com/aquasecurity/trivy-db v0.0.0-20241120092622-333d808d7e45/go.mod h1:Lg2avQhFy5qeGA0eMysI/61REVvWpEltverCarGc3l0= github.com/aquasecurity/trivy-java-db v0.0.0-20240109071736-184bd7481d48 h1:JVgBIuIYbwG+ekC5lUHUpGJboPYiCcxiz06RCtz8neI= diff --git a/integration/check_examples_test.go b/integration/check_examples_test.go new file mode 100644 index 00000000..f92141a2 --- /dev/null +++ b/integration/check_examples_test.go @@ -0,0 +1,165 @@ +//go:build integration + +package integration + +import ( + "io/fs" + "os" + "os/exec" + "path/filepath" + "strconv" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/aquasecurity/trivy-checks/internal/examples" + "github.com/aquasecurity/trivy/pkg/iac/framework" + "github.com/aquasecurity/trivy/pkg/iac/rego" + "github.com/aquasecurity/trivy/pkg/iac/rules" + "github.com/aquasecurity/trivy/pkg/types" +) + +func TestValidateCheckExamples(t *testing.T) { + cacheDir := setupCache(t) + targetDir := setupTarget(t) + outputFile := filepath.Join(t.TempDir(), "report.json") + + args := []string{ + "conf", + "--skip-check-update", + "--quiet", + "--format", "json", + "--output", outputFile, + "--cache-dir", cacheDir, + targetDir, + } + runTrivy(t, args) + + report := readTrivyReport(t, outputFile) + + verifyExamples(t, report, targetDir) +} + +func setupCache(t *testing.T) string { + t.Helper() + + cmd := exec.Command("make", "create-bundle") + cmd.Dir = ".." + require.NoError(t, cmd.Run()) + defer os.Remove("../bundle.tar.gz") + + cacheDir := t.TempDir() + + policyDir := filepath.Join(cacheDir, "policy", "content") + require.NoError(t, os.MkdirAll(policyDir, os.ModePerm)) + + cmd = exec.Command("tar", "-zxf", "bundle.tar.gz", "-C", policyDir) + cmd.Dir = ".." + require.NoError(t, cmd.Run()) + + return cacheDir +} + +func setupTarget(t *testing.T) string { + t.Helper() + + targetDir := t.TempDir() + + // TODO(nikpivkin): load examples from fs + rego.LoadAndRegister() + + for _, r := range rules.GetRegistered(framework.ALL) { + if _, ok := r.Frameworks[framework.Default]; !ok { + // TODO(nikpivkin): Trivy does not load non default checks + continue + } + + examples, path, err := examples.GetCheckExamples(r.Rule) + require.NoError(t, err) + + if path == "" { + continue + } + + for provider, providerExamples := range examples { + writeExamples(t, providerExamples.Bad.ToStrings(), provider, targetDir, r.AVDID, "bad") + writeExamples(t, providerExamples.Good.ToStrings(), provider, targetDir, r.AVDID, "good") + } + } + + return targetDir +} + +func writeExamples(t *testing.T, examples []string, provider, cacheDir string, id string, typ string) { + for i, example := range examples { + name := "test" + extensionByProvider(provider) + file := filepath.Join(cacheDir, id, provider, typ, strconv.Itoa(i), name) + require.NoError(t, os.MkdirAll(filepath.Dir(file), fs.ModePerm)) + require.NoError(t, os.WriteFile(file, []byte(example), fs.ModePerm)) + } +} + +func verifyExamples(t *testing.T, report types.Report, targetDir string) { + got := getFailureIDs(report) + + err := filepath.Walk(targetDir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() { + return nil + } + + relPath, err := filepath.Rel(targetDir, path) + require.NoError(t, err) + + parts := strings.Split(relPath, string(os.PathSeparator)) + require.Len(t, parts, 5) + + id, _, exampleType := parts[0], parts[1], parts[2] + + shouldBePresent := exampleType == "bad" + + t.Run(relPath, func(t *testing.T) { + if shouldBePresent { + ids, exists := got[relPath] + assert.True(t, exists) + assert.Contains(t, ids, id) + } else { + ids, exists := got[relPath] + if exists { + assert.NotContains(t, ids, id) + } + } + }) + return nil + }) + + require.NoError(t, err) +} + +func getFailureIDs(report types.Report) map[string][]string { + ids := make(map[string][]string) + + for _, result := range report.Results { + for _, misconf := range result.Misconfigurations { + if misconf.Status == types.MisconfStatusFailure { + ids[result.Target] = append(ids[result.Target], misconf.AVDID) + } + } + } + + return ids +} + +func extensionByProvider(provider string) string { + switch provider { + case "terraform": + return ".tf" + case "cloudformation": + return ".yaml" + } + panic("unreachable") +} diff --git a/integration/examples_test.go b/integration/examples_test.go index b5ebd2e9..d3569b59 100644 --- a/integration/examples_test.go +++ b/integration/examples_test.go @@ -1,18 +1,14 @@ -package test +//go:build integration + +package integration import ( - "context" - "encoding/json" - "io" - "os" "path/filepath" "testing" - "github.com/spf13/viper" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/aquasecurity/trivy/pkg/commands" "github.com/aquasecurity/trivy/pkg/types" ) @@ -79,8 +75,6 @@ func TestExamples(t *testing.T) { for _, tt := range tests { t.Run(tt.dir, func(t *testing.T) { - t.Cleanup(viper.Reset) - targetDir := filepath.Join(workDir, tt.dir) outputFile := filepath.Join(t.TempDir(), "report.json") @@ -100,17 +94,7 @@ func TestExamples(t *testing.T) { trivyArgs = append(trivyArgs, args...) runTrivy(t, trivyArgs) - out, err := os.ReadFile(outputFile) - require.NoError(t, err) - - var rep types.Report - require.NoError(t, json.Unmarshal(out, &rep)) - - defer func() { - if t.Failed() { - t.Log(string(out)) - } - }() + rep := readTrivyReport(t, outputFile) results := filterResults(rep.Results) @@ -143,14 +127,3 @@ func collectFailures(misconfs []types.DetectedMisconfiguration) []types.Detected } return fails } - -func runTrivy(t *testing.T, args []string) { - t.Helper() - - app := commands.NewApp() - app.SetOut(io.Discard) - app.SetArgs(args) - - err := app.ExecuteContext(context.TODO()) - require.NoError(t, err) -} diff --git a/integration/integration_test.go b/integration/integration_test.go new file mode 100644 index 00000000..c416efe2 --- /dev/null +++ b/integration/integration_test.go @@ -0,0 +1,41 @@ +//go:build integration + +package integration + +import ( + "context" + "encoding/json" + "io" + "os" + "testing" + + "github.com/spf13/viper" + "github.com/stretchr/testify/require" + + "github.com/aquasecurity/trivy/pkg/commands" + "github.com/aquasecurity/trivy/pkg/types" +) + +func runTrivy(t *testing.T, args []string) { + defer viper.Reset() + + t.Helper() + + app := commands.NewApp() + app.SetOut(io.Discard) + app.SetArgs(args) + + err := app.ExecuteContext(context.TODO()) + require.NoError(t, err) +} + +func readTrivyReport(t *testing.T, outputFile string) types.Report { + t.Helper() + + out, err := os.ReadFile(outputFile) + require.NoError(t, err) + + var report types.Report + require.NoError(t, json.Unmarshal(out, &report)) + return report +} diff --git a/test/rego/aws_ecs_test.go b/test/rego/aws_ecs_test.go index ad9ebf2a..9b9aaa2b 100644 --- a/test/rego/aws_ecs_test.go +++ b/test/rego/aws_ecs_test.go @@ -134,8 +134,8 @@ var awsEcsTestCases = testCases{ Essential: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), Environment: []ecs.EnvVar{ { - Name: "ENVIRONMENT", - Value: "development", + Name: trivyTypes.StringTest("ENVIRONMENT"), + Value: trivyTypes.StringTest("development"), }, }, },