diff --git a/avd_docs/google/compute/AVD-GCP-0027/docs.md b/avd_docs/google/compute/AVD-GCP-0027/docs.md index 5c1fdab0..62a5af84 100644 --- a/avd_docs/google/compute/AVD-GCP-0027/docs.md +++ b/avd_docs/google/compute/AVD-GCP-0027/docs.md @@ -1,10 +1,10 @@ Network security rules should not use very broad subnets. - Where possible, segments should be broken into smaller subnets and avoid using the /0 subnet. + ### Impact -The port is exposed for ingress from the internet + {{ remediationActions }} diff --git a/avd_docs/google/compute/AVD-GCP-0029/docs.md b/avd_docs/google/compute/AVD-GCP-0029/docs.md index 082212e3..058f790f 100644 --- a/avd_docs/google/compute/AVD-GCP-0029/docs.md +++ b/avd_docs/google/compute/AVD-GCP-0029/docs.md @@ -1,8 +1,9 @@ VPC flow logs record information about all traffic, which is a vital tool in reviewing anomalous traffic. + ### Impact -Limited auditing capability and awareness + {{ remediationActions }} diff --git a/avd_docs/google/compute/AVD-GCP-0030/docs.md b/avd_docs/google/compute/AVD-GCP-0030/docs.md index 00fbdf3a..a18c67ae 100644 --- a/avd_docs/google/compute/AVD-GCP-0030/docs.md +++ b/avd_docs/google/compute/AVD-GCP-0030/docs.md @@ -1,8 +1,9 @@ Use of project-wide SSH keys means that a compromise of any one of these key pairs can result in all instances being compromised. It is recommended to use instance-level keys. + ### Impact -Compromise of a single key pair compromises all instances + {{ remediationActions }} diff --git a/avd_docs/google/compute/AVD-GCP-0031/docs.md b/avd_docs/google/compute/AVD-GCP-0031/docs.md index 673dafc3..165f84a8 100644 --- a/avd_docs/google/compute/AVD-GCP-0031/docs.md +++ b/avd_docs/google/compute/AVD-GCP-0031/docs.md @@ -1,8 +1,9 @@ Instances should not be publicly exposed to the internet + ### Impact -Direct exposure of an instance to the public internet + {{ remediationActions }} diff --git a/avd_docs/google/compute/AVD-GCP-0032/docs.md b/avd_docs/google/compute/AVD-GCP-0032/docs.md index 8bb9a13c..140d959b 100644 --- a/avd_docs/google/compute/AVD-GCP-0032/docs.md +++ b/avd_docs/google/compute/AVD-GCP-0032/docs.md @@ -1,8 +1,9 @@ When serial port access is enabled, the access is not governed by network security rules meaning the port can be exposed publicly. + ### Impact -Unrestricted network access to the serial console of the instance + {{ remediationActions }} diff --git a/avd_docs/google/compute/AVD-GCP-0033/Terraform.md b/avd_docs/google/compute/AVD-GCP-0033/Terraform.md index 686fe629..8a15b33e 100644 --- a/avd_docs/google/compute/AVD-GCP-0033/Terraform.md +++ b/avd_docs/google/compute/AVD-GCP-0033/Terraform.md @@ -1,5 +1,5 @@ -Use managed keys +Use managed keys ```hcl resource "google_service_account" "default" { diff --git a/avd_docs/google/compute/AVD-GCP-0033/docs.md b/avd_docs/google/compute/AVD-GCP-0033/docs.md index 85cfbc53..01ccf571 100644 --- a/avd_docs/google/compute/AVD-GCP-0033/docs.md +++ b/avd_docs/google/compute/AVD-GCP-0033/docs.md @@ -1,8 +1,9 @@ Using unmanaged keys makes rotation and general management difficult. + ### Impact -Using unmanaged keys does not allow for proper management + {{ remediationActions }} diff --git a/avd_docs/google/compute/AVD-GCP-0034/docs.md b/avd_docs/google/compute/AVD-GCP-0034/docs.md index 45bf9f5d..01ccf571 100644 --- a/avd_docs/google/compute/AVD-GCP-0034/docs.md +++ b/avd_docs/google/compute/AVD-GCP-0034/docs.md @@ -1,8 +1,9 @@ Using unmanaged keys makes rotation and general management difficult. + ### Impact -Using unmanaged keys does not allow for proper key management. + {{ remediationActions }} diff --git a/avd_docs/google/compute/AVD-GCP-0035/docs.md b/avd_docs/google/compute/AVD-GCP-0035/docs.md index de39ac9b..62a5af84 100644 --- a/avd_docs/google/compute/AVD-GCP-0035/docs.md +++ b/avd_docs/google/compute/AVD-GCP-0035/docs.md @@ -1,10 +1,10 @@ Network security rules should not use very broad subnets. - Where possible, segments should be broken into smaller subnets and avoid using the /0 subnet. + ### Impact -The port is exposed for egress to the internet + {{ remediationActions }} diff --git a/avd_docs/google/compute/AVD-GCP-0036/docs.md b/avd_docs/google/compute/AVD-GCP-0036/docs.md index ef1c3093..a2db8c01 100644 --- a/avd_docs/google/compute/AVD-GCP-0036/docs.md +++ b/avd_docs/google/compute/AVD-GCP-0036/docs.md @@ -1,8 +1,9 @@ OS Login automatically revokes the relevant SSH keys when an IAM user has their access revoked. + ### Impact -Access via SSH key cannot be revoked automatically when an IAM user is removed. + {{ remediationActions }} diff --git a/avd_docs/google/compute/AVD-GCP-0037/docs.md b/avd_docs/google/compute/AVD-GCP-0037/docs.md index ad6faf1d..badad856 100644 --- a/avd_docs/google/compute/AVD-GCP-0037/docs.md +++ b/avd_docs/google/compute/AVD-GCP-0037/docs.md @@ -1,8 +1,9 @@ Sensitive values such as raw encryption keys should not be included in your Terraform code, and should be stored securely by a secrets manager. + ### Impact -The encryption key should be considered compromised as it is not stored securely. + {{ remediationActions }} diff --git a/avd_docs/google/compute/AVD-GCP-0039/docs.md b/avd_docs/google/compute/AVD-GCP-0039/docs.md index cc52743d..5378192c 100644 --- a/avd_docs/google/compute/AVD-GCP-0039/docs.md +++ b/avd_docs/google/compute/AVD-GCP-0039/docs.md @@ -1,8 +1,9 @@ TLS versions prior to 1.2 are outdated and insecure. You should use 1.2 as aminimum version. + ### Impact -Data in transit is not sufficiently secured + {{ remediationActions }} diff --git a/avd_docs/google/compute/AVD-GCP-0041/docs.md b/avd_docs/google/compute/AVD-GCP-0041/docs.md index b3523ef9..2eb73faa 100644 --- a/avd_docs/google/compute/AVD-GCP-0041/docs.md +++ b/avd_docs/google/compute/AVD-GCP-0041/docs.md @@ -1,8 +1,9 @@ The virtual TPM provides numerous security measures to your VM. + ### Impact -Unable to prevent unwanted system state modification + {{ remediationActions }} diff --git a/avd_docs/google/compute/AVD-GCP-0042/docs.md b/avd_docs/google/compute/AVD-GCP-0042/docs.md index ef1c3093..a2db8c01 100644 --- a/avd_docs/google/compute/AVD-GCP-0042/docs.md +++ b/avd_docs/google/compute/AVD-GCP-0042/docs.md @@ -1,8 +1,9 @@ OS Login automatically revokes the relevant SSH keys when an IAM user has their access revoked. + ### Impact -Access via SSH key cannot be revoked automatically when an IAM user is removed. + {{ remediationActions }} diff --git a/avd_docs/google/compute/AVD-GCP-0043/docs.md b/avd_docs/google/compute/AVD-GCP-0043/docs.md index b64da5dc..60578172 100644 --- a/avd_docs/google/compute/AVD-GCP-0043/docs.md +++ b/avd_docs/google/compute/AVD-GCP-0043/docs.md @@ -1,8 +1,9 @@ Disabling IP forwarding ensures the instance can only receive packets addressed to the instance and can only send packets with a source address of the instance. + ### Impact -Instance can send/receive packets without the explicit instance address + {{ remediationActions }} diff --git a/avd_docs/google/compute/AVD-GCP-0044/docs.md b/avd_docs/google/compute/AVD-GCP-0044/docs.md index 9b013753..cae97f2c 100644 --- a/avd_docs/google/compute/AVD-GCP-0044/docs.md +++ b/avd_docs/google/compute/AVD-GCP-0044/docs.md @@ -1,8 +1,9 @@ The default service account has full project access. Instances should instead be assigned the minimal access they need. + ### Impact -Instance has full access to the project + {{ remediationActions }} diff --git a/avd_docs/google/compute/AVD-GCP-0045/docs.md b/avd_docs/google/compute/AVD-GCP-0045/docs.md index 80ab9276..4bbaa795 100644 --- a/avd_docs/google/compute/AVD-GCP-0045/docs.md +++ b/avd_docs/google/compute/AVD-GCP-0045/docs.md @@ -1,8 +1,9 @@ Integrity monitoring helps you understand and make decisions about the state of your VM instances. + ### Impact -No visibility of VM instance boot state. + {{ remediationActions }} diff --git a/avd_docs/google/compute/AVD-GCP-0067/docs.md b/avd_docs/google/compute/AVD-GCP-0067/docs.md index 0f5d76fb..ff531130 100644 --- a/avd_docs/google/compute/AVD-GCP-0067/docs.md +++ b/avd_docs/google/compute/AVD-GCP-0067/docs.md @@ -1,8 +1,9 @@ Secure boot helps ensure that the system only runs authentic software. + ### Impact -Unable to verify digital signature of boot components, and unable to stop the boot process if verification fails. + {{ remediationActions }} diff --git a/checks/cloud/google/compute/disk_encryption_customer_key.go b/checks/cloud/google/compute/disk_encryption_customer_key.go index 650d7438..cd84f570 100755 --- a/checks/cloud/google/compute/disk_encryption_customer_key.go +++ b/checks/cloud/google/compute/disk_encryption_customer_key.go @@ -25,7 +25,8 @@ var CheckDiskEncryptionCustomerKey = rules.Register( Links: terraformDiskEncryptionCustomerKeyLinks, RemediationMarkdown: terraformDiskEncryptionCustomerKeyRemediationMarkdown, }, - Severity: severity.Low, + Severity: severity.Low, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, disk := range s.Google.Compute.Disks { diff --git a/checks/cloud/google/compute/disk_encryption_customer_key.rego b/checks/cloud/google/compute/disk_encryption_customer_key.rego new file mode 100644 index 00000000..82ef98b1 --- /dev/null +++ b/checks/cloud/google/compute/disk_encryption_customer_key.rego @@ -0,0 +1,40 @@ +# METADATA +# title: Disks should be encrypted with customer managed encryption keys +# description: | +# Using unmanaged keys makes rotation and general management difficult. +# scope: package +# schemas: +# - input: schema["cloud"] +# custom: +# id: AVD-GCP-0034 +# avd_id: AVD-GCP-0034 +# provider: google +# service: compute +# severity: LOW +# short_code: disk-encryption-customer-key +# recommended_action: Use managed keys to encrypt disks. +# input: +# selector: +# - type: cloud +# subtypes: +# - service: compute +# provider: google +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_disk#kms_key_self_link +# good_examples: checks/cloud/google/compute/disk_encryption_customer_key.tf.go +# bad_examples: checks/cloud/google/compute/disk_encryption_customer_key.tf.go +package builtin.google.compute.google0034 + +import rego.v1 + +deny contains res if { + some disk in input.google.compute.disks + not is_disk_encrypted(disk) + res := result.new( + "Disk is not encrypted with a customer managed key.", + object.get(disk, ["encryption", "kmskeylink"], disk), + ) +} + +is_disk_encrypted(disk) := disk.encryption.kmskeylink.value != "" diff --git a/checks/cloud/google/compute/disk_encryption_customer_key_test.go b/checks/cloud/google/compute/disk_encryption_customer_key_test.go deleted file mode 100644 index de3675c8..00000000 --- a/checks/cloud/google/compute/disk_encryption_customer_key_test.go +++ /dev/null @@ -1,71 +0,0 @@ -package compute - -import ( - "testing" - - trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/google/compute" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/stretchr/testify/assert" -) - -func TestCheckDiskEncryptionCustomerKey(t *testing.T) { - tests := []struct { - name string - input compute.Compute - expected bool - }{ - { - name: "Disk missing KMS key link", - input: compute.Compute{ - Disks: []compute.Disk{ - { - Metadata: trivyTypes.NewTestMetadata(), - Encryption: compute.DiskEncryption{ - Metadata: trivyTypes.NewTestMetadata(), - KMSKeyLink: trivyTypes.String("", trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: true, - }, - { - name: "Disk with KMS key link provided", - input: compute.Compute{ - Disks: []compute.Disk{ - { - Metadata: trivyTypes.NewTestMetadata(), - Encryption: compute.DiskEncryption{ - Metadata: trivyTypes.NewTestMetadata(), - KMSKeyLink: trivyTypes.String("kms-key-link", trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.Google.Compute = test.input - results := CheckDiskEncryptionCustomerKey.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckDiskEncryptionCustomerKey.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/google/compute/disk_encryption_customer_key_test.rego b/checks/cloud/google/compute/disk_encryption_customer_key_test.rego new file mode 100644 index 00000000..cebc1611 --- /dev/null +++ b/checks/cloud/google/compute/disk_encryption_customer_key_test.rego @@ -0,0 +1,24 @@ +package builtin.google.compute.google0034_test + +import rego.v1 + +import data.builtin.google.compute.google0034 as check +import data.lib.test + +test_deny_disk_is_not_encrypted if { + inp := {"google": {"compute": {"disks": [{"encryption": {"kmskeylink": {"value": ""}}}]}}} + res := check.deny with input as inp + count(res) == 1 +} + +test_deny_disk_encryption_is_not_specified if { + inp := {"google": {"compute": {"disks": [{}]}}} + res := check.deny with input as inp + count(res) == 1 +} + +test_allow_disk_is_encrypted if { + inp := {"google": {"compute": {"disks": [{"encryption": {"kmskeylink": {"value": "something"}}}]}}} + res := check.deny with input as inp + res == set() +} diff --git a/checks/cloud/google/compute/disk_encryption_no_plaintext_key.go b/checks/cloud/google/compute/disk_encryption_no_plaintext_key.go index 8ca3f038..695656d6 100755 --- a/checks/cloud/google/compute/disk_encryption_no_plaintext_key.go +++ b/checks/cloud/google/compute/disk_encryption_no_plaintext_key.go @@ -27,7 +27,8 @@ var CheckDiskEncryptionRequired = rules.Register( Links: terraformDiskEncryptionNoPlaintextKeyLinks, RemediationMarkdown: terraformDiskEncryptionNoPlaintextKeyRemediationMarkdown, }, - Severity: severity.Critical, + Severity: severity.Critical, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, instance := range s.Google.Compute.Instances { diff --git a/checks/cloud/google/compute/disk_encryption_no_plaintext_key.rego b/checks/cloud/google/compute/disk_encryption_no_plaintext_key.rego new file mode 100644 index 00000000..9fac519a --- /dev/null +++ b/checks/cloud/google/compute/disk_encryption_no_plaintext_key.rego @@ -0,0 +1,57 @@ +# METADATA +# title: The encryption key used to encrypt a compute disk has been specified in plaintext. +# description: | +# Sensitive values such as raw encryption keys should not be included in your Terraform code, and should be stored securely by a secrets manager. +# scope: package +# schemas: +# - input: schema["cloud"] +# related_resources: +# - https://cloud.google.com/compute/docs/disks/customer-supplied-encryption +# custom: +# id: AVD-GCP-0037 +# avd_id: AVD-GCP-0037 +# provider: google +# service: compute +# severity: CRITICAL +# short_code: disk-encryption-no-plaintext-key +# recommended_action: Reference a managed key rather than include the key in raw format. +# input: +# selector: +# - type: cloud +# subtypes: +# - service: compute +# provider: google +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_disk#kms_key_self_link +# good_examples: checks/cloud/google/compute/disk_encryption_no_plaintext_key.tf.go +# bad_examples: checks/cloud/google/compute/disk_encryption_no_plaintext_key.tf.go +package builtin.google.compute.google0037 + +import rego.v1 + +deny contains res if { + some instance in input.google.compute.instances + disks := array.concat( + object.get(instance, "bootdisks", []), + object.get(instance, "attacheddisks", []), + ) + + some disk in disks + encryption_key_has_plaintext(disk) + res := result.new( + "Instance disk has encryption key provided in plaintext.", + disk.encryption.rawkey, + ) +} + +deny contains res if { + some disk in input.google.compute.disks + encryption_key_has_plaintext(disk) + res := result.new( + "Disk encryption key is supplied in plaintext.", + disk.encryption.rawkey, + ) +} + +encryption_key_has_plaintext(disk) := count(disk.encryption.rawkey.value) > 0 diff --git a/checks/cloud/google/compute/disk_encryption_no_plaintext_key_test.go b/checks/cloud/google/compute/disk_encryption_no_plaintext_key_test.go deleted file mode 100644 index 89fa43bb..00000000 --- a/checks/cloud/google/compute/disk_encryption_no_plaintext_key_test.go +++ /dev/null @@ -1,114 +0,0 @@ -package compute - -import ( - "testing" - - trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/google/compute" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/stretchr/testify/assert" -) - -func TestCheckDiskEncryptionRequired(t *testing.T) { - tests := []struct { - name string - input compute.Compute - expected bool - }{ - { - name: "Disk with plaintext encryption key", - input: compute.Compute{ - Disks: []compute.Disk{ - { - Metadata: trivyTypes.NewTestMetadata(), - Encryption: compute.DiskEncryption{ - Metadata: trivyTypes.NewTestMetadata(), - RawKey: trivyTypes.Bytes([]byte("b2ggbm8gdGhpcyBpcyBiYWQ"), trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: true, - }, - { - name: "Instance disk with plaintext encryption key", - input: compute.Compute{ - Instances: []compute.Instance{ - { - Metadata: trivyTypes.NewTestMetadata(), - BootDisks: []compute.Disk{ - { - Metadata: trivyTypes.NewTestMetadata(), - Encryption: compute.DiskEncryption{ - Metadata: trivyTypes.NewTestMetadata(), - RawKey: trivyTypes.Bytes([]byte("b2ggbm8gdGhpcyBpcyBiYWQ"), trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - }, - }, - expected: true, - }, - { - name: "Disks with no plaintext encryption keys", - input: compute.Compute{ - Disks: []compute.Disk{ - { - Metadata: trivyTypes.NewTestMetadata(), - Encryption: compute.DiskEncryption{ - Metadata: trivyTypes.NewTestMetadata(), - RawKey: trivyTypes.Bytes([]byte(""), trivyTypes.NewTestMetadata()), - }, - }, - }, - Instances: []compute.Instance{ - { - Metadata: trivyTypes.NewTestMetadata(), - BootDisks: []compute.Disk{ - { - Metadata: trivyTypes.NewTestMetadata(), - Encryption: compute.DiskEncryption{ - Metadata: trivyTypes.NewTestMetadata(), - RawKey: trivyTypes.Bytes([]byte(""), trivyTypes.NewTestMetadata()), - }, - }, - }, - AttachedDisks: []compute.Disk{ - { - Metadata: trivyTypes.NewTestMetadata(), - Encryption: compute.DiskEncryption{ - Metadata: trivyTypes.NewTestMetadata(), - RawKey: trivyTypes.Bytes([]byte(""), trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - }, - }, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.Google.Compute = test.input - results := CheckDiskEncryptionRequired.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckDiskEncryptionRequired.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/google/compute/disk_encryption_no_plaintext_key_test.rego b/checks/cloud/google/compute/disk_encryption_no_plaintext_key_test.rego new file mode 100644 index 00000000..300c0800 --- /dev/null +++ b/checks/cloud/google/compute/disk_encryption_no_plaintext_key_test.rego @@ -0,0 +1,40 @@ +package builtin.google.compute.google0037_test + +import rego.v1 + +import data.builtin.google.compute.google0037 as check +import data.lib.test + +test_deny_disk_with_plaintext_encryption_key if { + inp := {"google": {"compute": {"disks": [{"encryption": {"rawkey": {"value": "b2ggbm8gdGhpcyBpcyBiYWQ"}}}]}}} + + res := check.deny with input as inp + count(res) == 1 +} + +test_deny_instance_boot_disk_with_plaintext_encryption_key if { + inp := {"google": {"compute": {"instances": [{"bootdisks": [{"encryption": {"rawkey": {"value": "b2ggbm8gdGhpcyBpcyBiYWQ"}}}]}]}}} + + res := check.deny with input as inp + count(res) == 1 +} + +test_deny_instance_attached_disk_with_plaintext_encryption_key if { + inp := {"google": {"compute": {"instances": [{"attacheddisks": [{"encryption": {"rawkey": {"value": "b2ggbm8gdGhpcyBpcyBiYWQ"}}}]}]}}} + + res := check.deny with input as inp + count(res) == 1 +} + +test_allow_disks_without_plaintext_encryption_key if { + inp := {"google": {"compute": { + "disks": [{"encryption": {"rawkey": {"value": ""}}}], + "instances": [{ + "bootdisks": [{"encryption": {"rawkey": {"value": ""}}}], + "attacheddisks": [{"encryption": {"rawkey": {"value": ""}}}], + }], + }}} + + res := check.deny with input as inp + res == set() +} diff --git a/checks/cloud/google/compute/enable_shielded_vm_im.go b/checks/cloud/google/compute/enable_shielded_vm_im.go index 4a6a058d..cf0ff0b1 100755 --- a/checks/cloud/google/compute/enable_shielded_vm_im.go +++ b/checks/cloud/google/compute/enable_shielded_vm_im.go @@ -27,7 +27,8 @@ var CheckEnableShieldedVMIntegrityMonitoring = rules.Register( Links: terraformEnableShieldedVmImLinks, RemediationMarkdown: terraformEnableShieldedVmImRemediationMarkdown, }, - Severity: severity.Medium, + Severity: severity.Medium, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, instance := range s.Google.Compute.Instances { diff --git a/checks/cloud/google/compute/enable_shielded_vm_im.rego b/checks/cloud/google/compute/enable_shielded_vm_im.rego new file mode 100644 index 00000000..c017f5e4 --- /dev/null +++ b/checks/cloud/google/compute/enable_shielded_vm_im.rego @@ -0,0 +1,37 @@ +# METADATA +# title: Instances should have Shielded VM integrity monitoring enabled +# description: | +# Integrity monitoring helps you understand and make decisions about the state of your VM instances. +# scope: package +# schemas: +# - input: schema["cloud"] +# related_resources: +# - https://cloud.google.com/security/shielded-cloud/shielded-vm#integrity-monitoring +# custom: +# id: AVD-GCP-0045 +# avd_id: AVD-GCP-0045 +# provider: google +# service: compute +# severity: MEDIUM +# short_code: enable-shielded-vm-im +# recommended_action: Enable Shielded VM Integrity Monitoring +# input: +# selector: +# - type: cloud +# subtypes: +# - service: compute +# provider: google +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_instance#enable_vtpm +# good_examples: checks/cloud/google/compute/enable_shielded_vm_im.tf.go +# bad_examples: checks/cloud/google/compute/enable_shielded_vm_im.tf.go +package builtin.google.compute.google0045 + +import rego.v1 + +deny contains res if { + some instance in input.google.compute.instances + instance.shieldedvm.integritymonitoringenabled.value == false + res := result.new("Instance does not have shielded VM integrity monitoring enabled.", instance.shieldedvm.integritymonitoringenabled) +} diff --git a/checks/cloud/google/compute/enable_shielded_vm_im_test.go b/checks/cloud/google/compute/enable_shielded_vm_im_test.go deleted file mode 100644 index 94f768be..00000000 --- a/checks/cloud/google/compute/enable_shielded_vm_im_test.go +++ /dev/null @@ -1,71 +0,0 @@ -package compute - -import ( - "testing" - - trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/google/compute" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/stretchr/testify/assert" -) - -func TestCheckEnableShieldedVMIntegrityMonitoring(t *testing.T) { - tests := []struct { - name string - input compute.Compute - expected bool - }{ - { - name: "Instance shielded VM integrity monitoring disabled", - input: compute.Compute{ - Instances: []compute.Instance{ - { - Metadata: trivyTypes.NewTestMetadata(), - ShieldedVM: compute.ShieldedVMConfig{ - Metadata: trivyTypes.NewTestMetadata(), - IntegrityMonitoringEnabled: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: true, - }, - { - name: "Instance shielded VM integrity monitoring enabled", - input: compute.Compute{ - Instances: []compute.Instance{ - { - Metadata: trivyTypes.NewTestMetadata(), - ShieldedVM: compute.ShieldedVMConfig{ - Metadata: trivyTypes.NewTestMetadata(), - IntegrityMonitoringEnabled: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.Google.Compute = test.input - results := CheckEnableShieldedVMIntegrityMonitoring.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckEnableShieldedVMIntegrityMonitoring.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/google/compute/enable_shielded_vm_im_test.rego b/checks/cloud/google/compute/enable_shielded_vm_im_test.rego new file mode 100644 index 00000000..40c097d3 --- /dev/null +++ b/checks/cloud/google/compute/enable_shielded_vm_im_test.rego @@ -0,0 +1,20 @@ +package builtin.google.compute.google0045_test + +import rego.v1 + +import data.builtin.google.compute.google0045 as check +import data.lib.test + +test_deny_instance_shielded_vm_integrity_monitoring_disabled if { + inp := {"google": {"compute": {"instances": [{"shieldedvm": {"integritymonitoringenabled": {"value": false}}}]}}} + + res := check.deny with input as inp + count(res) == 1 +} + +test_allow_instance_shielded_vm_integrity_monitoring_enabled if { + inp := {"google": {"compute": {"instances": [{"shieldedvm": {"integritymonitoringenabled": {"value": true}}}]}}} + + res := check.deny with input as inp + res == set() +} diff --git a/checks/cloud/google/compute/enable_shielded_vm_sb.go b/checks/cloud/google/compute/enable_shielded_vm_sb.go index d0b7abe7..16f7747b 100644 --- a/checks/cloud/google/compute/enable_shielded_vm_sb.go +++ b/checks/cloud/google/compute/enable_shielded_vm_sb.go @@ -27,7 +27,8 @@ var CheckEnableShieldedVMSecureBoot = rules.Register( Links: terraformEnableShieldedVmSbLinks, RemediationMarkdown: terraformEnableShieldedVmSbRemediationMarkdown, }, - Severity: severity.Medium, + Severity: severity.Medium, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, instance := range s.Google.Compute.Instances { diff --git a/checks/cloud/google/compute/enable_shielded_vm_sb.rego b/checks/cloud/google/compute/enable_shielded_vm_sb.rego new file mode 100644 index 00000000..1c252d12 --- /dev/null +++ b/checks/cloud/google/compute/enable_shielded_vm_sb.rego @@ -0,0 +1,40 @@ +# METADATA +# title: Instances should have Shielded VM secure boot enabled +# description: | +# Secure boot helps ensure that the system only runs authentic software. +# scope: package +# schemas: +# - input: schema["cloud"] +# related_resources: +# - https://cloud.google.com/security/shielded-cloud/shielded-vm#secure-boot +# custom: +# id: AVD-GCP-0067 +# avd_id: AVD-GCP-0067 +# provider: google +# service: compute +# severity: MEDIUM +# short_code: enable-shielded-vm-sb +# recommended_action: Enable Shielded VM secure boot +# input: +# selector: +# - type: cloud +# subtypes: +# - service: compute +# provider: google +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_instance#enable_secure_boot +# good_examples: checks/cloud/google/compute/enable_shielded_vm_sb.tf.go +# bad_examples: checks/cloud/google/compute/enable_shielded_vm_sb.tf.go +package builtin.google.compute.google0067 + +import rego.v1 + +deny contains res if { + some instance in input.google.compute.instances + instance.shieldedvm.securebootenabled.value == false + res := result.new( + "Instance does not have shielded VM secure boot enabled.", + instance.shieldedvm.securebootenabled, + ) +} diff --git a/checks/cloud/google/compute/enable_shielded_vm_sb_test.go b/checks/cloud/google/compute/enable_shielded_vm_sb_test.go deleted file mode 100644 index a4f948e4..00000000 --- a/checks/cloud/google/compute/enable_shielded_vm_sb_test.go +++ /dev/null @@ -1,71 +0,0 @@ -package compute - -import ( - "testing" - - trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/google/compute" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/stretchr/testify/assert" -) - -func TestCheckEnableShieldedVMSecureBoot(t *testing.T) { - tests := []struct { - name string - input compute.Compute - expected bool - }{ - { - name: "Instance shielded VM secure boot disabled", - input: compute.Compute{ - Instances: []compute.Instance{ - { - Metadata: trivyTypes.NewTestMetadata(), - ShieldedVM: compute.ShieldedVMConfig{ - Metadata: trivyTypes.NewTestMetadata(), - SecureBootEnabled: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: true, - }, - { - name: "Instance shielded VM secure boot enabled", - input: compute.Compute{ - Instances: []compute.Instance{ - { - Metadata: trivyTypes.NewTestMetadata(), - ShieldedVM: compute.ShieldedVMConfig{ - Metadata: trivyTypes.NewTestMetadata(), - SecureBootEnabled: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.Google.Compute = test.input - results := CheckEnableShieldedVMSecureBoot.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckEnableShieldedVMSecureBoot.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/google/compute/enable_shielded_vm_sb_test.rego b/checks/cloud/google/compute/enable_shielded_vm_sb_test.rego new file mode 100644 index 00000000..76d9821a --- /dev/null +++ b/checks/cloud/google/compute/enable_shielded_vm_sb_test.rego @@ -0,0 +1,20 @@ +package builtin.google.compute.google0067_test + +import rego.v1 + +import data.builtin.google.compute.google0067 as check +import data.lib.test + +test_deny_instance_shielded_vm_secure_boot_disabled if { + inp := {"google": {"compute": {"instances": [{"shieldedvm": {"securebootenabled": {"value": false}}}]}}} + + res := check.deny with input as inp + count(res) == 1 +} + +test_allow_instance_shielded_vm_secure_boot_enabled if { + inp := {"google": {"compute": {"instances": [{"shieldedvm": {"securebootenabled": {"value": true}}}]}}} + + res := check.deny with input as inp + res == set() +} diff --git a/checks/cloud/google/compute/enable_shielded_vm_vtpm.go b/checks/cloud/google/compute/enable_shielded_vm_vtpm.go index 26786edb..650a7fe5 100755 --- a/checks/cloud/google/compute/enable_shielded_vm_vtpm.go +++ b/checks/cloud/google/compute/enable_shielded_vm_vtpm.go @@ -27,7 +27,8 @@ var CheckEnableShieldedVMVTPM = rules.Register( Links: terraformEnableShieldedVmVtpmLinks, RemediationMarkdown: terraformEnableShieldedVmVtpmRemediationMarkdown, }, - Severity: severity.Medium, + Severity: severity.Medium, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, instance := range s.Google.Compute.Instances { diff --git a/checks/cloud/google/compute/enable_shielded_vm_vtpm.rego b/checks/cloud/google/compute/enable_shielded_vm_vtpm.rego new file mode 100644 index 00000000..043a4a12 --- /dev/null +++ b/checks/cloud/google/compute/enable_shielded_vm_vtpm.rego @@ -0,0 +1,40 @@ +# METADATA +# title: Instances should have Shielded VM VTPM enabled +# description: | +# The virtual TPM provides numerous security measures to your VM. +# scope: package +# schemas: +# - input: schema["cloud"] +# related_resources: +# - https://cloud.google.com/blog/products/identity-security/virtual-trusted-platform-module-for-shielded-vms-security-in-plaintext +# custom: +# id: AVD-GCP-0041 +# avd_id: AVD-GCP-0041 +# provider: google +# service: compute +# severity: MEDIUM +# short_code: enable-shielded-vm-vtpm +# recommended_action: Enable Shielded VM VTPM +# input: +# selector: +# - type: cloud +# subtypes: +# - service: compute +# provider: google +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_instance#enable_vtpm +# good_examples: checks/cloud/google/compute/enable_shielded_vm_vtpm.tf.go +# bad_examples: checks/cloud/google/compute/enable_shielded_vm_vtpm.tf.go +package builtin.google.compute.google0041 + +import rego.v1 + +deny contains res if { + some instance in input.google.compute.instances + instance.shieldedvm.vtpmenabled.value == false + res := result.new( + "Instance does not have VTPM for shielded VMs enabled.", + instance.shieldedvm.vtpmenabled, + ) +} diff --git a/checks/cloud/google/compute/enable_shielded_vm_vtpm_test.go b/checks/cloud/google/compute/enable_shielded_vm_vtpm_test.go deleted file mode 100644 index 40925a81..00000000 --- a/checks/cloud/google/compute/enable_shielded_vm_vtpm_test.go +++ /dev/null @@ -1,71 +0,0 @@ -package compute - -import ( - "testing" - - trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/google/compute" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/stretchr/testify/assert" -) - -func TestCheckEnableShieldedVMVTPM(t *testing.T) { - tests := []struct { - name string - input compute.Compute - expected bool - }{ - { - name: "Instance shielded VM VTPM disabled", - input: compute.Compute{ - Instances: []compute.Instance{ - { - Metadata: trivyTypes.NewTestMetadata(), - ShieldedVM: compute.ShieldedVMConfig{ - Metadata: trivyTypes.NewTestMetadata(), - VTPMEnabled: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: true, - }, - { - name: "Instance shielded VM VTPM enabled", - input: compute.Compute{ - Instances: []compute.Instance{ - { - Metadata: trivyTypes.NewTestMetadata(), - ShieldedVM: compute.ShieldedVMConfig{ - Metadata: trivyTypes.NewTestMetadata(), - VTPMEnabled: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.Google.Compute = test.input - results := CheckEnableShieldedVMVTPM.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckEnableShieldedVMVTPM.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/google/compute/enable_shielded_vm_vtpm_test.rego b/checks/cloud/google/compute/enable_shielded_vm_vtpm_test.rego new file mode 100644 index 00000000..f808134c --- /dev/null +++ b/checks/cloud/google/compute/enable_shielded_vm_vtpm_test.rego @@ -0,0 +1,20 @@ +package builtin.google.compute.google0041_test + +import rego.v1 + +import data.builtin.google.compute.google0041 as check +import data.lib.test + +test_deny_instance_shielded_vm_vptm_disabled if { + inp := {"google": {"compute": {"instances": [{"shieldedvm": {"vtpmenabled": {"value": false}}}]}}} + + res := check.deny with input as inp + count(res) == 1 +} + +test_allow_instance_shielded_vm_vptm_enabled if { + inp := {"google": {"compute": {"instances": [{"shieldedvm": {"vtpmenabled": {"value": true}}}]}}} + + res := check.deny with input as inp + res == set() +} diff --git a/checks/cloud/google/compute/enable_vpc_flow_logs.go b/checks/cloud/google/compute/enable_vpc_flow_logs.go index 060f9cff..ef88b095 100755 --- a/checks/cloud/google/compute/enable_vpc_flow_logs.go +++ b/checks/cloud/google/compute/enable_vpc_flow_logs.go @@ -25,7 +25,8 @@ var CheckEnableVPCFlowLogs = rules.Register( Links: terraformEnableVpcFlowLogsLinks, RemediationMarkdown: terraformEnableVpcFlowLogsRemediationMarkdown, }, - Severity: severity.Low, + Severity: severity.Low, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, network := range s.Google.Compute.Networks { diff --git a/checks/cloud/google/compute/enable_vpc_flow_logs.rego b/checks/cloud/google/compute/enable_vpc_flow_logs.rego new file mode 100644 index 00000000..73ef9bd6 --- /dev/null +++ b/checks/cloud/google/compute/enable_vpc_flow_logs.rego @@ -0,0 +1,45 @@ +# METADATA +# title: VPC flow logs should be enabled for all subnetworks +# description: | +# VPC flow logs record information about all traffic, which is a vital tool in reviewing anomalous traffic. +# scope: package +# schemas: +# - input: schema["cloud"] +# custom: +# id: AVD-GCP-0029 +# avd_id: AVD-GCP-0029 +# provider: google +# service: compute +# severity: LOW +# short_code: enable-vpc-flow-logs +# recommended_action: Enable VPC flow logs +# input: +# selector: +# - type: cloud +# subtypes: +# - service: compute +# provider: google +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_subnetwork#enable_flow_logs +# good_examples: checks/cloud/google/compute/enable_vpc_flow_logs.tf.go +# bad_examples: checks/cloud/google/compute/enable_vpc_flow_logs.tf.go +package builtin.google.compute.google0029 + +import rego.v1 + +deny contains res if { + some subnetwork in input.google.compute.networks[_].subnetworks + not is_proxy_only_network(subnetwork) + is_flow_logs_disabled(subnetwork) + res := result.new( + "Subnetwork does not have VPC flow logs enabled.", + object.get(subnetwork, "enableflowlogs", subnetwork), + ) +} + +is_proxy_only_network(network) if network.purpose.value in {"REGIONAL_MANAGED_PROXY", "GLOBAL_MANAGED_PROXY"} + +is_flow_logs_disabled(network) if { + not network.enableflowlogs.value +} diff --git a/checks/cloud/google/compute/enable_vpc_flow_logs_test.go b/checks/cloud/google/compute/enable_vpc_flow_logs_test.go deleted file mode 100644 index 30a31292..00000000 --- a/checks/cloud/google/compute/enable_vpc_flow_logs_test.go +++ /dev/null @@ -1,93 +0,0 @@ -package compute - -import ( - "testing" - - trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/google/compute" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/stretchr/testify/assert" -) - -func TestCheckEnableVPCFlowLogs(t *testing.T) { - tests := []struct { - name string - input compute.Compute - expected bool - }{ - { - name: "Subnetwork VPC flow logs disabled", - input: compute.Compute{ - Networks: []compute.Network{ - { - Metadata: trivyTypes.NewTestMetadata(), - Subnetworks: []compute.SubNetwork{ - { - Metadata: trivyTypes.NewTestMetadata(), - EnableFlowLogs: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - }, - expected: true, - }, - { - name: "Subnetwork VPC flow logs enabled", - input: compute.Compute{ - Networks: []compute.Network{ - { - Metadata: trivyTypes.NewTestMetadata(), - Subnetworks: []compute.SubNetwork{ - { - Metadata: trivyTypes.NewTestMetadata(), - EnableFlowLogs: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - }, - expected: false, - }, - { - name: "Proxy-only subnets and logs disabled", - input: compute.Compute{ - Networks: []compute.Network{ - { - Metadata: trivyTypes.NewTestMetadata(), - Subnetworks: []compute.SubNetwork{ - { - Metadata: trivyTypes.NewTestMetadata(), - EnableFlowLogs: trivyTypes.BoolDefault(false, trivyTypes.NewTestMetadata()), - Purpose: trivyTypes.String("REGIONAL_MANAGED_PROXY", trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - }, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.Google.Compute = test.input - results := CheckEnableVPCFlowLogs.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckEnableVPCFlowLogs.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/google/compute/enable_vpc_flow_logs_test.rego b/checks/cloud/google/compute/enable_vpc_flow_logs_test.rego new file mode 100644 index 00000000..78d348a0 --- /dev/null +++ b/checks/cloud/google/compute/enable_vpc_flow_logs_test.rego @@ -0,0 +1,43 @@ +package builtin.google.compute.google0029_test + +import rego.v1 + +import data.builtin.google.compute.google0029 as check +import data.lib.test + +test_deny_vpc_flow_logs_disabled if { + inp := {"google": {"compute": {"networks": [{"subnetworks": [{"enableflowlogs": {"value": false}}]}]}}} + + res := check.deny with input as inp + count(res) == 1 +} + +test_deny_vpc_flow_logs_is_not_specified if { + inp := {"google": {"compute": {"networks": [{"subnetworks": [{}]}]}}} + + res := check.deny with input as inp + count(res) == 1 +} + +test_allow_vpc_flow_logs_enabled if { + inp := {"google": {"compute": {"networks": [{"subnetworks": [{"enableflowlogs": {"value": true}}]}]}}} + + res := check.deny with input as inp + res == set() +} + +test_allow_vpc_flow_logs_disabled_for_proxy_only if { + inp := {"google": {"compute": {"networks": [{"subnetworks": [ + { + "enableflowlogs": {"value": false}, + "purpose": {"value": "REGIONAL_MANAGED_PROXY"}, + }, + { + "enableflowlogs": {"value": false}, + "purpose": {"value": "GLOBAL_MANAGED_PROXY"}, + }, + ]}]}}} + + res := check.deny with input as inp + res == set() +} diff --git a/checks/cloud/google/compute/no_default_service_account.go b/checks/cloud/google/compute/no_default_service_account.go index c2894b7c..9d7b3752 100755 --- a/checks/cloud/google/compute/no_default_service_account.go +++ b/checks/cloud/google/compute/no_default_service_account.go @@ -25,7 +25,8 @@ var CheckNoDefaultServiceAccount = rules.Register( Links: terraformNoDefaultServiceAccountLinks, RemediationMarkdown: terraformNoDefaultServiceAccountRemediationMarkdown, }, - Severity: severity.Critical, + Severity: severity.Critical, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, instance := range s.Google.Compute.Instances { diff --git a/checks/cloud/google/compute/no_default_service_account.rego b/checks/cloud/google/compute/no_default_service_account.rego new file mode 100644 index 00000000..5508e7e7 --- /dev/null +++ b/checks/cloud/google/compute/no_default_service_account.rego @@ -0,0 +1,41 @@ +# METADATA +# title: Instances should not use the default service account +# description: | +# The default service account has full project access. Instances should instead be assigned the minimal access they need. +# scope: package +# schemas: +# - input: schema["cloud"] +# custom: +# id: AVD-GCP-0044 +# avd_id: AVD-GCP-0044 +# provider: google +# service: compute +# severity: CRITICAL +# short_code: no-default-service-account +# recommended_action: Remove use of default service account +# input: +# selector: +# - type: cloud +# subtypes: +# - service: compute +# provider: google +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_instance# +# good_examples: checks/cloud/google/compute/no_default_service_account.tf.go +# bad_examples: checks/cloud/google/compute/no_default_service_account.tf.go +package builtin.google.compute.google0044 + +import rego.v1 + +deny contains res if { + some instance in input.google.compute.instances + service_account := instance.serviceaccount + is_default_service_account(service_account) + res := result.new( + "Instance uses the default service account.", + object.get(service_account, "email", service_account), + ) +} + +is_default_service_account(service_account) := service_account.isdefault.value == true diff --git a/checks/cloud/google/compute/no_default_service_account_test.go b/checks/cloud/google/compute/no_default_service_account_test.go deleted file mode 100644 index a6aaab82..00000000 --- a/checks/cloud/google/compute/no_default_service_account_test.go +++ /dev/null @@ -1,89 +0,0 @@ -package compute - -import ( - "testing" - - trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/google/compute" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/stretchr/testify/assert" -) - -func TestCheckNoDefaultServiceAccount(t *testing.T) { - tests := []struct { - name string - input compute.Compute - expected bool - }{ - { - name: "Instance service account not specified", - input: compute.Compute{ - Instances: []compute.Instance{ - { - Metadata: trivyTypes.NewTestMetadata(), - ServiceAccount: compute.ServiceAccount{ - Metadata: trivyTypes.NewTestMetadata(), - Email: trivyTypes.String("", trivyTypes.NewTestMetadata()), - IsDefault: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: true, - }, - { - name: "Instance service account using the default email", - input: compute.Compute{ - Instances: []compute.Instance{ - { - Metadata: trivyTypes.NewTestMetadata(), - ServiceAccount: compute.ServiceAccount{ - Metadata: trivyTypes.NewTestMetadata(), - Email: trivyTypes.String("1234567890-compute@developer.gserviceaccount.com", trivyTypes.NewTestMetadata()), - IsDefault: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: true, - }, - { - name: "Instance service account with email provided", - input: compute.Compute{ - Instances: []compute.Instance{ - { - Metadata: trivyTypes.NewTestMetadata(), - ServiceAccount: compute.ServiceAccount{ - Metadata: trivyTypes.NewTestMetadata(), - Email: trivyTypes.String("proper@email.com", trivyTypes.NewTestMetadata()), - IsDefault: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.Google.Compute = test.input - results := CheckNoDefaultServiceAccount.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckNoDefaultServiceAccount.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/google/compute/no_default_service_account_test.rego b/checks/cloud/google/compute/no_default_service_account_test.rego new file mode 100644 index 00000000..15814b7b --- /dev/null +++ b/checks/cloud/google/compute/no_default_service_account_test.rego @@ -0,0 +1,23 @@ +package builtin.google.compute.google0044_test + +import rego.v1 + +import data.builtin.google.compute.google0044 as check +import data.lib.test + +test_deny_instance_use_default_service_account if { + inp := {"google": {"compute": {"instances": [{"serviceaccount": {"isdefault": {"value": true}}}]}}} + + res := check.deny with input as inp + count(res) == 1 +} + +test_allow_instance_use_proper_service_account if { + inp := {"google": {"compute": {"instances": [{"serviceaccount": { + "isdefault": {"value": false}, + "email": {"value": "proper@email.com"}, + }}]}}} + + res := check.deny with input as inp + res == set() +} diff --git a/checks/cloud/google/compute/no_ip_forwarding.go b/checks/cloud/google/compute/no_ip_forwarding.go index b14106fc..2f61be03 100755 --- a/checks/cloud/google/compute/no_ip_forwarding.go +++ b/checks/cloud/google/compute/no_ip_forwarding.go @@ -25,7 +25,8 @@ var CheckNoIpForwarding = rules.Register( Links: terraformNoIpForwardingLinks, RemediationMarkdown: terraformNoIpForwardingRemediationMarkdown, }, - Severity: severity.High, + Severity: severity.High, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, instance := range s.Google.Compute.Instances { diff --git a/checks/cloud/google/compute/no_ip_forwarding.rego b/checks/cloud/google/compute/no_ip_forwarding.rego new file mode 100644 index 00000000..58c01de2 --- /dev/null +++ b/checks/cloud/google/compute/no_ip_forwarding.rego @@ -0,0 +1,35 @@ +# METADATA +# title: Instances should not have IP forwarding enabled +# description: | +# Disabling IP forwarding ensures the instance can only receive packets addressed to the instance and can only send packets with a source address of the instance. +# scope: package +# schemas: +# - input: schema["cloud"] +# custom: +# id: AVD-GCP-0043 +# avd_id: AVD-GCP-0043 +# provider: google +# service: compute +# severity: HIGH +# short_code: no-ip-forwarding +# recommended_action: Disable IP forwarding +# input: +# selector: +# - type: cloud +# subtypes: +# - service: compute +# provider: google +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_instance#can_ip_forward +# good_examples: checks/cloud/google/compute/no_ip_forwarding.tf.go +# bad_examples: checks/cloud/google/compute/no_ip_forwarding.tf.go +package builtin.google.compute.google0043 + +import rego.v1 + +deny contains res if { + some instance in input.google.compute.instances + instance.canipforward.value == true + res := result.new("Instance has IP forwarding allowed", instance.canipforward) +} diff --git a/checks/cloud/google/compute/no_ip_forwarding_test.go b/checks/cloud/google/compute/no_ip_forwarding_test.go deleted file mode 100644 index 24f7df6d..00000000 --- a/checks/cloud/google/compute/no_ip_forwarding_test.go +++ /dev/null @@ -1,65 +0,0 @@ -package compute - -import ( - "testing" - - trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/google/compute" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/stretchr/testify/assert" -) - -func TestCheckNoIpForwarding(t *testing.T) { - tests := []struct { - name string - input compute.Compute - expected bool - }{ - { - name: "Instance IP forwarding enabled", - input: compute.Compute{ - Instances: []compute.Instance{ - { - Metadata: trivyTypes.NewTestMetadata(), - CanIPForward: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - }, - }, - }, - expected: true, - }, - { - name: "Instance IP forwarding disabled", - input: compute.Compute{ - Instances: []compute.Instance{ - { - Metadata: trivyTypes.NewTestMetadata(), - CanIPForward: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - }, - }, - }, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.Google.Compute = test.input - results := CheckNoIpForwarding.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckNoIpForwarding.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/google/compute/no_ip_forwarding_test.rego b/checks/cloud/google/compute/no_ip_forwarding_test.rego new file mode 100644 index 00000000..ee3b5169 --- /dev/null +++ b/checks/cloud/google/compute/no_ip_forwarding_test.rego @@ -0,0 +1,27 @@ +package builtin.google.compute.google0043_test + +import rego.v1 + +import data.builtin.google.compute.google0043 as check +import data.lib.test + +test_deny_instance_ip_forwarding_enabled if { + inp := {"google": {"compute": {"instances": [{"canipforward": {"value": true}}]}}} + + res := check.deny with input as inp + count(res) == 1 +} + +test_allow_instance_ip_forwarding_disabled if { + inp := {"google": {"compute": {"instances": [{"canipforward": {"value": false}}]}}} + + res := check.deny with input as inp + res == set() +} + +test_allow_instance_ip_forwarding_is_not_specified if { + inp := {"google": {"compute": {"instances": [{}]}}} + + res := check.deny with input as inp + res == set() +} diff --git a/checks/cloud/google/compute/no_oslogin_override.go b/checks/cloud/google/compute/no_oslogin_override.go index 1a683440..73520307 100755 --- a/checks/cloud/google/compute/no_oslogin_override.go +++ b/checks/cloud/google/compute/no_oslogin_override.go @@ -25,7 +25,8 @@ var CheckNoOsloginOverride = rules.Register( Links: terraformNoOsloginOverrideLinks, RemediationMarkdown: terraformNoOsloginOverrideRemediationMarkdown, }, - Severity: severity.Medium, + Severity: severity.Medium, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, instance := range s.Google.Compute.Instances { diff --git a/checks/cloud/google/compute/no_oslogin_override.rego b/checks/cloud/google/compute/no_oslogin_override.rego new file mode 100644 index 00000000..2a4f49e7 --- /dev/null +++ b/checks/cloud/google/compute/no_oslogin_override.rego @@ -0,0 +1,35 @@ +# METADATA +# title: Instances should not override the project setting for OS Login +# description: | +# OS Login automatically revokes the relevant SSH keys when an IAM user has their access revoked. +# scope: package +# schemas: +# - input: schema["cloud"] +# custom: +# id: AVD-GCP-0036 +# avd_id: AVD-GCP-0036 +# provider: google +# service: compute +# severity: MEDIUM +# short_code: no-oslogin-override +# recommended_action: Enable OS Login at project level and remove instance-level overrides +# input: +# selector: +# - type: cloud +# subtypes: +# - service: compute +# provider: google +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_instance# +# good_examples: checks/cloud/google/compute/no_oslogin_override.tf.go +# bad_examples: checks/cloud/google/compute/no_oslogin_override.tf.go +package builtin.google.compute.google0036 + +import rego.v1 + +deny contains res if { + some instance in input.google.compute.instances + instance.osloginenabled.value == false + res := result.new("Instance has OS Login disabled.", instance.osloginenabled) +} diff --git a/checks/cloud/google/compute/no_oslogin_override_test.go b/checks/cloud/google/compute/no_oslogin_override_test.go deleted file mode 100644 index 7704dfa6..00000000 --- a/checks/cloud/google/compute/no_oslogin_override_test.go +++ /dev/null @@ -1,65 +0,0 @@ -package compute - -import ( - "testing" - - trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/google/compute" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/stretchr/testify/assert" -) - -func TestCheckNoOsloginOverride(t *testing.T) { - tests := []struct { - name string - input compute.Compute - expected bool - }{ - { - name: "Instance OS login disabled", - input: compute.Compute{ - Instances: []compute.Instance{ - { - Metadata: trivyTypes.NewTestMetadata(), - OSLoginEnabled: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - }, - }, - }, - expected: true, - }, - { - name: "Instance OS login enabled", - input: compute.Compute{ - Instances: []compute.Instance{ - { - Metadata: trivyTypes.NewTestMetadata(), - OSLoginEnabled: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - }, - }, - }, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.Google.Compute = test.input - results := CheckNoOsloginOverride.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckNoOsloginOverride.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/google/compute/no_oslogin_override_test.rego b/checks/cloud/google/compute/no_oslogin_override_test.rego new file mode 100644 index 00000000..54677433 --- /dev/null +++ b/checks/cloud/google/compute/no_oslogin_override_test.rego @@ -0,0 +1,20 @@ +package builtin.google.compute.google0036_test + +import rego.v1 + +import data.builtin.google.compute.google0036 as check +import data.lib.test + +test_deny_instance_os_login_disabled if { + inp := {"google": {"compute": {"instances": [{"osloginenabled": {"value": false}}]}}} + + res := check.deny with input as inp + count(res) == 1 +} + +test_allow_instance_os_login_enabled if { + inp := {"google": {"compute": {"instances": [{"osloginenabled": {"value": true}}]}}} + + res := check.deny with input as inp + res == set() +} diff --git a/checks/cloud/google/compute/no_project_wide_ssh_keys.go b/checks/cloud/google/compute/no_project_wide_ssh_keys.go index 6d9b1ca0..b9d43557 100755 --- a/checks/cloud/google/compute/no_project_wide_ssh_keys.go +++ b/checks/cloud/google/compute/no_project_wide_ssh_keys.go @@ -25,7 +25,8 @@ var CheckNoProjectWideSshKeys = rules.Register( Links: terraformNoProjectWideSshKeysLinks, RemediationMarkdown: terraformNoProjectWideSshKeysRemediationMarkdown, }, - Severity: severity.Medium, + Severity: severity.Medium, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, instance := range s.Google.Compute.Instances { diff --git a/checks/cloud/google/compute/no_project_wide_ssh_keys.rego b/checks/cloud/google/compute/no_project_wide_ssh_keys.rego new file mode 100644 index 00000000..fc60bb8f --- /dev/null +++ b/checks/cloud/google/compute/no_project_wide_ssh_keys.rego @@ -0,0 +1,35 @@ +# METADATA +# title: Disable project-wide SSH keys for all instances +# description: | +# Use of project-wide SSH keys means that a compromise of any one of these key pairs can result in all instances being compromised. It is recommended to use instance-level keys. +# scope: package +# schemas: +# - input: schema["cloud"] +# custom: +# id: AVD-GCP-0030 +# avd_id: AVD-GCP-0030 +# provider: google +# service: compute +# severity: MEDIUM +# short_code: no-project-wide-ssh-keys +# recommended_action: Disable project-wide SSH keys +# input: +# selector: +# - type: cloud +# subtypes: +# - service: compute +# provider: google +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_instance# +# good_examples: checks/cloud/google/compute/no_project_wide_ssh_keys.tf.go +# bad_examples: checks/cloud/google/compute/no_project_wide_ssh_keys.tf.go +package builtin.google.compute.google0030 + +import rego.v1 + +deny contains res if { + some instance in input.google.compute.instances + instance.enableprojectsshkeyblocking.value == false + res := result.new("Instance allows use of project-level SSH keys.", instance.enableprojectsshkeyblocking) +} diff --git a/checks/cloud/google/compute/no_project_wide_ssh_keys_test.go b/checks/cloud/google/compute/no_project_wide_ssh_keys_test.go deleted file mode 100644 index 981f9d45..00000000 --- a/checks/cloud/google/compute/no_project_wide_ssh_keys_test.go +++ /dev/null @@ -1,65 +0,0 @@ -package compute - -import ( - "testing" - - trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/google/compute" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/stretchr/testify/assert" -) - -func TestCheckNoProjectWideSshKeys(t *testing.T) { - tests := []struct { - name string - input compute.Compute - expected bool - }{ - { - name: "Instance project level SSH keys blocked", - input: compute.Compute{ - Instances: []compute.Instance{ - { - Metadata: trivyTypes.NewTestMetadata(), - EnableProjectSSHKeyBlocking: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - }, - }, - }, - expected: true, - }, - { - name: "Instance project level SSH keys allowed", - input: compute.Compute{ - Instances: []compute.Instance{ - { - Metadata: trivyTypes.NewTestMetadata(), - EnableProjectSSHKeyBlocking: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - }, - }, - }, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.Google.Compute = test.input - results := CheckNoProjectWideSshKeys.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckNoProjectWideSshKeys.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/google/compute/no_project_wide_ssh_keys_test.rego b/checks/cloud/google/compute/no_project_wide_ssh_keys_test.rego new file mode 100644 index 00000000..be2d56d7 --- /dev/null +++ b/checks/cloud/google/compute/no_project_wide_ssh_keys_test.rego @@ -0,0 +1,20 @@ +package builtin.google.compute.google0030_test + +import rego.v1 + +import data.builtin.google.compute.google0030 as check +import data.lib.test + +test_deny_project_level_ssh_key_blocking_disabled if { + inp := {"google": {"compute": {"instances": [{"enableprojectsshkeyblocking": {"value": false}}]}}} + + res := check.deny with input as inp + count(res) == 1 +} + +test_allow_project_level_ssh_key_blocking_enabled if { + inp := {"google": {"compute": {"instances": [{"enableprojectsshkeyblocking": {"value": true}}]}}} + + res := check.deny with input as inp + res == set() +} diff --git a/checks/cloud/google/compute/no_public_egress.go b/checks/cloud/google/compute/no_public_egress.go index 6c5cd00e..c05858fb 100755 --- a/checks/cloud/google/compute/no_public_egress.go +++ b/checks/cloud/google/compute/no_public_egress.go @@ -30,7 +30,8 @@ Where possible, segments should be broken into smaller subnets and avoid using t Links: terraformNoPublicEgressLinks, RemediationMarkdown: terraformNoPublicEgressRemediationMarkdown, }, - Severity: severity.Critical, + Severity: severity.Critical, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, network := range s.Google.Compute.Networks { diff --git a/checks/cloud/google/compute/no_public_egress.rego b/checks/cloud/google/compute/no_public_egress.rego new file mode 100644 index 00000000..d521ef18 --- /dev/null +++ b/checks/cloud/google/compute/no_public_egress.rego @@ -0,0 +1,46 @@ +# METADATA +# title: An outbound firewall rule allows traffic to /0. +# description: | +# Network security rules should not use very broad subnets. +# Where possible, segments should be broken into smaller subnets and avoid using the /0 subnet. +# scope: package +# schemas: +# - input: schema["cloud"] +# related_resources: +# - https://cloud.google.com/vpc/docs/using-firewalls +# custom: +# id: AVD-GCP-0035 +# avd_id: AVD-GCP-0035 +# provider: google +# service: compute +# severity: CRITICAL +# short_code: no-public-egress +# recommended_action: Set a more restrictive cidr range +# input: +# selector: +# - type: cloud +# subtypes: +# - service: compute +# provider: google +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_firewall +# good_examples: checks/cloud/google/compute/no_public_egress.tf.go +# bad_examples: checks/cloud/google/compute/no_public_egress.tf.go +package builtin.google.compute.google0035 + +import rego.v1 + +deny contains res if { + some network in input.google.compute.networks + some rule in network.firewall.egressrules + rule.firewallrule.isallow.value + rule.firewallrule.enforced.value + some destination in rule.destinationranges + cidr.is_public(destination.value) + cidr.count_addresses(destination.value) > 1 + res := result.new( + "Firewall rule allows egress traffic to multiple addresses on the public internet.", + destination, + ) +} diff --git a/checks/cloud/google/compute/no_public_egress_test.go b/checks/cloud/google/compute/no_public_egress_test.go deleted file mode 100644 index a1c7250b..00000000 --- a/checks/cloud/google/compute/no_public_egress_test.go +++ /dev/null @@ -1,96 +0,0 @@ -package compute - -import ( - "testing" - - trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/google/compute" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/stretchr/testify/assert" -) - -func TestCheckNoPublicEgress(t *testing.T) { - tests := []struct { - name string - input compute.Compute - expected bool - }{ - { - name: "Firewall egress rule with multiple public destination addresses", - input: compute.Compute{ - Networks: []compute.Network{ - { - Metadata: trivyTypes.NewTestMetadata(), - Firewall: &compute.Firewall{ - Metadata: trivyTypes.NewTestMetadata(), - EgressRules: []compute.EgressRule{ - { - Metadata: trivyTypes.NewTestMetadata(), - FirewallRule: compute.FirewallRule{ - Metadata: trivyTypes.NewTestMetadata(), - IsAllow: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - Enforced: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - }, - DestinationRanges: []trivyTypes.StringValue{ - trivyTypes.String("0.0.0.0/0", trivyTypes.NewTestMetadata()), - trivyTypes.String("1.2.3.4/32", trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - }, - }, - }, - expected: true, - }, - { - name: "Firewall egress rule with public destination address", - input: compute.Compute{ - Networks: []compute.Network{ - { - Metadata: trivyTypes.NewTestMetadata(), - Firewall: &compute.Firewall{ - Metadata: trivyTypes.NewTestMetadata(), - EgressRules: []compute.EgressRule{ - { - Metadata: trivyTypes.NewTestMetadata(), - FirewallRule: compute.FirewallRule{ - Metadata: trivyTypes.NewTestMetadata(), - IsAllow: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - Enforced: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - }, - DestinationRanges: []trivyTypes.StringValue{ - trivyTypes.String("1.2.3.4/32", trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - }, - }, - }, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.Google.Compute = test.input - results := CheckNoPublicEgress.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckNoPublicEgress.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/google/compute/no_public_egress_test.rego b/checks/cloud/google/compute/no_public_egress_test.rego new file mode 100644 index 00000000..c7020430 --- /dev/null +++ b/checks/cloud/google/compute/no_public_egress_test.rego @@ -0,0 +1,35 @@ +package builtin.google.compute.google0035_test + +import rego.v1 + +import data.builtin.google.compute.google0035 as check +import data.lib.test + +test_deny_egress_rule_with_multiple_public_destinations if { + inp := {"google": {"compute": {"networks": [{"firewall": {"egressrules": [{ + "firewallrule": { + "isallow": {"value": true}, + "enforced": {"value": true}, + }, + "destinationranges": [ + {"value": "0.0.0.0/0"}, + {"value": "1.2.3.4/32"}, + ], + }]}}]}}} + + res := check.deny with input as inp + count(res) == 1 +} + +test_allow_egress_rule_with_public_destination if { + inp := {"google": {"compute": {"networks": [{"firewall": {"egressrules": [{ + "firewallrule": { + "isallow": {"value": true}, + "enforced": {"value": true}, + }, + "destinationranges": [{"value": "1.2.3.4/32"}], + }]}}]}}} + + res := check.deny with input as inp + res == set() +} diff --git a/checks/cloud/google/compute/no_public_ingress.go b/checks/cloud/google/compute/no_public_ingress.go index 729bdfb5..835e0d68 100755 --- a/checks/cloud/google/compute/no_public_ingress.go +++ b/checks/cloud/google/compute/no_public_ingress.go @@ -30,7 +30,8 @@ Where possible, segments should be broken into smaller subnets and avoid using t Links: terraformNoPublicIngressLinks, RemediationMarkdown: terraformNoPublicIngressRemediationMarkdown, }, - Severity: severity.Critical, + Severity: severity.Critical, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, network := range s.Google.Compute.Networks { diff --git a/checks/cloud/google/compute/no_public_ingress.rego b/checks/cloud/google/compute/no_public_ingress.rego new file mode 100644 index 00000000..5ef3e608 --- /dev/null +++ b/checks/cloud/google/compute/no_public_ingress.rego @@ -0,0 +1,50 @@ +# METADATA +# title: An inbound firewall rule allows traffic from /0. +# description: | +# Network security rules should not use very broad subnets. +# Where possible, segments should be broken into smaller subnets and avoid using the /0 subnet. +# scope: package +# schemas: +# - input: schema["cloud"] +# related_resources: +# - https://cloud.google.com/vpc/docs/using-firewalls +# custom: +# id: AVD-GCP-0027 +# avd_id: AVD-GCP-0027 +# provider: google +# service: compute +# severity: CRITICAL +# short_code: no-public-ingress +# recommended_action: Set a more restrictive cidr range +# input: +# selector: +# - type: cloud +# subtypes: +# - service: compute +# provider: google +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_firewall#source_ranges +# - https://www.terraform.io/docs/providers/google/r/compute_firewall.html +# good_examples: checks/cloud/google/compute/no_public_ingress.tf.go +# bad_examples: checks/cloud/google/compute/no_public_ingress.tf.go +package builtin.google.compute.google0027 + +import rego.v1 + +deny contains res if { + some network in input.google.compute.networks + count(object.get(network.firewall, "sourcetags", [])) == 0 + count(object.get(network.firewall, "targettags", [])) == 0 + + some rule in network.firewall.ingressrules + rule.firewallrule.isallow.value + rule.firewallrule.enforced.value + some source in rule.sourceranges + cidr.is_public(source.value) + cidr.count_addresses(source.value) > 1 + res := result.new( + "Firewall rule allows ingress traffic from multiple addresses on the public internet.", + source, + ) +} diff --git a/checks/cloud/google/compute/no_public_ingress_test.go b/checks/cloud/google/compute/no_public_ingress_test.go deleted file mode 100644 index b5da484c..00000000 --- a/checks/cloud/google/compute/no_public_ingress_test.go +++ /dev/null @@ -1,96 +0,0 @@ -package compute - -import ( - "testing" - - trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/google/compute" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/stretchr/testify/assert" -) - -func TestCheckNoPublicIngress(t *testing.T) { - tests := []struct { - name string - input compute.Compute - expected bool - }{ - { - name: "Firewall ingress rule with multiple public source addresses", - input: compute.Compute{ - Networks: []compute.Network{ - { - Metadata: trivyTypes.NewTestMetadata(), - Firewall: &compute.Firewall{ - Metadata: trivyTypes.NewTestMetadata(), - IngressRules: []compute.IngressRule{ - { - Metadata: trivyTypes.NewTestMetadata(), - FirewallRule: compute.FirewallRule{ - Metadata: trivyTypes.NewTestMetadata(), - IsAllow: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - Enforced: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - }, - SourceRanges: []trivyTypes.StringValue{ - trivyTypes.String("0.0.0.0/0", trivyTypes.NewTestMetadata()), - trivyTypes.String("1.2.3.4/32", trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - }, - }, - }, - expected: true, - }, - { - name: "Firewall ingress rule with public source address", - input: compute.Compute{ - Networks: []compute.Network{ - { - Metadata: trivyTypes.NewTestMetadata(), - Firewall: &compute.Firewall{ - Metadata: trivyTypes.NewTestMetadata(), - IngressRules: []compute.IngressRule{ - { - Metadata: trivyTypes.NewTestMetadata(), - FirewallRule: compute.FirewallRule{ - Metadata: trivyTypes.NewTestMetadata(), - IsAllow: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - Enforced: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - }, - SourceRanges: []trivyTypes.StringValue{ - trivyTypes.String("1.2.3.4/32", trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - }, - }, - }, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.Google.Compute = test.input - results := CheckNoPublicIngress.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckNoPublicIngress.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/google/compute/no_public_ingress_test.rego b/checks/cloud/google/compute/no_public_ingress_test.rego new file mode 100644 index 00000000..3c175021 --- /dev/null +++ b/checks/cloud/google/compute/no_public_ingress_test.rego @@ -0,0 +1,35 @@ +package builtin.google.compute.google0027_test + +import rego.v1 + +import data.builtin.google.compute.google0027 as check +import data.lib.test + +test_deny_ingress_rule_with_multiple_public_sources if { + inp := {"google": {"compute": {"networks": [{"firewall": {"ingressrules": [{ + "firewallrule": { + "isallow": {"value": true}, + "enforced": {"value": true}, + }, + "sourceranges": [ + {"value": "0.0.0.0/0"}, + {"value": "1.2.3.4/32"}, + ], + }]}}]}}} + + res := check.deny with input as inp + count(res) == 1 +} + +test_allow_ingress_rule_with_public_source_address if { + inp := {"google": {"compute": {"networks": [{"firewall": {"ingressrules": [{ + "firewallrule": { + "isallow": {"value": true}, + "enforced": {"value": true}, + }, + "sourceranges": [{"value": "1.2.3.4/32"}], + }]}}]}}} + + res := check.deny with input as inp + res == set() +} diff --git a/checks/cloud/google/compute/no_public_ip.go b/checks/cloud/google/compute/no_public_ip.go index 0cc15a7d..469323f8 100755 --- a/checks/cloud/google/compute/no_public_ip.go +++ b/checks/cloud/google/compute/no_public_ip.go @@ -28,6 +28,7 @@ var CheckInstancesDoNotHavePublicIPs = rules.Register( Links: []string{ "https://cloud.google.com/compute/docs/ip-addresses#externaladdresses", }, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, instance := range s.Google.Compute.Instances { diff --git a/checks/cloud/google/compute/no_public_ip.rego b/checks/cloud/google/compute/no_public_ip.rego new file mode 100644 index 00000000..9cc029f1 --- /dev/null +++ b/checks/cloud/google/compute/no_public_ip.rego @@ -0,0 +1,38 @@ +# METADATA +# title: Instances should not have public IP addresses +# description: | +# Instances should not be publicly exposed to the internet +# scope: package +# schemas: +# - input: schema["cloud"] +# related_resources: +# - https://cloud.google.com/compute/docs/ip-addresses#externaladdresses +# custom: +# id: AVD-GCP-0031 +# avd_id: AVD-GCP-0031 +# provider: google +# service: compute +# severity: HIGH +# short_code: no-public-ip +# recommended_action: Remove public IP +# input: +# selector: +# - type: cloud +# subtypes: +# - service: compute +# provider: google +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_instance#access_config +# good_examples: checks/cloud/google/compute/no_public_ip.tf.go +# bad_examples: checks/cloud/google/compute/no_public_ip.tf.go +package builtin.google.compute.google0031 + +import rego.v1 + +deny contains res if { + some instance in input.google.compute.instances + some ni in instance.networkinterfaces + ni.haspublicip.value == true + res := result.new("Instance has a public IP allocated.", ni.haspublicip) +} diff --git a/checks/cloud/google/compute/no_public_ip_test.go b/checks/cloud/google/compute/no_public_ip_test.go deleted file mode 100755 index 8855a5a7..00000000 --- a/checks/cloud/google/compute/no_public_ip_test.go +++ /dev/null @@ -1,74 +0,0 @@ -package compute - -import ( - "testing" - - trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/google/compute" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/stretchr/testify/assert" -) - -func TestCheckInstancesDoNotHavePublicIPs(t *testing.T) { - tests := []struct { - name string - input compute.Compute - expected bool - }{ - { - name: "Network interface with public IP", - input: compute.Compute{ - Instances: []compute.Instance{ - { - Metadata: trivyTypes.NewTestMetadata(), - NetworkInterfaces: []compute.NetworkInterface{ - { - Metadata: trivyTypes.NewTestMetadata(), - HasPublicIP: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - }, - expected: true, - }, - { - name: "Network interface without public IP", - input: compute.Compute{ - Instances: []compute.Instance{ - { - Metadata: trivyTypes.NewTestMetadata(), - NetworkInterfaces: []compute.NetworkInterface{ - { - Metadata: trivyTypes.NewTestMetadata(), - HasPublicIP: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - }, - }, - }, - }}, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.Google.Compute = test.input - results := CheckInstancesDoNotHavePublicIPs.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckInstancesDoNotHavePublicIPs.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/google/compute/no_public_ip_test.rego b/checks/cloud/google/compute/no_public_ip_test.rego new file mode 100644 index 00000000..f6e639ec --- /dev/null +++ b/checks/cloud/google/compute/no_public_ip_test.rego @@ -0,0 +1,20 @@ +package builtin.google.compute.google0031_test + +import rego.v1 + +import data.builtin.google.compute.google0031 as check +import data.lib.test + +test_deny_instance_network_interface_has_public_ip if { + inp := {"google": {"compute": {"instances": [{"networkinterfaces": [{"haspublicip": {"value": true}}]}]}}} + + res := check.deny with input as inp + count(res) == 1 +} + +test_allow_instance_network_interface_has_no_public_ip if { + inp := {"google": {"compute": {"instances": [{"networkinterfaces": [{"haspublicip": {"value": false}}]}]}}} + + res := check.deny with input as inp + res == set() +} diff --git a/checks/cloud/google/compute/no_serial_port.go b/checks/cloud/google/compute/no_serial_port.go index 6f545a52..ebdb1049 100755 --- a/checks/cloud/google/compute/no_serial_port.go +++ b/checks/cloud/google/compute/no_serial_port.go @@ -25,7 +25,8 @@ var CheckNoSerialPort = rules.Register( Links: terraformNoSerialPortLinks, RemediationMarkdown: terraformNoSerialPortRemediationMarkdown, }, - Severity: severity.Medium, + Severity: severity.Medium, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, instance := range s.Google.Compute.Instances { diff --git a/checks/cloud/google/compute/no_serial_port.rego b/checks/cloud/google/compute/no_serial_port.rego new file mode 100644 index 00000000..f69233e0 --- /dev/null +++ b/checks/cloud/google/compute/no_serial_port.rego @@ -0,0 +1,35 @@ +# METADATA +# title: Disable serial port connectivity for all instances +# description: | +# When serial port access is enabled, the access is not governed by network security rules meaning the port can be exposed publicly. +# scope: package +# schemas: +# - input: schema["cloud"] +# custom: +# id: AVD-GCP-0032 +# avd_id: AVD-GCP-0032 +# provider: google +# service: compute +# severity: MEDIUM +# short_code: no-serial-port +# recommended_action: Disable serial port access +# input: +# selector: +# - type: cloud +# subtypes: +# - service: compute +# provider: google +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_instance# +# good_examples: checks/cloud/google/compute/no_serial_port.tf.go +# bad_examples: checks/cloud/google/compute/no_serial_port.tf.go +package builtin.google.compute.google0032 + +import rego.v1 + +deny contains res if { + some instance in input.google.compute.instances + instance.enableserialport.value == true + res := result.new("Instance has serial port enabled.", instance.enableserialport) +} diff --git a/checks/cloud/google/compute/no_serial_port_test.go b/checks/cloud/google/compute/no_serial_port_test.go deleted file mode 100644 index 7334a661..00000000 --- a/checks/cloud/google/compute/no_serial_port_test.go +++ /dev/null @@ -1,65 +0,0 @@ -package compute - -import ( - "testing" - - trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/google/compute" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/stretchr/testify/assert" -) - -func TestCheckNoSerialPort(t *testing.T) { - tests := []struct { - name string - input compute.Compute - expected bool - }{ - { - name: "Instance serial port enabled", - input: compute.Compute{ - Instances: []compute.Instance{ - { - Metadata: trivyTypes.NewTestMetadata(), - EnableSerialPort: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - }, - }, - }, - expected: true, - }, - { - name: "Instance serial port disabled", - input: compute.Compute{ - Instances: []compute.Instance{ - { - Metadata: trivyTypes.NewTestMetadata(), - EnableSerialPort: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - }, - }, - }, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.Google.Compute = test.input - results := CheckNoSerialPort.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckNoSerialPort.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/google/compute/no_serial_port_test.rego b/checks/cloud/google/compute/no_serial_port_test.rego new file mode 100644 index 00000000..746e7c42 --- /dev/null +++ b/checks/cloud/google/compute/no_serial_port_test.rego @@ -0,0 +1,20 @@ +package builtin.google.compute.google0032_test + +import rego.v1 + +import data.builtin.google.compute.google0032 as check +import data.lib.test + +test_deny_instance_serial_port_enabled if { + inp := {"google": {"compute": {"instances": [{"enableserialport": {"value": true}}]}}} + + res := check.deny with input as inp + count(res) == 1 +} + +test_allow_instance_serial_port_disabled if { + inp := {"google": {"compute": {"instances": [{"enableserialport": {"value": false}}]}}} + + res := check.deny with input as inp + res == set() +} diff --git a/checks/cloud/google/compute/project_level_oslogin.go b/checks/cloud/google/compute/project_level_oslogin.go index 6fb75955..87f65e10 100755 --- a/checks/cloud/google/compute/project_level_oslogin.go +++ b/checks/cloud/google/compute/project_level_oslogin.go @@ -25,7 +25,8 @@ var CheckProjectLevelOslogin = rules.Register( Links: terraformProjectLevelOsloginLinks, RemediationMarkdown: terraformProjectLevelOsloginRemediationMarkdown, }, - Severity: severity.Medium, + Severity: severity.Medium, + Deprecated: true, }, func(s *state.State) (results scan.Results) { if s.Google.Compute.ProjectMetadata.Metadata.IsManaged() { diff --git a/checks/cloud/google/compute/project_level_oslogin.rego b/checks/cloud/google/compute/project_level_oslogin.rego new file mode 100644 index 00000000..d4d299ff --- /dev/null +++ b/checks/cloud/google/compute/project_level_oslogin.rego @@ -0,0 +1,36 @@ +# METADATA +# title: OS Login should be enabled at project level +# description: | +# OS Login automatically revokes the relevant SSH keys when an IAM user has their access revoked. +# scope: package +# schemas: +# - input: schema["cloud"] +# custom: +# id: AVD-GCP-0042 +# avd_id: AVD-GCP-0042 +# provider: google +# service: compute +# severity: MEDIUM +# short_code: project-level-oslogin +# recommended_action: Enable OS Login at project level +# input: +# selector: +# - type: cloud +# subtypes: +# - service: compute +# provider: google +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_project_metadata# +# good_examples: checks/cloud/google/compute/project_level_oslogin.tf.go +# bad_examples: checks/cloud/google/compute/project_level_oslogin.tf.go +package builtin.google.compute.google0042 + +import rego.v1 + +deny contains res if { + metadata := input.google.compute.projectmetadata + isManaged(metadata) + not metadata.enableoslogin.value + res := result.new("OS Login is disabled at project level.", metadata) +} diff --git a/checks/cloud/google/compute/project_level_oslogin_test.go b/checks/cloud/google/compute/project_level_oslogin_test.go deleted file mode 100644 index 6c16f198..00000000 --- a/checks/cloud/google/compute/project_level_oslogin_test.go +++ /dev/null @@ -1,61 +0,0 @@ -package compute - -import ( - "testing" - - trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/google/compute" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/stretchr/testify/assert" -) - -func TestCheckProjectLevelOslogin(t *testing.T) { - tests := []struct { - name string - input compute.Compute - expected bool - }{ - { - name: "Compute OS login disabled", - input: compute.Compute{ - ProjectMetadata: compute.ProjectMetadata{ - Metadata: trivyTypes.NewTestMetadata(), - EnableOSLogin: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), - }, - }, - expected: true, - }, - { - name: "Compute OS login enabled", - input: compute.Compute{ - ProjectMetadata: compute.ProjectMetadata{ - Metadata: trivyTypes.NewTestMetadata(), - EnableOSLogin: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), - }, - }, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.Google.Compute = test.input - results := CheckProjectLevelOslogin.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckProjectLevelOslogin.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/google/compute/project_level_oslogin_test.rego b/checks/cloud/google/compute/project_level_oslogin_test.rego new file mode 100644 index 00000000..e5d96aa0 --- /dev/null +++ b/checks/cloud/google/compute/project_level_oslogin_test.rego @@ -0,0 +1,30 @@ +package builtin.google.compute.google0042_test + +import rego.v1 + +import data.builtin.google.compute.google0042 as check +import data.lib.test + +test_deny_compute_os_login_disabled if { + inp := {"google": {"compute": {"projectmetadata": {"enableoslogin": {"value": false}}}}} + + res := check.deny with input as inp + count(res) == 1 +} + +test_allow_compute_os_login_enabled if { + inp := {"google": {"compute": {"projectmetadata": {"enableoslogin": {"value": true}}}}} + + res := check.deny with input as inp + res == set() +} + +test_allow_compute_os_login_is_not_managed if { + inp := {"google": {"compute": {"projectmetadata": { + "__defsec_metadata": {"managed": false}, + "enableoslogin": {"value": false}, + }}}} + + res := check.deny with input as inp + res == set() +} diff --git a/checks/cloud/google/compute/use_secure_tls_policy.go b/checks/cloud/google/compute/use_secure_tls_policy.go index 8f25934d..71592f6f 100755 --- a/checks/cloud/google/compute/use_secure_tls_policy.go +++ b/checks/cloud/google/compute/use_secure_tls_policy.go @@ -25,7 +25,8 @@ var CheckUseSecureTlsPolicy = rules.Register( Links: terraformUseSecureTlsPolicyLinks, RemediationMarkdown: terraformUseSecureTlsPolicyRemediationMarkdown, }, - Severity: severity.Critical, + Severity: severity.Critical, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, policy := range s.Google.Compute.SSLPolicies { diff --git a/checks/cloud/google/compute/use_secure_tls_policy.rego b/checks/cloud/google/compute/use_secure_tls_policy.rego new file mode 100644 index 00000000..123d42c4 --- /dev/null +++ b/checks/cloud/google/compute/use_secure_tls_policy.rego @@ -0,0 +1,37 @@ +# METADATA +# title: SSL policies should enforce secure versions of TLS +# description: | +# TLS versions prior to 1.2 are outdated and insecure. You should use 1.2 as aminimum version. +# scope: package +# schemas: +# - input: schema["cloud"] +# custom: +# id: AVD-GCP-0039 +# avd_id: AVD-GCP-0039 +# provider: google +# service: compute +# severity: CRITICAL +# short_code: use-secure-tls-policy +# recommended_action: Enforce a minimum TLS version of 1.2 +# input: +# selector: +# - type: cloud +# subtypes: +# - service: compute +# provider: google +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_ssl_policy#min_tls_version +# good_examples: checks/cloud/google/compute/use_secure_tls_policy.tf.go +# bad_examples: checks/cloud/google/compute/use_secure_tls_policy.tf.go +package builtin.google.compute.google0039 + +import rego.v1 + +tls_v_1_2 := "TLS_1_2" + +deny contains res if { + some policy in input.google.compute.sslpolicies + policy.minimumtlsversion.value != tls_v_1_2 + res := result.new("TLS policy does not specify a minimum of TLS 1.2", policy.minimumtlsversion) +} diff --git a/checks/cloud/google/compute/use_secure_tls_policy_test.go b/checks/cloud/google/compute/use_secure_tls_policy_test.go deleted file mode 100644 index 5da9f4ca..00000000 --- a/checks/cloud/google/compute/use_secure_tls_policy_test.go +++ /dev/null @@ -1,65 +0,0 @@ -package compute - -import ( - "testing" - - trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/google/compute" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/stretchr/testify/assert" -) - -func TestCheckUseSecureTlsPolicy(t *testing.T) { - tests := []struct { - name string - input compute.Compute - expected bool - }{ - { - name: "SSL policy minimum TLS version 1.0", - input: compute.Compute{ - SSLPolicies: []compute.SSLPolicy{ - { - Metadata: trivyTypes.NewTestMetadata(), - MinimumTLSVersion: trivyTypes.String("TLS_1_0", trivyTypes.NewTestMetadata()), - }, - }, - }, - expected: true, - }, - { - name: "SSL policy minimum TLS version 1.2", - input: compute.Compute{ - SSLPolicies: []compute.SSLPolicy{ - { - Metadata: trivyTypes.NewTestMetadata(), - MinimumTLSVersion: trivyTypes.String("TLS_1_2", trivyTypes.NewTestMetadata()), - }, - }, - }, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.Google.Compute = test.input - results := CheckUseSecureTlsPolicy.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckUseSecureTlsPolicy.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/google/compute/use_secure_tls_policy_test.rego b/checks/cloud/google/compute/use_secure_tls_policy_test.rego new file mode 100644 index 00000000..1e66d9a5 --- /dev/null +++ b/checks/cloud/google/compute/use_secure_tls_policy_test.rego @@ -0,0 +1,20 @@ +package builtin.google.compute.google0039_test + +import rego.v1 + +import data.builtin.google.compute.google0039 as check +import data.lib.test + +test_deny_ssl_policy_minimum_tls_version_is_1 if { + inp := {"google": {"compute": {"sslpolicies": [{"minimumtlsversion": {"value": "TLS_1_0"}}]}}} + + res := check.deny with input as inp + count(res) == 1 +} + +test_allow_ssl_policy_minimum_tls_version_is_1_2 if { + inp := {"google": {"compute": {"sslpolicies": [{"minimumtlsversion": {"value": check.tls_v_1_2}}]}}} + + res := check.deny with input as inp + res == set() +} diff --git a/checks/cloud/google/compute/vm_disk_encryption_customer_key.go b/checks/cloud/google/compute/vm_disk_encryption_customer_key.go index 6370e9cf..63ba66ad 100755 --- a/checks/cloud/google/compute/vm_disk_encryption_customer_key.go +++ b/checks/cloud/google/compute/vm_disk_encryption_customer_key.go @@ -25,7 +25,8 @@ var CheckVmDiskEncryptionCustomerKey = rules.Register( Links: terraformVmDiskEncryptionCustomerKeyLinks, RemediationMarkdown: terraformVmDiskEncryptionCustomerKeyRemediationMarkdown, }, - Severity: severity.Low, + Severity: severity.Low, + Deprecated: true, }, func(s *state.State) (results scan.Results) { for _, instance := range s.Google.Compute.Instances { diff --git a/checks/cloud/google/compute/vm_disk_encryption_customer_key.rego b/checks/cloud/google/compute/vm_disk_encryption_customer_key.rego new file mode 100644 index 00000000..75a84b27 --- /dev/null +++ b/checks/cloud/google/compute/vm_disk_encryption_customer_key.rego @@ -0,0 +1,47 @@ +# METADATA +# title: VM disks should be encrypted with Customer Supplied Encryption Keys +# description: | +# Using unmanaged keys makes rotation and general management difficult. +# scope: package +# schemas: +# - input: schema["cloud"] +# custom: +# id: AVD-GCP-0033 +# avd_id: AVD-GCP-0033 +# provider: google +# service: compute +# severity: LOW +# short_code: vm-disk-encryption-customer-key +# recommended_action: Use managed keys +# input: +# selector: +# - type: cloud +# subtypes: +# - service: compute +# provider: google +# terraform: +# links: +# - https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_instance#kms_key_self_link +# good_examples: checks/cloud/google/compute/vm_disk_encryption_customer_key.tf.go +# bad_examples: checks/cloud/google/compute/vm_disk_encryption_customer_key.tf.go +package builtin.google.compute.google0033 + +import rego.v1 + +deny contains res if { + some instance in input.google.compute.instances + disks := array.concat( + object.get(instance, "bootdisks", []), + object.get(instance, "attacheddisks", []), + ) + + some disk in disks + + not disk_is_encrypted(disk) + res := result.new( + "Instance disk encryption does not use a customer managed key.", + object.get(disk, ["encryption", "kmskeylink"], disk), + ) +} + +disk_is_encrypted(disk) := disk.encryption.kmskeylink.value != "" diff --git a/checks/cloud/google/compute/vm_disk_encryption_customer_key_test.go b/checks/cloud/google/compute/vm_disk_encryption_customer_key_test.go deleted file mode 100644 index 2bbcccdf..00000000 --- a/checks/cloud/google/compute/vm_disk_encryption_customer_key_test.go +++ /dev/null @@ -1,81 +0,0 @@ -package compute - -import ( - "testing" - - trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" - - "github.com/aquasecurity/trivy/pkg/iac/state" - - "github.com/aquasecurity/trivy/pkg/iac/providers/google/compute" - "github.com/aquasecurity/trivy/pkg/iac/scan" - - "github.com/stretchr/testify/assert" -) - -func TestCheckVmDiskEncryptionCustomerKey(t *testing.T) { - tests := []struct { - name string - input compute.Compute - expected bool - }{ - { - name: "Instance disk missing encryption key link", - input: compute.Compute{ - Instances: []compute.Instance{ - { - Metadata: trivyTypes.NewTestMetadata(), - BootDisks: []compute.Disk{ - { - Metadata: trivyTypes.NewTestMetadata(), - Encryption: compute.DiskEncryption{ - Metadata: trivyTypes.NewTestMetadata(), - KMSKeyLink: trivyTypes.String("", trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - }, - }, - expected: true, - }, - { - name: "Instance disk encryption key link provided", - input: compute.Compute{ - Instances: []compute.Instance{ - { - Metadata: trivyTypes.NewTestMetadata(), - AttachedDisks: []compute.Disk{ - { - Metadata: trivyTypes.NewTestMetadata(), - Encryption: compute.DiskEncryption{ - Metadata: trivyTypes.NewTestMetadata(), - KMSKeyLink: trivyTypes.String("kms-key-link", trivyTypes.NewTestMetadata()), - }, - }, - }, - }, - }, - }, - expected: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - var testState state.State - testState.Google.Compute = test.input - results := CheckVmDiskEncryptionCustomerKey.Evaluate(&testState) - var found bool - for _, result := range results { - if result.Status() == scan.StatusFailed && result.Rule().LongID() == CheckVmDiskEncryptionCustomerKey.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/google/compute/vm_disk_encryption_customer_key_test.rego b/checks/cloud/google/compute/vm_disk_encryption_customer_key_test.rego new file mode 100644 index 00000000..67dac348 --- /dev/null +++ b/checks/cloud/google/compute/vm_disk_encryption_customer_key_test.rego @@ -0,0 +1,30 @@ +package builtin.google.compute.google0033_test + +import rego.v1 + +import data.builtin.google.compute.google0033 as check +import data.lib.test + +test_deny_instance_boot_disk_is_not_encrypted if { + inp := {"google": {"compute": {"instances": [{"bootdisks": [{"encryption": {"kmskeylink": {"value": ""}}}]}]}}} + + res := check.deny with input as inp + count(res) == 1 +} + +test_deny_instance_attached_disk_is_not_encrypted if { + inp := {"google": {"compute": {"instances": [{"attacheddisks": [{"encryption": {}}]}]}}} + + res := check.deny with input as inp + count(res) == 1 +} + +test_allow_instance_disks_is_encrypted if { + inp := {"google": {"compute": { + "bootdisks": [{"encryption": {"kmskeylink": {"value": "kms-key-link"}}}], + "attacheddisks": [{"disk": {"encryption": {"kmskeylink": {"value": "kms-key-link"}}}}], + }}} + + res := check.deny with input as inp + res == set() +} diff --git a/test/rego/google_compute_test.go b/test/rego/google_compute_test.go new file mode 100644 index 00000000..7c89e6e5 --- /dev/null +++ b/test/rego/google_compute_test.go @@ -0,0 +1,666 @@ +package test + +import ( + "github.com/aquasecurity/trivy/pkg/iac/providers/google" + "github.com/aquasecurity/trivy/pkg/iac/providers/google/compute" + "github.com/aquasecurity/trivy/pkg/iac/state" + trivyTypes "github.com/aquasecurity/trivy/pkg/iac/types" +) + +func init() { + addTests(googleComputeTestCases) +} + +var googleComputeTestCases = testCases{ + "AVD-GCP-0034": { + { + name: "Disk missing KMS key link", + input: state.State{Google: google.Google{Compute: compute.Compute{ + Disks: []compute.Disk{ + { + Metadata: trivyTypes.NewTestMetadata(), + Encryption: compute.DiskEncryption{ + Metadata: trivyTypes.NewTestMetadata(), + KMSKeyLink: trivyTypes.String("", trivyTypes.NewTestMetadata()), + }, + }, + }, + }}}, + expected: true, + }, + { + name: "Disk with KMS key link provided", + input: state.State{Google: google.Google{Compute: compute.Compute{ + Disks: []compute.Disk{ + { + Metadata: trivyTypes.NewTestMetadata(), + Encryption: compute.DiskEncryption{ + Metadata: trivyTypes.NewTestMetadata(), + KMSKeyLink: trivyTypes.String("kms-key-link", trivyTypes.NewTestMetadata()), + }, + }, + }, + }}}, + expected: false, + }, + }, + "AVD-GCP-0037": { + { + name: "Disk with plaintext encryption key", + input: state.State{Google: google.Google{Compute: compute.Compute{ + Disks: []compute.Disk{ + { + Metadata: trivyTypes.NewTestMetadata(), + Encryption: compute.DiskEncryption{ + Metadata: trivyTypes.NewTestMetadata(), + RawKey: trivyTypes.Bytes([]byte("b2ggbm8gdGhpcyBpcyBiYWQ"), trivyTypes.NewTestMetadata()), + }, + }, + }, + }}}, + expected: true, + }, + { + name: "Instance disk with plaintext encryption key", + input: state.State{Google: google.Google{Compute: compute.Compute{ + Instances: []compute.Instance{ + { + Metadata: trivyTypes.NewTestMetadata(), + BootDisks: []compute.Disk{ + { + Metadata: trivyTypes.NewTestMetadata(), + Encryption: compute.DiskEncryption{ + Metadata: trivyTypes.NewTestMetadata(), + RawKey: trivyTypes.Bytes([]byte("b2ggbm8gdGhpcyBpcyBiYWQ"), trivyTypes.NewTestMetadata()), + }, + }, + }, + }, + }, + }}}, + expected: true, + }, + { + name: "Disks with no plaintext encryption keys", + input: state.State{Google: google.Google{Compute: compute.Compute{ + Disks: []compute.Disk{ + { + Metadata: trivyTypes.NewTestMetadata(), + Encryption: compute.DiskEncryption{ + Metadata: trivyTypes.NewTestMetadata(), + RawKey: trivyTypes.Bytes([]byte(""), trivyTypes.NewTestMetadata()), + }, + }, + }, + Instances: []compute.Instance{ + { + Metadata: trivyTypes.NewTestMetadata(), + BootDisks: []compute.Disk{ + { + Metadata: trivyTypes.NewTestMetadata(), + Encryption: compute.DiskEncryption{ + Metadata: trivyTypes.NewTestMetadata(), + RawKey: trivyTypes.Bytes([]byte(""), trivyTypes.NewTestMetadata()), + }, + }, + }, + AttachedDisks: []compute.Disk{ + { + Metadata: trivyTypes.NewTestMetadata(), + Encryption: compute.DiskEncryption{ + Metadata: trivyTypes.NewTestMetadata(), + RawKey: trivyTypes.Bytes([]byte(""), trivyTypes.NewTestMetadata()), + }, + }, + }, + }, + }, + }}}, + expected: false, + }, + }, + "AVD-GCP-0045": { + { + name: "Instance shielded VM integrity monitoring disabled", + input: state.State{Google: google.Google{Compute: compute.Compute{ + Instances: []compute.Instance{ + { + Metadata: trivyTypes.NewTestMetadata(), + ShieldedVM: compute.ShieldedVMConfig{ + Metadata: trivyTypes.NewTestMetadata(), + IntegrityMonitoringEnabled: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), + }, + }, + }, + }}}, + expected: true, + }, + { + name: "Instance shielded VM integrity monitoring enabled", + input: state.State{Google: google.Google{Compute: compute.Compute{ + Instances: []compute.Instance{ + { + Metadata: trivyTypes.NewTestMetadata(), + ShieldedVM: compute.ShieldedVMConfig{ + Metadata: trivyTypes.NewTestMetadata(), + IntegrityMonitoringEnabled: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), + }, + }, + }, + }}}, + expected: false, + }, + }, + "AVD-GCP-0067": { + { + name: "Instance shielded VM secure boot disabled", + input: state.State{Google: google.Google{Compute: compute.Compute{ + Instances: []compute.Instance{ + { + Metadata: trivyTypes.NewTestMetadata(), + ShieldedVM: compute.ShieldedVMConfig{ + Metadata: trivyTypes.NewTestMetadata(), + SecureBootEnabled: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), + }, + }, + }, + }}}, + expected: true, + }, + { + name: "Instance shielded VM secure boot enabled", + input: state.State{Google: google.Google{Compute: compute.Compute{ + Instances: []compute.Instance{ + { + Metadata: trivyTypes.NewTestMetadata(), + ShieldedVM: compute.ShieldedVMConfig{ + Metadata: trivyTypes.NewTestMetadata(), + SecureBootEnabled: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), + }, + }, + }, + }}}, + expected: false, + }, + }, + "AVD-GCP-0041": { + { + name: "Instance shielded VM VTPM disabled", + input: state.State{Google: google.Google{Compute: compute.Compute{ + Instances: []compute.Instance{ + { + Metadata: trivyTypes.NewTestMetadata(), + ShieldedVM: compute.ShieldedVMConfig{ + Metadata: trivyTypes.NewTestMetadata(), + VTPMEnabled: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), + }, + }, + }, + }}}, + expected: true, + }, + { + name: "Instance shielded VM VTPM enabled", + input: state.State{Google: google.Google{Compute: compute.Compute{ + Instances: []compute.Instance{ + { + Metadata: trivyTypes.NewTestMetadata(), + ShieldedVM: compute.ShieldedVMConfig{ + Metadata: trivyTypes.NewTestMetadata(), + VTPMEnabled: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), + }, + }, + }, + }}}, + expected: false, + }, + }, + "AVD-GCP-0029": { + { + name: "Subnetwork VPC flow logs disabled", + input: state.State{Google: google.Google{Compute: compute.Compute{ + Networks: []compute.Network{ + { + Metadata: trivyTypes.NewTestMetadata(), + Subnetworks: []compute.SubNetwork{ + { + Metadata: trivyTypes.NewTestMetadata(), + EnableFlowLogs: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), + }, + }, + }, + }, + }}}, + expected: true, + }, + { + name: "Subnetwork VPC flow logs enabled", + input: state.State{Google: google.Google{Compute: compute.Compute{ + Networks: []compute.Network{ + { + Metadata: trivyTypes.NewTestMetadata(), + Subnetworks: []compute.SubNetwork{ + { + Metadata: trivyTypes.NewTestMetadata(), + EnableFlowLogs: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), + }, + }, + }, + }, + }}}, + expected: false, + }, + { + name: "Proxy-only subnets and logs disabled", + input: state.State{Google: google.Google{Compute: compute.Compute{ + Networks: []compute.Network{ + { + Metadata: trivyTypes.NewTestMetadata(), + Subnetworks: []compute.SubNetwork{ + { + Metadata: trivyTypes.NewTestMetadata(), + EnableFlowLogs: trivyTypes.BoolDefault(false, trivyTypes.NewTestMetadata()), + Purpose: trivyTypes.String("REGIONAL_MANAGED_PROXY", trivyTypes.NewTestMetadata()), + }, + }, + }, + }, + }}}, + expected: false, + }, + }, + "AVD-GCP-0044": { + { + name: "Instance service account not specified", + input: state.State{Google: google.Google{Compute: compute.Compute{ + Instances: []compute.Instance{ + { + Metadata: trivyTypes.NewTestMetadata(), + ServiceAccount: compute.ServiceAccount{ + Metadata: trivyTypes.NewTestMetadata(), + Email: trivyTypes.String("", trivyTypes.NewTestMetadata()), + IsDefault: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), + }, + }, + }, + }}}, + expected: true, + }, + { + name: "Instance service account using the default email", + input: state.State{Google: google.Google{Compute: compute.Compute{ + Instances: []compute.Instance{ + { + Metadata: trivyTypes.NewTestMetadata(), + ServiceAccount: compute.ServiceAccount{ + Metadata: trivyTypes.NewTestMetadata(), + Email: trivyTypes.String("1234567890-compute@developer.gserviceaccount.com", trivyTypes.NewTestMetadata()), + IsDefault: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), + }, + }, + }, + }}}, + expected: true, + }, + { + name: "Instance service account with email provided", + input: state.State{Google: google.Google{Compute: compute.Compute{ + Instances: []compute.Instance{ + { + Metadata: trivyTypes.NewTestMetadata(), + ServiceAccount: compute.ServiceAccount{ + Metadata: trivyTypes.NewTestMetadata(), + Email: trivyTypes.String("proper@email.com", trivyTypes.NewTestMetadata()), + IsDefault: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), + }, + }, + }, + }}}, + expected: false, + }, + }, + "AVD-GCP-0043": { + { + name: "Instance IP forwarding enabled", + input: state.State{Google: google.Google{Compute: compute.Compute{ + Instances: []compute.Instance{ + { + Metadata: trivyTypes.NewTestMetadata(), + CanIPForward: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), + }, + }, + }}}, + expected: true, + }, + { + name: "Instance IP forwarding disabled", + input: state.State{Google: google.Google{Compute: compute.Compute{ + Instances: []compute.Instance{ + { + Metadata: trivyTypes.NewTestMetadata(), + CanIPForward: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), + }, + }, + }}}, + expected: false, + }, + }, + "AVD-GCP-0036": { + { + name: "Instance OS login disabled", + input: state.State{Google: google.Google{Compute: compute.Compute{ + Instances: []compute.Instance{ + { + Metadata: trivyTypes.NewTestMetadata(), + OSLoginEnabled: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), + }, + }, + }}}, + expected: true, + }, + { + name: "Instance OS login enabled", + input: state.State{Google: google.Google{Compute: compute.Compute{ + Instances: []compute.Instance{ + { + Metadata: trivyTypes.NewTestMetadata(), + OSLoginEnabled: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), + }, + }, + }}}, + expected: false, + }, + }, + "AVD-GCP-0030": { + { + name: "Instance project level SSH keys blocked", + input: state.State{Google: google.Google{Compute: compute.Compute{ + Instances: []compute.Instance{ + { + Metadata: trivyTypes.NewTestMetadata(), + EnableProjectSSHKeyBlocking: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), + }, + }, + }}}, + expected: true, + }, + { + name: "Instance project level SSH keys allowed", + input: state.State{Google: google.Google{Compute: compute.Compute{ + Instances: []compute.Instance{ + { + Metadata: trivyTypes.NewTestMetadata(), + EnableProjectSSHKeyBlocking: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), + }, + }, + }}}, + expected: false, + }, + }, + "AVD-GCP-0035": { + { + name: "Firewall egress rule with multiple public destination addresses", + input: state.State{Google: google.Google{Compute: compute.Compute{ + Networks: []compute.Network{ + { + Metadata: trivyTypes.NewTestMetadata(), + Firewall: &compute.Firewall{ + Metadata: trivyTypes.NewTestMetadata(), + EgressRules: []compute.EgressRule{ + { + Metadata: trivyTypes.NewTestMetadata(), + FirewallRule: compute.FirewallRule{ + Metadata: trivyTypes.NewTestMetadata(), + IsAllow: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), + Enforced: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), + }, + DestinationRanges: []trivyTypes.StringValue{ + trivyTypes.String("0.0.0.0/0", trivyTypes.NewTestMetadata()), + trivyTypes.String("1.2.3.4/32", trivyTypes.NewTestMetadata()), + }, + }, + }, + }, + }, + }, + }}}, + expected: true, + }, + { + name: "Firewall egress rule with public destination address", + input: state.State{Google: google.Google{Compute: compute.Compute{ + Networks: []compute.Network{ + { + Metadata: trivyTypes.NewTestMetadata(), + Firewall: &compute.Firewall{ + Metadata: trivyTypes.NewTestMetadata(), + EgressRules: []compute.EgressRule{ + { + Metadata: trivyTypes.NewTestMetadata(), + FirewallRule: compute.FirewallRule{ + Metadata: trivyTypes.NewTestMetadata(), + IsAllow: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), + Enforced: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), + }, + DestinationRanges: []trivyTypes.StringValue{ + trivyTypes.String("1.2.3.4/32", trivyTypes.NewTestMetadata()), + }, + }, + }, + }, + }, + }, + }}}, + expected: false, + }, + }, + "AVD-GCP-0027": { + { + name: "Firewall ingress rule with multiple public source addresses", + input: state.State{Google: google.Google{Compute: compute.Compute{ + Networks: []compute.Network{ + { + Metadata: trivyTypes.NewTestMetadata(), + Firewall: &compute.Firewall{ + Metadata: trivyTypes.NewTestMetadata(), + IngressRules: []compute.IngressRule{ + { + Metadata: trivyTypes.NewTestMetadata(), + FirewallRule: compute.FirewallRule{ + Metadata: trivyTypes.NewTestMetadata(), + IsAllow: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), + Enforced: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), + }, + SourceRanges: []trivyTypes.StringValue{ + trivyTypes.String("0.0.0.0/0", trivyTypes.NewTestMetadata()), + trivyTypes.String("1.2.3.4/32", trivyTypes.NewTestMetadata()), + }, + }, + }, + }, + }, + }, + }}}, + expected: true, + }, + { + name: "Firewall ingress rule with public source address", + input: state.State{Google: google.Google{Compute: compute.Compute{ + Networks: []compute.Network{ + { + Metadata: trivyTypes.NewTestMetadata(), + Firewall: &compute.Firewall{ + Metadata: trivyTypes.NewTestMetadata(), + IngressRules: []compute.IngressRule{ + { + Metadata: trivyTypes.NewTestMetadata(), + FirewallRule: compute.FirewallRule{ + Metadata: trivyTypes.NewTestMetadata(), + IsAllow: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), + Enforced: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), + }, + SourceRanges: []trivyTypes.StringValue{ + trivyTypes.String("1.2.3.4/32", trivyTypes.NewTestMetadata()), + }, + }, + }, + }, + }, + }, + }}}, + expected: false, + }, + }, + "AVD-GCP-0031": { + { + name: "Network interface with public IP", + input: state.State{Google: google.Google{Compute: compute.Compute{ + Instances: []compute.Instance{ + { + Metadata: trivyTypes.NewTestMetadata(), + NetworkInterfaces: []compute.NetworkInterface{ + { + Metadata: trivyTypes.NewTestMetadata(), + HasPublicIP: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), + }, + }, + }, + }, + }}}, + expected: true, + }, + { + name: "Network interface without public IP", + input: state.State{Google: google.Google{Compute: compute.Compute{ + Instances: []compute.Instance{ + { + Metadata: trivyTypes.NewTestMetadata(), + NetworkInterfaces: []compute.NetworkInterface{ + { + Metadata: trivyTypes.NewTestMetadata(), + HasPublicIP: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), + }, + }, + }, + }, + }}}, + expected: false, + }, + }, + "AVD-GCP-0032": { + { + name: "Instance serial port enabled", + input: state.State{Google: google.Google{Compute: compute.Compute{ + Instances: []compute.Instance{ + { + Metadata: trivyTypes.NewTestMetadata(), + EnableSerialPort: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), + }, + }, + }}}, + expected: true, + }, + { + name: "Instance serial port disabled", + input: state.State{Google: google.Google{Compute: compute.Compute{ + Instances: []compute.Instance{ + { + Metadata: trivyTypes.NewTestMetadata(), + EnableSerialPort: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), + }, + }, + }}}, + expected: false, + }, + }, + "AVD-GCP-0042": { + { + name: "Compute OS login disabled", + input: state.State{Google: google.Google{Compute: compute.Compute{ + ProjectMetadata: compute.ProjectMetadata{ + Metadata: trivyTypes.NewTestMetadata(), + EnableOSLogin: trivyTypes.Bool(false, trivyTypes.NewTestMetadata()), + }, + }}}, + expected: true, + }, + { + name: "Compute OS login enabled", + input: state.State{Google: google.Google{Compute: compute.Compute{ + ProjectMetadata: compute.ProjectMetadata{ + Metadata: trivyTypes.NewTestMetadata(), + EnableOSLogin: trivyTypes.Bool(true, trivyTypes.NewTestMetadata()), + }, + }}}, + expected: false, + }, + }, + "AVD-GCP-0039": { + { + name: "SSL policy minimum TLS version 1.0", + input: state.State{Google: google.Google{Compute: compute.Compute{ + SSLPolicies: []compute.SSLPolicy{ + { + Metadata: trivyTypes.NewTestMetadata(), + MinimumTLSVersion: trivyTypes.String("TLS_1_0", trivyTypes.NewTestMetadata()), + }, + }, + }}}, + expected: true, + }, + { + name: "SSL policy minimum TLS version 1.2", + input: state.State{Google: google.Google{Compute: compute.Compute{ + SSLPolicies: []compute.SSLPolicy{ + { + Metadata: trivyTypes.NewTestMetadata(), + MinimumTLSVersion: trivyTypes.String("TLS_1_2", trivyTypes.NewTestMetadata()), + }, + }, + }}}, + expected: false, + }, + }, + "AVD-GCP-0033": { + { + name: "Instance disk missing encryption key link", + input: state.State{Google: google.Google{Compute: compute.Compute{ + Instances: []compute.Instance{ + { + Metadata: trivyTypes.NewTestMetadata(), + BootDisks: []compute.Disk{ + { + Metadata: trivyTypes.NewTestMetadata(), + Encryption: compute.DiskEncryption{ + Metadata: trivyTypes.NewTestMetadata(), + KMSKeyLink: trivyTypes.String("", trivyTypes.NewTestMetadata()), + }, + }, + }, + }, + }, + }}}, + expected: true, + }, + { + name: "Instance disk encryption key link provided", + input: state.State{Google: google.Google{Compute: compute.Compute{ + Instances: []compute.Instance{ + { + Metadata: trivyTypes.NewTestMetadata(), + AttachedDisks: []compute.Disk{ + { + Metadata: trivyTypes.NewTestMetadata(), + Encryption: compute.DiskEncryption{ + Metadata: trivyTypes.NewTestMetadata(), + KMSKeyLink: trivyTypes.String("kms-key-link", trivyTypes.NewTestMetadata()), + }, + }, + }, + }, + }, + }}}, + expected: false, + }, + }, +}