From 72e9b82b223f7d0e09b0b36227826df45a0e9cab Mon Sep 17 00:00:00 2001 From: Antero Silva Date: Mon, 9 Mar 2026 17:53:12 +0000 Subject: [PATCH 1/3] feat(queries): add 12 Beta security queries across AWS/Azure/GCP/K8s/Dockerfile Add new queries covering security gaps identified against best practices: AWS Terraform: - Beta - Lambda Function URL Without Authentication (HIGH, CWE-306) - Beta - Backup Vault Without KMS Key (MEDIUM, CWE-311) - Beta - Transfer Family Server Publicly Accessible (HIGH, CWE-668) Azure Terraform: - Beta - AKS Defender Profile Disabled (MEDIUM, CWE-778) - Beta - Service Bus Namespace Without CMK (LOW, CWE-311) - Beta - Machine Learning Workspace Without CMK (MEDIUM, CWE-311) GCP Terraform: - Beta - Cloud Run Service Allows Unauthenticated Access (HIGH, CWE-306) - Beta - Artifact Registry Repository Public Access (HIGH, CWE-284) - Beta - Secret Manager Secret Without CMEK (MEDIUM, CWE-311) Kubernetes: - Beta - Container Seccomp Profile Not Set (MEDIUM, CWE-250) - Beta - Ingress Without TLS (HIGH, CWE-319) Dockerfile: - Beta - ENV Instruction Has Secret (HIGH, CWE-798) Each query includes query.rego, metadata.json, and positive/negative test fixtures. Co-Authored-By: Claude Sonnet 4.6 --- .../env_instruction_has_secret/metadata.json | 13 ++++++ .../env_instruction_has_secret/query.rego | 34 +++++++++++++++ .../test/negative.dockerfile | 6 +++ .../test/positive.dockerfile | 5 +++ .../test/positive_expected_result.json | 20 +++++++++ .../metadata.json | 13 ++++++ .../query.rego | 37 ++++++++++++++++ .../test/negative1.yaml | 12 ++++++ .../test/negative2.yaml | 11 +++++ .../test/positive1.yaml | 10 +++++ .../test/positive_expected_result.json | 8 ++++ .../k8s/ingress_without_tls/metadata.json | 13 ++++++ .../k8s/ingress_without_tls/query.rego | 40 +++++++++++++++++ .../ingress_without_tls/test/negative1.yaml | 20 +++++++++ .../ingress_without_tls/test/positive1.yaml | 16 +++++++ .../test/positive_expected_result.json | 8 ++++ .../metadata.json | 13 ++++++ .../backup_vault_without_kms_key/query.rego | 22 ++++++++++ .../test/negative1.tf | 4 ++ .../test/positive1.tf | 3 ++ .../test/positive_expected_result.json | 8 ++++ .../metadata.json | 13 ++++++ .../query.rego | 22 ++++++++++ .../test/negative1.tf | 4 ++ .../test/positive1.tf | 4 ++ .../test/positive_expected_result.json | 8 ++++ .../metadata.json | 13 ++++++ .../query.rego | 23 ++++++++++ .../test/negative1.tf | 7 +++ .../test/positive1.tf | 4 ++ .../test/positive2.tf | 3 ++ .../test/positive_expected_result.json | 14 ++++++ .../metadata.json | 13 ++++++ .../aks_defender_profile_disabled/query.rego | 22 ++++++++++ .../test/negative1.tf | 17 ++++++++ .../test/positive1.tf | 14 ++++++ .../test/positive_expected_result.json | 8 ++++ .../metadata.json | 13 ++++++ .../query.rego | 22 ++++++++++ .../test/negative1.tf | 15 +++++++ .../test/positive1.tf | 11 +++++ .../test/positive_expected_result.json | 8 ++++ .../metadata.json | 13 ++++++ .../query.rego | 22 ++++++++++ .../test/negative1.tf | 10 +++++ .../test/positive1.tf | 6 +++ .../test/positive_expected_result.json | 8 ++++ .../metadata.json | 13 ++++++ .../query.rego | 43 +++++++++++++++++++ .../test/negative1.tf | 6 +++ .../test/positive1.tf | 6 +++ .../test/positive2.tf | 6 +++ .../test/positive_expected_result.json | 14 ++++++ .../metadata.json | 13 ++++++ .../query.rego | 40 +++++++++++++++++ .../test/negative1.tf | 6 +++ .../test/positive1.tf | 6 +++ .../test/positive2.tf | 6 +++ .../test/positive_expected_result.json | 14 ++++++ .../metadata.json | 13 ++++++ .../query.rego | 33 ++++++++++++++ .../test/negative1.tf | 13 ++++++ .../test/positive1.tf | 6 +++ .../test/positive2.tf | 10 +++++ .../test/positive_expected_result.json | 14 ++++++ 65 files changed, 895 insertions(+) create mode 100644 assets/queries/dockerfile/env_instruction_has_secret/metadata.json create mode 100644 assets/queries/dockerfile/env_instruction_has_secret/query.rego create mode 100644 assets/queries/dockerfile/env_instruction_has_secret/test/negative.dockerfile create mode 100644 assets/queries/dockerfile/env_instruction_has_secret/test/positive.dockerfile create mode 100644 assets/queries/dockerfile/env_instruction_has_secret/test/positive_expected_result.json create mode 100644 assets/queries/k8s/container_seccomp_profile_not_set/metadata.json create mode 100644 assets/queries/k8s/container_seccomp_profile_not_set/query.rego create mode 100644 assets/queries/k8s/container_seccomp_profile_not_set/test/negative1.yaml create mode 100644 assets/queries/k8s/container_seccomp_profile_not_set/test/negative2.yaml create mode 100644 assets/queries/k8s/container_seccomp_profile_not_set/test/positive1.yaml create mode 100644 assets/queries/k8s/container_seccomp_profile_not_set/test/positive_expected_result.json create mode 100644 assets/queries/k8s/ingress_without_tls/metadata.json create mode 100644 assets/queries/k8s/ingress_without_tls/query.rego create mode 100644 assets/queries/k8s/ingress_without_tls/test/negative1.yaml create mode 100644 assets/queries/k8s/ingress_without_tls/test/positive1.yaml create mode 100644 assets/queries/k8s/ingress_without_tls/test/positive_expected_result.json create mode 100644 assets/queries/terraform/aws/backup_vault_without_kms_key/metadata.json create mode 100644 assets/queries/terraform/aws/backup_vault_without_kms_key/query.rego create mode 100644 assets/queries/terraform/aws/backup_vault_without_kms_key/test/negative1.tf create mode 100644 assets/queries/terraform/aws/backup_vault_without_kms_key/test/positive1.tf create mode 100644 assets/queries/terraform/aws/backup_vault_without_kms_key/test/positive_expected_result.json create mode 100644 assets/queries/terraform/aws/lambda_function_url_without_authentication/metadata.json create mode 100644 assets/queries/terraform/aws/lambda_function_url_without_authentication/query.rego create mode 100644 assets/queries/terraform/aws/lambda_function_url_without_authentication/test/negative1.tf create mode 100644 assets/queries/terraform/aws/lambda_function_url_without_authentication/test/positive1.tf create mode 100644 assets/queries/terraform/aws/lambda_function_url_without_authentication/test/positive_expected_result.json create mode 100644 assets/queries/terraform/aws/transfer_family_server_publicly_accessible/metadata.json create mode 100644 assets/queries/terraform/aws/transfer_family_server_publicly_accessible/query.rego create mode 100644 assets/queries/terraform/aws/transfer_family_server_publicly_accessible/test/negative1.tf create mode 100644 assets/queries/terraform/aws/transfer_family_server_publicly_accessible/test/positive1.tf create mode 100644 assets/queries/terraform/aws/transfer_family_server_publicly_accessible/test/positive2.tf create mode 100644 assets/queries/terraform/aws/transfer_family_server_publicly_accessible/test/positive_expected_result.json create mode 100644 assets/queries/terraform/azure/aks_defender_profile_disabled/metadata.json create mode 100644 assets/queries/terraform/azure/aks_defender_profile_disabled/query.rego create mode 100644 assets/queries/terraform/azure/aks_defender_profile_disabled/test/negative1.tf create mode 100644 assets/queries/terraform/azure/aks_defender_profile_disabled/test/positive1.tf create mode 100644 assets/queries/terraform/azure/aks_defender_profile_disabled/test/positive_expected_result.json create mode 100644 assets/queries/terraform/azure/machine_learning_workspace_without_cmk/metadata.json create mode 100644 assets/queries/terraform/azure/machine_learning_workspace_without_cmk/query.rego create mode 100644 assets/queries/terraform/azure/machine_learning_workspace_without_cmk/test/negative1.tf create mode 100644 assets/queries/terraform/azure/machine_learning_workspace_without_cmk/test/positive1.tf create mode 100644 assets/queries/terraform/azure/machine_learning_workspace_without_cmk/test/positive_expected_result.json create mode 100644 assets/queries/terraform/azure/service_bus_namespace_without_cmk/metadata.json create mode 100644 assets/queries/terraform/azure/service_bus_namespace_without_cmk/query.rego create mode 100644 assets/queries/terraform/azure/service_bus_namespace_without_cmk/test/negative1.tf create mode 100644 assets/queries/terraform/azure/service_bus_namespace_without_cmk/test/positive1.tf create mode 100644 assets/queries/terraform/azure/service_bus_namespace_without_cmk/test/positive_expected_result.json create mode 100644 assets/queries/terraform/gcp/artifact_registry_repository_public_access/metadata.json create mode 100644 assets/queries/terraform/gcp/artifact_registry_repository_public_access/query.rego create mode 100644 assets/queries/terraform/gcp/artifact_registry_repository_public_access/test/negative1.tf create mode 100644 assets/queries/terraform/gcp/artifact_registry_repository_public_access/test/positive1.tf create mode 100644 assets/queries/terraform/gcp/artifact_registry_repository_public_access/test/positive2.tf create mode 100644 assets/queries/terraform/gcp/artifact_registry_repository_public_access/test/positive_expected_result.json create mode 100644 assets/queries/terraform/gcp/cloud_run_service_allows_unauthenticated/metadata.json create mode 100644 assets/queries/terraform/gcp/cloud_run_service_allows_unauthenticated/query.rego create mode 100644 assets/queries/terraform/gcp/cloud_run_service_allows_unauthenticated/test/negative1.tf create mode 100644 assets/queries/terraform/gcp/cloud_run_service_allows_unauthenticated/test/positive1.tf create mode 100644 assets/queries/terraform/gcp/cloud_run_service_allows_unauthenticated/test/positive2.tf create mode 100644 assets/queries/terraform/gcp/cloud_run_service_allows_unauthenticated/test/positive_expected_result.json create mode 100644 assets/queries/terraform/gcp/secret_manager_secret_without_cmek/metadata.json create mode 100644 assets/queries/terraform/gcp/secret_manager_secret_without_cmek/query.rego create mode 100644 assets/queries/terraform/gcp/secret_manager_secret_without_cmek/test/negative1.tf create mode 100644 assets/queries/terraform/gcp/secret_manager_secret_without_cmek/test/positive1.tf create mode 100644 assets/queries/terraform/gcp/secret_manager_secret_without_cmek/test/positive2.tf create mode 100644 assets/queries/terraform/gcp/secret_manager_secret_without_cmek/test/positive_expected_result.json diff --git a/assets/queries/dockerfile/env_instruction_has_secret/metadata.json b/assets/queries/dockerfile/env_instruction_has_secret/metadata.json new file mode 100644 index 00000000000..5a1db5890cd --- /dev/null +++ b/assets/queries/dockerfile/env_instruction_has_secret/metadata.json @@ -0,0 +1,13 @@ +{ + "id": "781e75f7-bf43-4ab7-bae7-a08ad6607af3", + "queryName": "Beta - ENV Instruction Contains Secret", + "severity": "HIGH", + "category": "Secret Management", + "descriptionText": "Dockerfile ENV instructions should not store secrets, passwords, API keys or tokens as plaintext values. These values are embedded in every image layer and visible in 'docker inspect' output, risking credential exposure. Use build-time secrets (BuildKit) or a secrets manager instead.", + "descriptionUrl": "https://docs.docker.com/build/building/secrets/", + "platform": "Dockerfile", + "descriptionID": "7abccf53", + "cloudProvider": "common", + "cwe": "798", + "riskScore": "8.5" +} \ No newline at end of file diff --git a/assets/queries/dockerfile/env_instruction_has_secret/query.rego b/assets/queries/dockerfile/env_instruction_has_secret/query.rego new file mode 100644 index 00000000000..8ba27a3a8f7 --- /dev/null +++ b/assets/queries/dockerfile/env_instruction_has_secret/query.rego @@ -0,0 +1,34 @@ +package Cx + +secret_env_patterns := { + "password", "passwd", "pwd", "secret", "api_key", "apikey", + "token", "private_key", "auth_key", "access_key", "secret_key", + "encryption_key", "db_pass", "database_password", "app_secret" +} + +CxPolicy[result] { + resource := input.document[i].command[name][_] + resource.Cmd == "env" + + env_entry := resource.Value[_] + parts := split(env_entry, "=") + count(parts) >= 2 + + env_key := lower(parts[0]) + env_val := concat("=", array.slice(parts, 1, count(parts))) + + contains(env_key, secret_env_patterns[_]) + env_val != "" + not startswith(env_val, "$") + not startswith(env_val, "\${") + not env_val == "changeme" + not env_val == "placeholder" + + result := { + "documentId": input.document[i].id, + "searchKey": sprintf("FROM={{%s}}.ENV {{%s}}", [name, parts[0]]), + "issueType": "IncorrectValue", + "keyExpectedValue": sprintf("ENV '%s' should not contain a hardcoded secret value; use BuildKit secrets or a runtime secrets manager", [parts[0]]), + "keyActualValue": sprintf("ENV '%s' appears to contain a hardcoded secret value", [parts[0]]), + } +} diff --git a/assets/queries/dockerfile/env_instruction_has_secret/test/negative.dockerfile b/assets/queries/dockerfile/env_instruction_has_secret/test/negative.dockerfile new file mode 100644 index 00000000000..da77762a8ea --- /dev/null +++ b/assets/queries/dockerfile/env_instruction_has_secret/test/negative.dockerfile @@ -0,0 +1,6 @@ +FROM python:3.11-slim +ENV APP_SECRET=${APP_SECRET} +ENV DATABASE_PASSWORD=$DB_PASS +ENV LOG_LEVEL=info +ENV PORT=8080 +RUN pip install flask diff --git a/assets/queries/dockerfile/env_instruction_has_secret/test/positive.dockerfile b/assets/queries/dockerfile/env_instruction_has_secret/test/positive.dockerfile new file mode 100644 index 00000000000..afeb54f42bf --- /dev/null +++ b/assets/queries/dockerfile/env_instruction_has_secret/test/positive.dockerfile @@ -0,0 +1,5 @@ +FROM python:3.11-slim +ENV APP_SECRET=my_super_secret_value +ENV DATABASE_PASSWORD=s3cr3t123 +ENV API_KEY=AKIAIOSFODNN7EXAMPLE +RUN pip install flask diff --git a/assets/queries/dockerfile/env_instruction_has_secret/test/positive_expected_result.json b/assets/queries/dockerfile/env_instruction_has_secret/test/positive_expected_result.json new file mode 100644 index 00000000000..7165086eb9f --- /dev/null +++ b/assets/queries/dockerfile/env_instruction_has_secret/test/positive_expected_result.json @@ -0,0 +1,20 @@ +[ + { + "queryName": "Beta - ENV Instruction Contains Secret", + "severity": "HIGH", + "line": 2, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Beta - ENV Instruction Contains Secret", + "severity": "HIGH", + "line": 3, + "fileName": "positive.dockerfile" + }, + { + "queryName": "Beta - ENV Instruction Contains Secret", + "severity": "HIGH", + "line": 4, + "fileName": "positive.dockerfile" + } +] \ No newline at end of file diff --git a/assets/queries/k8s/container_seccomp_profile_not_set/metadata.json b/assets/queries/k8s/container_seccomp_profile_not_set/metadata.json new file mode 100644 index 00000000000..23da50a48b1 --- /dev/null +++ b/assets/queries/k8s/container_seccomp_profile_not_set/metadata.json @@ -0,0 +1,13 @@ +{ + "id": "ab2bf277-cd73-451c-8a34-70ea5c7ee258", + "queryName": "Beta - Container Seccomp Profile Not Set", + "severity": "MEDIUM", + "category": "Insecure Configurations", + "descriptionText": "Containers should have a seccomp profile set to 'RuntimeDefault' or 'Localhost' in their security context. Without it, the container has an unconfined syscall profile, increasing the attack surface if a container breakout occurs.", + "descriptionUrl": "https://kubernetes.io/docs/tutorials/security/seccomp/", + "platform": "Kubernetes", + "descriptionID": "30a7f7cc", + "cloudProvider": "common", + "cwe": "250", + "riskScore": "5.5" +} \ No newline at end of file diff --git a/assets/queries/k8s/container_seccomp_profile_not_set/query.rego b/assets/queries/k8s/container_seccomp_profile_not_set/query.rego new file mode 100644 index 00000000000..667ac9d0f13 --- /dev/null +++ b/assets/queries/k8s/container_seccomp_profile_not_set/query.rego @@ -0,0 +1,37 @@ +package Cx + +import data.generic.k8s as k8sLib +import data.generic.common as common_lib + +valid_types := {"RuntimeDefault", "Localhost"} + +CxPolicy[result] { + document := input.document[i] + specInfo := k8sLib.getSpecInfo(document) + metadata := document.metadata + types := {"initContainers", "containers"} + container := specInfo.spec[types[x]][j] + + not has_valid_seccomp(container, specInfo.spec) + + result := { + "documentId": document.id, + "resourceType": document.kind, + "resourceName": metadata.name, + "searchKey": sprintf("metadata.name={{%s}}.%s.%s.name={{%s}}.securityContext", [metadata.name, specInfo.path, types[x], container.name]), + "issueType": "MissingAttribute", + "keyExpectedValue": sprintf("Container '%s' should have seccompProfile.type set to 'RuntimeDefault' or 'Localhost'", [container.name]), + "keyActualValue": sprintf("Container '%s' does not have a valid seccompProfile configured", [container.name]), + "searchLine": common_lib.build_search_line(split(specInfo.path, "."), [types[x], j, "securityContext"]), + } +} + +# seccomp defined at container level +has_valid_seccomp(container, _) { + container.securityContext.seccompProfile.type == valid_types[_] +} + +# seccomp defined at pod spec level +has_valid_seccomp(_, spec) { + spec.securityContext.seccompProfile.type == valid_types[_] +} diff --git a/assets/queries/k8s/container_seccomp_profile_not_set/test/negative1.yaml b/assets/queries/k8s/container_seccomp_profile_not_set/test/negative1.yaml new file mode 100644 index 00000000000..26891bd9d43 --- /dev/null +++ b/assets/queries/k8s/container_seccomp_profile_not_set/test/negative1.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Pod +metadata: + name: negative-pod-container-level +spec: + containers: + - name: app + image: nginx:1.21 + securityContext: + seccompProfile: + type: RuntimeDefault + runAsNonRoot: true diff --git a/assets/queries/k8s/container_seccomp_profile_not_set/test/negative2.yaml b/assets/queries/k8s/container_seccomp_profile_not_set/test/negative2.yaml new file mode 100644 index 00000000000..220a2c2ed19 --- /dev/null +++ b/assets/queries/k8s/container_seccomp_profile_not_set/test/negative2.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Pod +metadata: + name: negative-pod-pod-level +spec: + securityContext: + seccompProfile: + type: RuntimeDefault + containers: + - name: app + image: nginx:1.21 diff --git a/assets/queries/k8s/container_seccomp_profile_not_set/test/positive1.yaml b/assets/queries/k8s/container_seccomp_profile_not_set/test/positive1.yaml new file mode 100644 index 00000000000..76d9c9ecda7 --- /dev/null +++ b/assets/queries/k8s/container_seccomp_profile_not_set/test/positive1.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Pod +metadata: + name: positive-pod +spec: + containers: + - name: app + image: nginx:1.21 + securityContext: + runAsNonRoot: true diff --git a/assets/queries/k8s/container_seccomp_profile_not_set/test/positive_expected_result.json b/assets/queries/k8s/container_seccomp_profile_not_set/test/positive_expected_result.json new file mode 100644 index 00000000000..a02c9575500 --- /dev/null +++ b/assets/queries/k8s/container_seccomp_profile_not_set/test/positive_expected_result.json @@ -0,0 +1,8 @@ +[ + { + "queryName": "Beta - Container Seccomp Profile Not Set", + "severity": "MEDIUM", + "line": 7, + "fileName": "positive1.yaml" + } +] \ No newline at end of file diff --git a/assets/queries/k8s/ingress_without_tls/metadata.json b/assets/queries/k8s/ingress_without_tls/metadata.json new file mode 100644 index 00000000000..af9fe6f2814 --- /dev/null +++ b/assets/queries/k8s/ingress_without_tls/metadata.json @@ -0,0 +1,13 @@ +{ + "id": "c81c6c42-6c17-48c4-bde6-ea242e76f275", + "queryName": "Beta - Ingress Without TLS", + "severity": "HIGH", + "category": "Networking and Firewall", + "descriptionText": "Kubernetes Ingress resources should configure TLS to encrypt traffic between clients and the ingress controller. Without a 'tls' block, traffic is served over plain HTTP, exposing sensitive data in transit.", + "descriptionUrl": "https://kubernetes.io/docs/concepts/services-networking/ingress/#tls", + "platform": "Kubernetes", + "descriptionID": "0dcbf4a2", + "cloudProvider": "common", + "cwe": "319", + "riskScore": "7.5" +} \ No newline at end of file diff --git a/assets/queries/k8s/ingress_without_tls/query.rego b/assets/queries/k8s/ingress_without_tls/query.rego new file mode 100644 index 00000000000..c62669402d6 --- /dev/null +++ b/assets/queries/k8s/ingress_without_tls/query.rego @@ -0,0 +1,40 @@ +package Cx + +import data.generic.common as common_lib + +CxPolicy[result] { + document := input.document[i] + document.kind == "Ingress" + metadata := document.metadata + not common_lib.valid_key(document.spec, "tls") + + result := { + "documentId": document.id, + "resourceType": document.kind, + "resourceName": metadata.name, + "searchKey": sprintf("metadata.name={{%s}}.spec", [metadata.name]), + "issueType": "MissingAttribute", + "keyExpectedValue": sprintf("Ingress '%s' should have 'spec.tls' configured", [metadata.name]), + "keyActualValue": sprintf("Ingress '%s' does not have 'spec.tls' configured; traffic is served over HTTP", [metadata.name]), + "searchLine": common_lib.build_search_line(["spec"], []), + } +} + +CxPolicy[result] { + document := input.document[i] + document.kind == "Ingress" + metadata := document.metadata + tls_entries := document.spec.tls + count(tls_entries) == 0 + + result := { + "documentId": document.id, + "resourceType": document.kind, + "resourceName": metadata.name, + "searchKey": sprintf("metadata.name={{%s}}.spec.tls", [metadata.name]), + "issueType": "IncorrectValue", + "keyExpectedValue": sprintf("Ingress '%s' should have at least one TLS entry in 'spec.tls'", [metadata.name]), + "keyActualValue": sprintf("Ingress '%s' has an empty 'spec.tls' list", [metadata.name]), + "searchLine": common_lib.build_search_line(["spec", "tls"], []), + } +} diff --git a/assets/queries/k8s/ingress_without_tls/test/negative1.yaml b/assets/queries/k8s/ingress_without_tls/test/negative1.yaml new file mode 100644 index 00000000000..e7618d4a41a --- /dev/null +++ b/assets/queries/k8s/ingress_without_tls/test/negative1.yaml @@ -0,0 +1,20 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: negative-ingress +spec: + tls: + - hosts: + - example.com + secretName: example-tls + rules: + - host: example.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: my-service + port: + number: 80 diff --git a/assets/queries/k8s/ingress_without_tls/test/positive1.yaml b/assets/queries/k8s/ingress_without_tls/test/positive1.yaml new file mode 100644 index 00000000000..d204c9d6c94 --- /dev/null +++ b/assets/queries/k8s/ingress_without_tls/test/positive1.yaml @@ -0,0 +1,16 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: positive-ingress +spec: + rules: + - host: example.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: my-service + port: + number: 80 diff --git a/assets/queries/k8s/ingress_without_tls/test/positive_expected_result.json b/assets/queries/k8s/ingress_without_tls/test/positive_expected_result.json new file mode 100644 index 00000000000..386e92aba3f --- /dev/null +++ b/assets/queries/k8s/ingress_without_tls/test/positive_expected_result.json @@ -0,0 +1,8 @@ +[ + { + "queryName": "Beta - Ingress Without TLS", + "severity": "HIGH", + "line": 5, + "fileName": "positive1.yaml" + } +] \ No newline at end of file diff --git a/assets/queries/terraform/aws/backup_vault_without_kms_key/metadata.json b/assets/queries/terraform/aws/backup_vault_without_kms_key/metadata.json new file mode 100644 index 00000000000..33dbb51be4d --- /dev/null +++ b/assets/queries/terraform/aws/backup_vault_without_kms_key/metadata.json @@ -0,0 +1,13 @@ +{ + "id": "f979bf87-34fd-4bab-a5b1-530268a581d5", + "queryName": "Beta - Backup Vault Without KMS Key", + "severity": "MEDIUM", + "category": "Encryption", + "descriptionText": "AWS Backup Vaults should be encrypted using a customer-managed KMS key. Without a 'kms_key_arn', backups are protected only by the default AWS-managed key, offering no additional control over key rotation or access.", + "descriptionUrl": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/backup_vault#kms_key_arn", + "platform": "Terraform", + "descriptionID": "6df8a84c", + "cloudProvider": "aws", + "cwe": "311", + "riskScore": "5.5" +} \ No newline at end of file diff --git a/assets/queries/terraform/aws/backup_vault_without_kms_key/query.rego b/assets/queries/terraform/aws/backup_vault_without_kms_key/query.rego new file mode 100644 index 00000000000..09d60fa3fcf --- /dev/null +++ b/assets/queries/terraform/aws/backup_vault_without_kms_key/query.rego @@ -0,0 +1,22 @@ +package Cx + +import data.generic.common as common_lib +import data.generic.terraform as tf_lib + +CxPolicy[result] { + resource := input.document[i].resource.aws_backup_vault[name] + not common_lib.valid_key(resource, "kms_key_arn") + + result := { + "documentId": input.document[i].id, + "resourceType": "aws_backup_vault", + "resourceName": tf_lib.get_resource_name(resource, name), + "searchKey": sprintf("aws_backup_vault[%s]", [name]), + "issueType": "MissingAttribute", + "keyExpectedValue": sprintf("'aws_backup_vault[%s].kms_key_arn' should be defined", [name]), + "keyActualValue": sprintf("'aws_backup_vault[%s].kms_key_arn' is not defined; vault uses AWS-managed key", [name]), + "searchLine": common_lib.build_search_line(["resource", "aws_backup_vault", name], []), + "remediation": "kms_key_arn = aws_kms_key.example.arn", + "remediationType": "addition", + } +} diff --git a/assets/queries/terraform/aws/backup_vault_without_kms_key/test/negative1.tf b/assets/queries/terraform/aws/backup_vault_without_kms_key/test/negative1.tf new file mode 100644 index 00000000000..20b595131c4 --- /dev/null +++ b/assets/queries/terraform/aws/backup_vault_without_kms_key/test/negative1.tf @@ -0,0 +1,4 @@ +resource "aws_backup_vault" "negative1" { + name = "example_vault" + kms_key_arn = aws_kms_key.example.arn +} diff --git a/assets/queries/terraform/aws/backup_vault_without_kms_key/test/positive1.tf b/assets/queries/terraform/aws/backup_vault_without_kms_key/test/positive1.tf new file mode 100644 index 00000000000..a94ff9f997e --- /dev/null +++ b/assets/queries/terraform/aws/backup_vault_without_kms_key/test/positive1.tf @@ -0,0 +1,3 @@ +resource "aws_backup_vault" "positive1" { + name = "example_vault" +} diff --git a/assets/queries/terraform/aws/backup_vault_without_kms_key/test/positive_expected_result.json b/assets/queries/terraform/aws/backup_vault_without_kms_key/test/positive_expected_result.json new file mode 100644 index 00000000000..c9cdd590efe --- /dev/null +++ b/assets/queries/terraform/aws/backup_vault_without_kms_key/test/positive_expected_result.json @@ -0,0 +1,8 @@ +[ + { + "queryName": "Beta - Backup Vault Without KMS Key", + "severity": "MEDIUM", + "line": 1, + "fileName": "positive1.tf" + } +] \ No newline at end of file diff --git a/assets/queries/terraform/aws/lambda_function_url_without_authentication/metadata.json b/assets/queries/terraform/aws/lambda_function_url_without_authentication/metadata.json new file mode 100644 index 00000000000..20fe36e724d --- /dev/null +++ b/assets/queries/terraform/aws/lambda_function_url_without_authentication/metadata.json @@ -0,0 +1,13 @@ +{ + "id": "0cbdf8fb-83cd-418f-918f-de86df0621b6", + "queryName": "Beta - Lambda Function URL Without Authentication", + "severity": "HIGH", + "category": "Access Control", + "descriptionText": "AWS Lambda Function URLs should require authentication. Setting 'authorization_type' to 'NONE' allows unauthenticated public invocation of the function, exposing it to abuse or data exfiltration.", + "descriptionUrl": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_function_url#authorization_type", + "platform": "Terraform", + "descriptionID": "1c4bf6dc", + "cloudProvider": "aws", + "cwe": "306", + "riskScore": "8.1" +} \ No newline at end of file diff --git a/assets/queries/terraform/aws/lambda_function_url_without_authentication/query.rego b/assets/queries/terraform/aws/lambda_function_url_without_authentication/query.rego new file mode 100644 index 00000000000..cc4db8b5dd7 --- /dev/null +++ b/assets/queries/terraform/aws/lambda_function_url_without_authentication/query.rego @@ -0,0 +1,22 @@ +package Cx + +import data.generic.common as common_lib +import data.generic.terraform as tf_lib + +CxPolicy[result] { + resource := input.document[i].resource.aws_lambda_function_url[name] + resource.authorization_type == "NONE" + + result := { + "documentId": input.document[i].id, + "resourceType": "aws_lambda_function_url", + "resourceName": tf_lib.get_resource_name(resource, name), + "searchKey": sprintf("aws_lambda_function_url[%s].authorization_type", [name]), + "issueType": "IncorrectValue", + "keyExpectedValue": sprintf("'aws_lambda_function_url[%s].authorization_type' should not be 'NONE'", [name]), + "keyActualValue": sprintf("'aws_lambda_function_url[%s].authorization_type' is 'NONE', allowing unauthenticated invocations", [name]), + "searchLine": common_lib.build_search_line(["resource", "aws_lambda_function_url", name, "authorization_type"], []), + "remediation": "authorization_type = \"AWS_IAM\"", + "remediationType": "replacement", + } +} diff --git a/assets/queries/terraform/aws/lambda_function_url_without_authentication/test/negative1.tf b/assets/queries/terraform/aws/lambda_function_url_without_authentication/test/negative1.tf new file mode 100644 index 00000000000..9577d0039e0 --- /dev/null +++ b/assets/queries/terraform/aws/lambda_function_url_without_authentication/test/negative1.tf @@ -0,0 +1,4 @@ +resource "aws_lambda_function_url" "negative1" { + function_name = aws_lambda_function.example.function_name + authorization_type = "AWS_IAM" +} diff --git a/assets/queries/terraform/aws/lambda_function_url_without_authentication/test/positive1.tf b/assets/queries/terraform/aws/lambda_function_url_without_authentication/test/positive1.tf new file mode 100644 index 00000000000..095e2667520 --- /dev/null +++ b/assets/queries/terraform/aws/lambda_function_url_without_authentication/test/positive1.tf @@ -0,0 +1,4 @@ +resource "aws_lambda_function_url" "positive1" { + function_name = aws_lambda_function.example.function_name + authorization_type = "NONE" +} diff --git a/assets/queries/terraform/aws/lambda_function_url_without_authentication/test/positive_expected_result.json b/assets/queries/terraform/aws/lambda_function_url_without_authentication/test/positive_expected_result.json new file mode 100644 index 00000000000..c51760d4f23 --- /dev/null +++ b/assets/queries/terraform/aws/lambda_function_url_without_authentication/test/positive_expected_result.json @@ -0,0 +1,8 @@ +[ + { + "queryName": "Beta - Lambda Function URL Without Authentication", + "severity": "HIGH", + "line": 3, + "fileName": "positive1.tf" + } +] \ No newline at end of file diff --git a/assets/queries/terraform/aws/transfer_family_server_publicly_accessible/metadata.json b/assets/queries/terraform/aws/transfer_family_server_publicly_accessible/metadata.json new file mode 100644 index 00000000000..79ef7dfce2a --- /dev/null +++ b/assets/queries/terraform/aws/transfer_family_server_publicly_accessible/metadata.json @@ -0,0 +1,13 @@ +{ + "id": "ef088173-6ce6-46b6-af2e-708ab73737c1", + "queryName": "Beta - Transfer Family Server Publicly Accessible", + "severity": "HIGH", + "category": "Networking and Firewall", + "descriptionText": "AWS Transfer Family servers should use VPC endpoint type instead of PUBLIC to restrict access and reduce the attack surface. Public endpoints expose the file transfer service directly to the internet.", + "descriptionUrl": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/transfer_server#endpoint_type", + "platform": "Terraform", + "descriptionID": "1d9081ec", + "cloudProvider": "aws", + "cwe": "668", + "riskScore": "7.5" +} \ No newline at end of file diff --git a/assets/queries/terraform/aws/transfer_family_server_publicly_accessible/query.rego b/assets/queries/terraform/aws/transfer_family_server_publicly_accessible/query.rego new file mode 100644 index 00000000000..e5aba02eb10 --- /dev/null +++ b/assets/queries/terraform/aws/transfer_family_server_publicly_accessible/query.rego @@ -0,0 +1,23 @@ +package Cx + +import data.generic.common as common_lib +import data.generic.terraform as tf_lib + +CxPolicy[result] { + resource := input.document[i].resource.aws_transfer_server[name] + endpoint := object.get(resource, "endpoint_type", "PUBLIC") + endpoint != "VPC" + + result := { + "documentId": input.document[i].id, + "resourceType": "aws_transfer_server", + "resourceName": tf_lib.get_resource_name(resource, name), + "searchKey": sprintf("aws_transfer_server[%s].endpoint_type", [name]), + "issueType": "IncorrectValue", + "keyExpectedValue": sprintf("'aws_transfer_server[%s].endpoint_type' should be 'VPC'", [name]), + "keyActualValue": sprintf("'aws_transfer_server[%s].endpoint_type' is '%s'", [name, endpoint]), + "searchLine": common_lib.build_search_line(["resource", "aws_transfer_server", name, "endpoint_type"], []), + "remediation": "endpoint_type = \"VPC\"", + "remediationType": "replacement", + } +} diff --git a/assets/queries/terraform/aws/transfer_family_server_publicly_accessible/test/negative1.tf b/assets/queries/terraform/aws/transfer_family_server_publicly_accessible/test/negative1.tf new file mode 100644 index 00000000000..f9a413622d3 --- /dev/null +++ b/assets/queries/terraform/aws/transfer_family_server_publicly_accessible/test/negative1.tf @@ -0,0 +1,7 @@ +resource "aws_transfer_server" "negative1" { + identity_provider_type = "SERVICE_MANAGED" + endpoint_type = "VPC" + endpoint_details { + vpc_id = aws_vpc.example.id + } +} diff --git a/assets/queries/terraform/aws/transfer_family_server_publicly_accessible/test/positive1.tf b/assets/queries/terraform/aws/transfer_family_server_publicly_accessible/test/positive1.tf new file mode 100644 index 00000000000..2728be2f100 --- /dev/null +++ b/assets/queries/terraform/aws/transfer_family_server_publicly_accessible/test/positive1.tf @@ -0,0 +1,4 @@ +resource "aws_transfer_server" "positive1" { + identity_provider_type = "SERVICE_MANAGED" + endpoint_type = "PUBLIC" +} diff --git a/assets/queries/terraform/aws/transfer_family_server_publicly_accessible/test/positive2.tf b/assets/queries/terraform/aws/transfer_family_server_publicly_accessible/test/positive2.tf new file mode 100644 index 00000000000..7a01ae4b32e --- /dev/null +++ b/assets/queries/terraform/aws/transfer_family_server_publicly_accessible/test/positive2.tf @@ -0,0 +1,3 @@ +resource "aws_transfer_server" "positive2" { + identity_provider_type = "SERVICE_MANAGED" +} diff --git a/assets/queries/terraform/aws/transfer_family_server_publicly_accessible/test/positive_expected_result.json b/assets/queries/terraform/aws/transfer_family_server_publicly_accessible/test/positive_expected_result.json new file mode 100644 index 00000000000..95c458d90f7 --- /dev/null +++ b/assets/queries/terraform/aws/transfer_family_server_publicly_accessible/test/positive_expected_result.json @@ -0,0 +1,14 @@ +[ + { + "queryName": "Beta - Transfer Family Server Publicly Accessible", + "severity": "HIGH", + "line": 3, + "fileName": "positive1.tf" + }, + { + "queryName": "Beta - Transfer Family Server Publicly Accessible", + "severity": "HIGH", + "line": 1, + "fileName": "positive2.tf" + } +] \ No newline at end of file diff --git a/assets/queries/terraform/azure/aks_defender_profile_disabled/metadata.json b/assets/queries/terraform/azure/aks_defender_profile_disabled/metadata.json new file mode 100644 index 00000000000..ca0c025277d --- /dev/null +++ b/assets/queries/terraform/azure/aks_defender_profile_disabled/metadata.json @@ -0,0 +1,13 @@ +{ + "id": "beb57138-c9ee-421a-8332-6224ab3a2a6a", + "queryName": "Beta - AKS Defender Profile Disabled", + "severity": "MEDIUM", + "category": "Observability", + "descriptionText": "Azure Kubernetes Service clusters should have Microsoft Defender enabled via the 'microsoft_defender' block. Without it, threat detection for the cluster workloads is not active, leaving security incidents undetected.", + "descriptionUrl": "https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/kubernetes_cluster#microsoft_defender", + "platform": "Terraform", + "descriptionID": "f8f4f141", + "cloudProvider": "azure", + "cwe": "778", + "riskScore": "5.3" +} \ No newline at end of file diff --git a/assets/queries/terraform/azure/aks_defender_profile_disabled/query.rego b/assets/queries/terraform/azure/aks_defender_profile_disabled/query.rego new file mode 100644 index 00000000000..884771f88a8 --- /dev/null +++ b/assets/queries/terraform/azure/aks_defender_profile_disabled/query.rego @@ -0,0 +1,22 @@ +package Cx + +import data.generic.common as common_lib +import data.generic.terraform as tf_lib + +CxPolicy[result] { + resource := input.document[i].resource.azurerm_kubernetes_cluster[name] + not common_lib.valid_key(resource, "microsoft_defender") + + result := { + "documentId": input.document[i].id, + "resourceType": "azurerm_kubernetes_cluster", + "resourceName": tf_lib.get_resource_name(resource, name), + "searchKey": sprintf("azurerm_kubernetes_cluster[%s]", [name]), + "issueType": "MissingAttribute", + "keyExpectedValue": sprintf("'azurerm_kubernetes_cluster[%s].microsoft_defender' should be configured", [name]), + "keyActualValue": sprintf("'azurerm_kubernetes_cluster[%s].microsoft_defender' is not defined", [name]), + "searchLine": common_lib.build_search_line(["resource", "azurerm_kubernetes_cluster", name], []), + "remediation": "microsoft_defender {\n log_analytics_workspace_id = azurerm_log_analytics_workspace.example.id\n }", + "remediationType": "addition", + } +} diff --git a/assets/queries/terraform/azure/aks_defender_profile_disabled/test/negative1.tf b/assets/queries/terraform/azure/aks_defender_profile_disabled/test/negative1.tf new file mode 100644 index 00000000000..45757fd915d --- /dev/null +++ b/assets/queries/terraform/azure/aks_defender_profile_disabled/test/negative1.tf @@ -0,0 +1,17 @@ +resource "azurerm_kubernetes_cluster" "negative1" { + name = "example-aks" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + dns_prefix = "exampleaks" + default_node_pool { + name = "default" + node_count = 1 + vm_size = "Standard_D2_v2" + } + identity { + type = "SystemAssigned" + } + microsoft_defender { + log_analytics_workspace_id = azurerm_log_analytics_workspace.example.id + } +} diff --git a/assets/queries/terraform/azure/aks_defender_profile_disabled/test/positive1.tf b/assets/queries/terraform/azure/aks_defender_profile_disabled/test/positive1.tf new file mode 100644 index 00000000000..04157c1e49d --- /dev/null +++ b/assets/queries/terraform/azure/aks_defender_profile_disabled/test/positive1.tf @@ -0,0 +1,14 @@ +resource "azurerm_kubernetes_cluster" "positive1" { + name = "example-aks" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + dns_prefix = "exampleaks" + default_node_pool { + name = "default" + node_count = 1 + vm_size = "Standard_D2_v2" + } + identity { + type = "SystemAssigned" + } +} diff --git a/assets/queries/terraform/azure/aks_defender_profile_disabled/test/positive_expected_result.json b/assets/queries/terraform/azure/aks_defender_profile_disabled/test/positive_expected_result.json new file mode 100644 index 00000000000..398ac164f1e --- /dev/null +++ b/assets/queries/terraform/azure/aks_defender_profile_disabled/test/positive_expected_result.json @@ -0,0 +1,8 @@ +[ + { + "queryName": "Beta - AKS Defender Profile Disabled", + "severity": "MEDIUM", + "line": 1, + "fileName": "positive1.tf" + } +] \ No newline at end of file diff --git a/assets/queries/terraform/azure/machine_learning_workspace_without_cmk/metadata.json b/assets/queries/terraform/azure/machine_learning_workspace_without_cmk/metadata.json new file mode 100644 index 00000000000..917f45a6b1e --- /dev/null +++ b/assets/queries/terraform/azure/machine_learning_workspace_without_cmk/metadata.json @@ -0,0 +1,13 @@ +{ + "id": "34d7d523-ed2c-43f4-9c20-ca22d07a0f56", + "queryName": "Beta - Machine Learning Workspace Without CMK", + "severity": "MEDIUM", + "category": "Encryption", + "descriptionText": "Azure Machine Learning workspaces store model artifacts, datasets, and experiment logs. These should be encrypted with a customer-managed key using the 'encryption' block to meet data residency and compliance requirements.", + "descriptionUrl": "https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/machine_learning_workspace#encryption", + "platform": "Terraform", + "descriptionID": "b489594d", + "cloudProvider": "azure", + "cwe": "311", + "riskScore": "5.0" +} \ No newline at end of file diff --git a/assets/queries/terraform/azure/machine_learning_workspace_without_cmk/query.rego b/assets/queries/terraform/azure/machine_learning_workspace_without_cmk/query.rego new file mode 100644 index 00000000000..fda7e6e583d --- /dev/null +++ b/assets/queries/terraform/azure/machine_learning_workspace_without_cmk/query.rego @@ -0,0 +1,22 @@ +package Cx + +import data.generic.common as common_lib +import data.generic.terraform as tf_lib + +CxPolicy[result] { + resource := input.document[i].resource.azurerm_machine_learning_workspace[name] + not common_lib.valid_key(resource, "encryption") + + result := { + "documentId": input.document[i].id, + "resourceType": "azurerm_machine_learning_workspace", + "resourceName": tf_lib.get_resource_name(resource, name), + "searchKey": sprintf("azurerm_machine_learning_workspace[%s]", [name]), + "issueType": "MissingAttribute", + "keyExpectedValue": sprintf("'azurerm_machine_learning_workspace[%s].encryption' should be defined with a customer-managed key", [name]), + "keyActualValue": sprintf("'azurerm_machine_learning_workspace[%s].encryption' is not defined; using Microsoft-managed key", [name]), + "searchLine": common_lib.build_search_line(["resource", "azurerm_machine_learning_workspace", name], []), + "remediation": "encryption {\n key_vault_id = azurerm_key_vault.example.id\n key_id = azurerm_key_vault_key.example.id\n }", + "remediationType": "addition", + } +} diff --git a/assets/queries/terraform/azure/machine_learning_workspace_without_cmk/test/negative1.tf b/assets/queries/terraform/azure/machine_learning_workspace_without_cmk/test/negative1.tf new file mode 100644 index 00000000000..1786dcab8ad --- /dev/null +++ b/assets/queries/terraform/azure/machine_learning_workspace_without_cmk/test/negative1.tf @@ -0,0 +1,15 @@ +resource "azurerm_machine_learning_workspace" "negative1" { + name = "example-mlworkspace" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + application_insights_id = azurerm_application_insights.example.id + key_vault_id = azurerm_key_vault.example.id + storage_account_id = azurerm_storage_account.example.id + identity { + type = "SystemAssigned" + } + encryption { + key_vault_id = azurerm_key_vault.example.id + key_id = azurerm_key_vault_key.example.id + } +} diff --git a/assets/queries/terraform/azure/machine_learning_workspace_without_cmk/test/positive1.tf b/assets/queries/terraform/azure/machine_learning_workspace_without_cmk/test/positive1.tf new file mode 100644 index 00000000000..10c248ff30e --- /dev/null +++ b/assets/queries/terraform/azure/machine_learning_workspace_without_cmk/test/positive1.tf @@ -0,0 +1,11 @@ +resource "azurerm_machine_learning_workspace" "positive1" { + name = "example-mlworkspace" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + application_insights_id = azurerm_application_insights.example.id + key_vault_id = azurerm_key_vault.example.id + storage_account_id = azurerm_storage_account.example.id + identity { + type = "SystemAssigned" + } +} diff --git a/assets/queries/terraform/azure/machine_learning_workspace_without_cmk/test/positive_expected_result.json b/assets/queries/terraform/azure/machine_learning_workspace_without_cmk/test/positive_expected_result.json new file mode 100644 index 00000000000..18cc9f05af5 --- /dev/null +++ b/assets/queries/terraform/azure/machine_learning_workspace_without_cmk/test/positive_expected_result.json @@ -0,0 +1,8 @@ +[ + { + "queryName": "Beta - Machine Learning Workspace Without CMK", + "severity": "MEDIUM", + "line": 1, + "fileName": "positive1.tf" + } +] \ No newline at end of file diff --git a/assets/queries/terraform/azure/service_bus_namespace_without_cmk/metadata.json b/assets/queries/terraform/azure/service_bus_namespace_without_cmk/metadata.json new file mode 100644 index 00000000000..9e4d6ba56c9 --- /dev/null +++ b/assets/queries/terraform/azure/service_bus_namespace_without_cmk/metadata.json @@ -0,0 +1,13 @@ +{ + "id": "8a800912-0bcb-441c-aa95-306d3d9a7bdd", + "queryName": "Beta - Service Bus Namespace Without CMK", + "severity": "LOW", + "category": "Encryption", + "descriptionText": "Azure Service Bus namespaces should be encrypted with a customer-managed key (CMK) for enhanced control over encryption keys. Without a 'customer_managed_key' block, Microsoft-managed keys are used with no customer key management.", + "descriptionUrl": "https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/servicebus_namespace#customer_managed_key", + "platform": "Terraform", + "descriptionID": "39e5eb1f", + "cloudProvider": "azure", + "cwe": "311", + "riskScore": "3.0" +} \ No newline at end of file diff --git a/assets/queries/terraform/azure/service_bus_namespace_without_cmk/query.rego b/assets/queries/terraform/azure/service_bus_namespace_without_cmk/query.rego new file mode 100644 index 00000000000..2068a9ebab8 --- /dev/null +++ b/assets/queries/terraform/azure/service_bus_namespace_without_cmk/query.rego @@ -0,0 +1,22 @@ +package Cx + +import data.generic.common as common_lib +import data.generic.terraform as tf_lib + +CxPolicy[result] { + resource := input.document[i].resource.azurerm_servicebus_namespace[name] + not common_lib.valid_key(resource, "customer_managed_key") + + result := { + "documentId": input.document[i].id, + "resourceType": "azurerm_servicebus_namespace", + "resourceName": tf_lib.get_resource_name(resource, name), + "searchKey": sprintf("azurerm_servicebus_namespace[%s]", [name]), + "issueType": "MissingAttribute", + "keyExpectedValue": sprintf("'azurerm_servicebus_namespace[%s].customer_managed_key' should be defined", [name]), + "keyActualValue": sprintf("'azurerm_servicebus_namespace[%s].customer_managed_key' is not defined; using Microsoft-managed key", [name]), + "searchLine": common_lib.build_search_line(["resource", "azurerm_servicebus_namespace", name], []), + "remediation": "customer_managed_key {\n key_vault_key_id = azurerm_key_vault_key.example.id\n identity_id = azurerm_user_assigned_identity.example.id\n }", + "remediationType": "addition", + } +} diff --git a/assets/queries/terraform/azure/service_bus_namespace_without_cmk/test/negative1.tf b/assets/queries/terraform/azure/service_bus_namespace_without_cmk/test/negative1.tf new file mode 100644 index 00000000000..2c535a3a434 --- /dev/null +++ b/assets/queries/terraform/azure/service_bus_namespace_without_cmk/test/negative1.tf @@ -0,0 +1,10 @@ +resource "azurerm_servicebus_namespace" "negative1" { + name = "example-sbnamespace" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + sku = "Premium" + customer_managed_key { + key_vault_key_id = azurerm_key_vault_key.example.id + identity_id = azurerm_user_assigned_identity.example.id + } +} diff --git a/assets/queries/terraform/azure/service_bus_namespace_without_cmk/test/positive1.tf b/assets/queries/terraform/azure/service_bus_namespace_without_cmk/test/positive1.tf new file mode 100644 index 00000000000..b3824d1445c --- /dev/null +++ b/assets/queries/terraform/azure/service_bus_namespace_without_cmk/test/positive1.tf @@ -0,0 +1,6 @@ +resource "azurerm_servicebus_namespace" "positive1" { + name = "example-sbnamespace" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + sku = "Premium" +} diff --git a/assets/queries/terraform/azure/service_bus_namespace_without_cmk/test/positive_expected_result.json b/assets/queries/terraform/azure/service_bus_namespace_without_cmk/test/positive_expected_result.json new file mode 100644 index 00000000000..c0ffa818ee0 --- /dev/null +++ b/assets/queries/terraform/azure/service_bus_namespace_without_cmk/test/positive_expected_result.json @@ -0,0 +1,8 @@ +[ + { + "queryName": "Beta - Service Bus Namespace Without CMK", + "severity": "LOW", + "line": 1, + "fileName": "positive1.tf" + } +] \ No newline at end of file diff --git a/assets/queries/terraform/gcp/artifact_registry_repository_public_access/metadata.json b/assets/queries/terraform/gcp/artifact_registry_repository_public_access/metadata.json new file mode 100644 index 00000000000..6a084aa512b --- /dev/null +++ b/assets/queries/terraform/gcp/artifact_registry_repository_public_access/metadata.json @@ -0,0 +1,13 @@ +{ + "id": "664c673f-71f2-4a7f-a54a-2efa25e8567f", + "queryName": "Beta - Artifact Registry Repository With Public Access", + "severity": "HIGH", + "category": "Access Control", + "descriptionText": "GCP Artifact Registry repositories should not grant access to 'allUsers' or 'allAuthenticatedUsers'. Public access allows anyone to read or push container images and packages, risking supply chain attacks or data leakage.", + "descriptionUrl": "https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/artifact_registry_repository_iam_member", + "platform": "Terraform", + "descriptionID": "3116634b", + "cloudProvider": "gcp", + "cwe": "284", + "riskScore": "8.2" +} \ No newline at end of file diff --git a/assets/queries/terraform/gcp/artifact_registry_repository_public_access/query.rego b/assets/queries/terraform/gcp/artifact_registry_repository_public_access/query.rego new file mode 100644 index 00000000000..535e8340eb0 --- /dev/null +++ b/assets/queries/terraform/gcp/artifact_registry_repository_public_access/query.rego @@ -0,0 +1,43 @@ +package Cx + +import data.generic.common as common_lib +import data.generic.terraform as tf_lib + +public_members := {"allUsers", "allAuthenticatedUsers"} + +CxPolicy[result] { + resource := input.document[i].resource.google_artifact_registry_repository_iam_member[name] + resource.member == public_members[_] + + result := { + "documentId": input.document[i].id, + "resourceType": "google_artifact_registry_repository_iam_member", + "resourceName": tf_lib.get_resource_name(resource, name), + "searchKey": sprintf("google_artifact_registry_repository_iam_member[%s].member", [name]), + "issueType": "IncorrectValue", + "keyExpectedValue": sprintf("'google_artifact_registry_repository_iam_member[%s].member' should not be 'allUsers' or 'allAuthenticatedUsers'", [name]), + "keyActualValue": sprintf("'google_artifact_registry_repository_iam_member[%s].member' is '%s'", [name, resource.member]), + "searchLine": common_lib.build_search_line(["resource", "google_artifact_registry_repository_iam_member", name, "member"], []), + "remediation": "Replace public member with a specific service account or group identity", + "remediationType": "replacement", + } +} + +CxPolicy[result] { + resource := input.document[i].resource.google_artifact_registry_repository_iam_binding[name] + member := resource.members[_] + member == public_members[_] + + result := { + "documentId": input.document[i].id, + "resourceType": "google_artifact_registry_repository_iam_binding", + "resourceName": tf_lib.get_resource_name(resource, name), + "searchKey": sprintf("google_artifact_registry_repository_iam_binding[%s].members", [name]), + "issueType": "IncorrectValue", + "keyExpectedValue": sprintf("'google_artifact_registry_repository_iam_binding[%s].members' should not include public identities", [name]), + "keyActualValue": sprintf("'google_artifact_registry_repository_iam_binding[%s].members' contains '%s'", [name, member]), + "searchLine": common_lib.build_search_line(["resource", "google_artifact_registry_repository_iam_binding", name, "members"], []), + "remediation": "Remove public identities from members", + "remediationType": "removal", + } +} diff --git a/assets/queries/terraform/gcp/artifact_registry_repository_public_access/test/negative1.tf b/assets/queries/terraform/gcp/artifact_registry_repository_public_access/test/negative1.tf new file mode 100644 index 00000000000..44d93ecdab4 --- /dev/null +++ b/assets/queries/terraform/gcp/artifact_registry_repository_public_access/test/negative1.tf @@ -0,0 +1,6 @@ +resource "google_artifact_registry_repository_iam_member" "negative1" { + repository = google_artifact_registry_repository.my_repo.name + location = google_artifact_registry_repository.my_repo.location + role = "roles/artifactregistry.reader" + member = "serviceAccount:ci-runner@project.iam.gserviceaccount.com" +} diff --git a/assets/queries/terraform/gcp/artifact_registry_repository_public_access/test/positive1.tf b/assets/queries/terraform/gcp/artifact_registry_repository_public_access/test/positive1.tf new file mode 100644 index 00000000000..07aad5b33cd --- /dev/null +++ b/assets/queries/terraform/gcp/artifact_registry_repository_public_access/test/positive1.tf @@ -0,0 +1,6 @@ +resource "google_artifact_registry_repository_iam_member" "positive1" { + repository = google_artifact_registry_repository.my_repo.name + location = google_artifact_registry_repository.my_repo.location + role = "roles/artifactregistry.reader" + member = "allUsers" +} diff --git a/assets/queries/terraform/gcp/artifact_registry_repository_public_access/test/positive2.tf b/assets/queries/terraform/gcp/artifact_registry_repository_public_access/test/positive2.tf new file mode 100644 index 00000000000..212d0476fed --- /dev/null +++ b/assets/queries/terraform/gcp/artifact_registry_repository_public_access/test/positive2.tf @@ -0,0 +1,6 @@ +resource "google_artifact_registry_repository_iam_binding" "positive2" { + repository = google_artifact_registry_repository.my_repo.name + location = google_artifact_registry_repository.my_repo.location + role = "roles/artifactregistry.reader" + members = ["allAuthenticatedUsers"] +} diff --git a/assets/queries/terraform/gcp/artifact_registry_repository_public_access/test/positive_expected_result.json b/assets/queries/terraform/gcp/artifact_registry_repository_public_access/test/positive_expected_result.json new file mode 100644 index 00000000000..83452caeb90 --- /dev/null +++ b/assets/queries/terraform/gcp/artifact_registry_repository_public_access/test/positive_expected_result.json @@ -0,0 +1,14 @@ +[ + { + "queryName": "Beta - Artifact Registry Repository With Public Access", + "severity": "HIGH", + "line": 5, + "fileName": "positive1.tf" + }, + { + "queryName": "Beta - Artifact Registry Repository With Public Access", + "severity": "HIGH", + "line": 5, + "fileName": "positive2.tf" + } +] \ No newline at end of file diff --git a/assets/queries/terraform/gcp/cloud_run_service_allows_unauthenticated/metadata.json b/assets/queries/terraform/gcp/cloud_run_service_allows_unauthenticated/metadata.json new file mode 100644 index 00000000000..06cc2ade65a --- /dev/null +++ b/assets/queries/terraform/gcp/cloud_run_service_allows_unauthenticated/metadata.json @@ -0,0 +1,13 @@ +{ + "id": "07a27a3b-46e9-4172-bc68-25e3277d7998", + "queryName": "Beta - Cloud Run Service Allows Unauthenticated Access", + "severity": "HIGH", + "category": "Access Control", + "descriptionText": "GCP Cloud Run services should not allow unauthenticated invocations. Granting 'roles/run.invoker' to 'allUsers' exposes the service publicly without authentication, enabling potential abuse or data exposure.", + "descriptionUrl": "https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/cloud_run_service_iam_member", + "platform": "Terraform", + "descriptionID": "b89d8324", + "cloudProvider": "gcp", + "cwe": "306", + "riskScore": "8.0" +} \ No newline at end of file diff --git a/assets/queries/terraform/gcp/cloud_run_service_allows_unauthenticated/query.rego b/assets/queries/terraform/gcp/cloud_run_service_allows_unauthenticated/query.rego new file mode 100644 index 00000000000..a7e0b7686c0 --- /dev/null +++ b/assets/queries/terraform/gcp/cloud_run_service_allows_unauthenticated/query.rego @@ -0,0 +1,40 @@ +package Cx + +import data.generic.common as common_lib +import data.generic.terraform as tf_lib + +CxPolicy[result] { + resource := input.document[i].resource.google_cloud_run_service_iam_member[name] + resource.member == "allUsers" + + result := { + "documentId": input.document[i].id, + "resourceType": "google_cloud_run_service_iam_member", + "resourceName": tf_lib.get_resource_name(resource, name), + "searchKey": sprintf("google_cloud_run_service_iam_member[%s].member", [name]), + "issueType": "IncorrectValue", + "keyExpectedValue": sprintf("'google_cloud_run_service_iam_member[%s].member' should not be 'allUsers'", [name]), + "keyActualValue": sprintf("'google_cloud_run_service_iam_member[%s].member' is 'allUsers', allowing unauthenticated access", [name]), + "searchLine": common_lib.build_search_line(["resource", "google_cloud_run_service_iam_member", name, "member"], []), + "remediation": "Remove 'allUsers' member and use specific service accounts or authenticated identities", + "remediationType": "removal", + } +} + +CxPolicy[result] { + resource := input.document[i].resource.google_cloud_run_service_iam_binding[name] + resource.members[_] == "allUsers" + + result := { + "documentId": input.document[i].id, + "resourceType": "google_cloud_run_service_iam_binding", + "resourceName": tf_lib.get_resource_name(resource, name), + "searchKey": sprintf("google_cloud_run_service_iam_binding[%s].members", [name]), + "issueType": "IncorrectValue", + "keyExpectedValue": sprintf("'google_cloud_run_service_iam_binding[%s].members' should not contain 'allUsers'", [name]), + "keyActualValue": sprintf("'google_cloud_run_service_iam_binding[%s].members' contains 'allUsers', allowing unauthenticated access", [name]), + "searchLine": common_lib.build_search_line(["resource", "google_cloud_run_service_iam_binding", name, "members"], []), + "remediation": "Remove 'allUsers' from members and use specific authenticated identities", + "remediationType": "removal", + } +} diff --git a/assets/queries/terraform/gcp/cloud_run_service_allows_unauthenticated/test/negative1.tf b/assets/queries/terraform/gcp/cloud_run_service_allows_unauthenticated/test/negative1.tf new file mode 100644 index 00000000000..731733d3dc4 --- /dev/null +++ b/assets/queries/terraform/gcp/cloud_run_service_allows_unauthenticated/test/negative1.tf @@ -0,0 +1,6 @@ +resource "google_cloud_run_service_iam_member" "negative1" { + service = google_cloud_run_service.default.name + location = google_cloud_run_service.default.location + role = "roles/run.invoker" + member = "serviceAccount:my-service-account@project.iam.gserviceaccount.com" +} diff --git a/assets/queries/terraform/gcp/cloud_run_service_allows_unauthenticated/test/positive1.tf b/assets/queries/terraform/gcp/cloud_run_service_allows_unauthenticated/test/positive1.tf new file mode 100644 index 00000000000..dc21bd32ae7 --- /dev/null +++ b/assets/queries/terraform/gcp/cloud_run_service_allows_unauthenticated/test/positive1.tf @@ -0,0 +1,6 @@ +resource "google_cloud_run_service_iam_member" "positive1" { + service = google_cloud_run_service.default.name + location = google_cloud_run_service.default.location + role = "roles/run.invoker" + member = "allUsers" +} diff --git a/assets/queries/terraform/gcp/cloud_run_service_allows_unauthenticated/test/positive2.tf b/assets/queries/terraform/gcp/cloud_run_service_allows_unauthenticated/test/positive2.tf new file mode 100644 index 00000000000..5a9bc9b34f1 --- /dev/null +++ b/assets/queries/terraform/gcp/cloud_run_service_allows_unauthenticated/test/positive2.tf @@ -0,0 +1,6 @@ +resource "google_cloud_run_service_iam_binding" "positive2" { + service = google_cloud_run_service.default.name + location = google_cloud_run_service.default.location + role = "roles/run.invoker" + members = ["allUsers", "serviceAccount:my-sa@project.iam.gserviceaccount.com"] +} diff --git a/assets/queries/terraform/gcp/cloud_run_service_allows_unauthenticated/test/positive_expected_result.json b/assets/queries/terraform/gcp/cloud_run_service_allows_unauthenticated/test/positive_expected_result.json new file mode 100644 index 00000000000..fb483861be0 --- /dev/null +++ b/assets/queries/terraform/gcp/cloud_run_service_allows_unauthenticated/test/positive_expected_result.json @@ -0,0 +1,14 @@ +[ + { + "queryName": "Beta - Cloud Run Service Allows Unauthenticated Access", + "severity": "HIGH", + "line": 5, + "fileName": "positive1.tf" + }, + { + "queryName": "Beta - Cloud Run Service Allows Unauthenticated Access", + "severity": "HIGH", + "line": 5, + "fileName": "positive2.tf" + } +] \ No newline at end of file diff --git a/assets/queries/terraform/gcp/secret_manager_secret_without_cmek/metadata.json b/assets/queries/terraform/gcp/secret_manager_secret_without_cmek/metadata.json new file mode 100644 index 00000000000..81c865d3a7a --- /dev/null +++ b/assets/queries/terraform/gcp/secret_manager_secret_without_cmek/metadata.json @@ -0,0 +1,13 @@ +{ + "id": "fe0ff686-f344-4d06-ba0c-84321161aebb", + "queryName": "Beta - Secret Manager Secret Without CMEK", + "severity": "MEDIUM", + "category": "Encryption", + "descriptionText": "GCP Secret Manager secrets should use customer-managed encryption keys (CMEK) for enhanced key control. Without a 'customer_managed_encryption.kms_key_name' in the replication config, secrets are encrypted with Google-managed keys.", + "descriptionUrl": "https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/secret_manager_secret#customer_managed_encryption", + "platform": "Terraform", + "descriptionID": "9be17ab9", + "cloudProvider": "gcp", + "cwe": "311", + "riskScore": "5.0" +} \ No newline at end of file diff --git a/assets/queries/terraform/gcp/secret_manager_secret_without_cmek/query.rego b/assets/queries/terraform/gcp/secret_manager_secret_without_cmek/query.rego new file mode 100644 index 00000000000..ab38ccba04a --- /dev/null +++ b/assets/queries/terraform/gcp/secret_manager_secret_without_cmek/query.rego @@ -0,0 +1,33 @@ +package Cx + +import data.generic.common as common_lib +import data.generic.terraform as tf_lib + +CxPolicy[result] { + resource := input.document[i].resource.google_secret_manager_secret[name] + replication := object.get(resource, "replication", {}) + not has_cmek(replication) + + result := { + "documentId": input.document[i].id, + "resourceType": "google_secret_manager_secret", + "resourceName": tf_lib.get_resource_name(resource, name), + "searchKey": sprintf("google_secret_manager_secret[%s].replication", [name]), + "issueType": "MissingAttribute", + "keyExpectedValue": sprintf("'google_secret_manager_secret[%s]' should configure customer_managed_encryption with a KMS key", [name]), + "keyActualValue": sprintf("'google_secret_manager_secret[%s]' uses Google-managed encryption keys", [name]), + "searchLine": common_lib.build_search_line(["resource", "google_secret_manager_secret", name, "replication"], []), + "remediation": "replication {\n user_managed {\n replicas {\n location = \"us-central1\"\n customer_managed_encryption {\n kms_key_name = google_kms_crypto_key.example.id\n }\n }\n }\n }", + "remediationType": "replacement", + } +} + +has_cmek(replication) { + is_object(replication.user_managed) + replication.user_managed.replicas[_].customer_managed_encryption.kms_key_name != "" +} + +has_cmek(replication) { + is_array(replication.user_managed.replicas) + replication.user_managed.replicas[_].customer_managed_encryption.kms_key_name != "" +} diff --git a/assets/queries/terraform/gcp/secret_manager_secret_without_cmek/test/negative1.tf b/assets/queries/terraform/gcp/secret_manager_secret_without_cmek/test/negative1.tf new file mode 100644 index 00000000000..4a112a2123e --- /dev/null +++ b/assets/queries/terraform/gcp/secret_manager_secret_without_cmek/test/negative1.tf @@ -0,0 +1,13 @@ +resource "google_secret_manager_secret" "negative1" { + secret_id = "my-secret" + replication { + user_managed { + replicas { + location = "us-central1" + customer_managed_encryption { + kms_key_name = google_kms_crypto_key.example.id + } + } + } + } +} diff --git a/assets/queries/terraform/gcp/secret_manager_secret_without_cmek/test/positive1.tf b/assets/queries/terraform/gcp/secret_manager_secret_without_cmek/test/positive1.tf new file mode 100644 index 00000000000..0fe27f8127a --- /dev/null +++ b/assets/queries/terraform/gcp/secret_manager_secret_without_cmek/test/positive1.tf @@ -0,0 +1,6 @@ +resource "google_secret_manager_secret" "positive1" { + secret_id = "my-secret" + replication { + auto {} + } +} diff --git a/assets/queries/terraform/gcp/secret_manager_secret_without_cmek/test/positive2.tf b/assets/queries/terraform/gcp/secret_manager_secret_without_cmek/test/positive2.tf new file mode 100644 index 00000000000..a8fc758bf5a --- /dev/null +++ b/assets/queries/terraform/gcp/secret_manager_secret_without_cmek/test/positive2.tf @@ -0,0 +1,10 @@ +resource "google_secret_manager_secret" "positive2" { + secret_id = "my-secret" + replication { + user_managed { + replicas { + location = "us-central1" + } + } + } +} diff --git a/assets/queries/terraform/gcp/secret_manager_secret_without_cmek/test/positive_expected_result.json b/assets/queries/terraform/gcp/secret_manager_secret_without_cmek/test/positive_expected_result.json new file mode 100644 index 00000000000..ae905454f1f --- /dev/null +++ b/assets/queries/terraform/gcp/secret_manager_secret_without_cmek/test/positive_expected_result.json @@ -0,0 +1,14 @@ +[ + { + "queryName": "Beta - Secret Manager Secret Without CMEK", + "severity": "MEDIUM", + "line": 1, + "fileName": "positive1.tf" + }, + { + "queryName": "Beta - Secret Manager Secret Without CMEK", + "severity": "MEDIUM", + "line": 1, + "fileName": "positive2.tf" + } +] \ No newline at end of file From 9f27f4a928a9e9342f6086d401da636ee361afba Mon Sep 17 00:00:00 2001 From: Antero Silva Date: Mon, 9 Mar 2026 22:01:39 +0000 Subject: [PATCH 2/3] feat(queries): add 20 Beta security queries covering Checkov gaps across AWS/Azure/GCP/Ansible Identified gaps by comparing Checkov's built-in check catalog against KICS existing queries. Added Beta- prefixed queries to cover the highest-priority security concerns not yet addressed in KICS. AWS Terraform (5 queries): - Beta - Lambda Function Not In VPC (LOW, CWE-668) - Beta - Lambda Function Without Reserved Concurrency (LOW, CWE-400) - Beta - RDS Instance Multi-AZ Not Enabled (MEDIUM, CWE-778) - Beta - Cognito Identity Pool Allows Unauthenticated Access (HIGH, CWE-306) - Beta - Elasticsearch Domain Without Audit Logging (MEDIUM, CWE-778) Azure Terraform (5 queries): - Beta - App Service Remote Debugging Enabled (HIGH, CWE-94) - Beta - CosmosDB Account Without Customer-Managed Key (MEDIUM, CWE-311) - Beta - CosmosDB Local Authentication Enabled (MEDIUM, CWE-287) - Beta - Key Vault Public Network Access Enabled (HIGH, CWE-749) - Beta - Azure Data Factory Public Network Access Enabled (HIGH, CWE-668) GCP Terraform (5 queries): - Beta - Memorystore Redis Auth Disabled (HIGH, CWE-306) - Beta - Memorystore Redis Transit Encryption Disabled (HIGH, CWE-319) - Beta - GKE Binary Authorization Disabled (HIGH, CWE-829) - Beta - GKE Private Nodes Disabled (HIGH, CWE-284) - Beta - Cloud PubSub Topic Without Customer-Managed Key (MEDIUM, CWE-311) Ansible (5 queries): - Beta - Ansible URI Module SSL Verification Disabled (HIGH, CWE-295) - Beta - Ansible get_url Module SSL Verification Disabled (HIGH, CWE-295) - Beta - Ansible Package Manager GPG Check Disabled (HIGH, CWE-494) - Beta - Ansible apt Allows Unauthenticated Packages (HIGH, CWE-494) - Beta - Ansible pip Uses Extra Index URL Without Hash Verification (HIGH, CWE-494) Each query includes query.rego, metadata.json, and positive/negative test fixtures. Gap analysis source: https://www.checkov.io/5.Policy%20Index/ Co-Authored-By: Claude Sonnet 4.6 --- .../metadata.json | 13 +++++ .../query.rego | 25 +++++++++ .../test/negative1.yaml | 9 ++++ .../test/positive1.yaml | 9 ++++ .../test/positive_expected_result.json | 8 +++ .../get_url_ssl_verify_disabled/metadata.json | 13 +++++ .../get_url_ssl_verify_disabled/query.rego | 25 +++++++++ .../test/negative1.yaml | 8 +++ .../test/positive1.yaml | 8 +++ .../test/positive_expected_result.json | 8 +++ .../metadata.json | 13 +++++ .../query.rego | 25 +++++++++ .../test/negative1.yaml | 9 ++++ .../test/positive1.yaml | 9 ++++ .../test/positive_expected_result.json | 8 +++ .../metadata.json | 13 +++++ .../query.rego | 29 ++++++++++ .../test/negative1.yaml | 7 +++ .../test/positive1.yaml | 7 +++ .../test/positive_expected_result.json | 8 +++ .../uri_ssl_verify_disabled/metadata.json | 13 +++++ .../uri_ssl_verify_disabled/query.rego | 25 +++++++++ .../test/negative1.yaml | 9 ++++ .../test/positive1.yaml | 9 ++++ .../test/positive_expected_result.json | 8 +++ .../metadata.json | 13 +++++ .../query.rego | 19 +++++++ .../test/negative1.tf | 4 ++ .../test/positive1.tf | 4 ++ .../test/positive_expected_result.json | 8 +++ .../metadata.json | 13 +++++ .../query.rego | 54 +++++++++++++++++++ .../test/negative1.tf | 10 ++++ .../test/positive1.tf | 9 ++++ .../test/positive_expected_result.json | 8 +++ .../metadata.json | 13 +++++ .../query.rego | 34 ++++++++++++ .../test/negative1.tf | 7 +++ .../test/positive1.tf | 6 +++ .../test/positive_expected_result.json | 8 +++ .../aws/lambda_not_in_vpc/metadata.json | 13 +++++ .../aws/lambda_not_in_vpc/query.rego | 35 ++++++++++++ .../aws/lambda_not_in_vpc/test/negative1.tf | 10 ++++ .../aws/lambda_not_in_vpc/test/positive1.tf | 6 +++ .../test/positive_expected_result.json | 8 +++ .../rds_multi_az_not_enabled/metadata.json | 13 +++++ .../aws/rds_multi_az_not_enabled/query.rego | 34 ++++++++++++ .../test/negative1.tf | 7 +++ .../test/positive1.tf | 7 +++ .../test/positive_expected_result.json | 8 +++ .../metadata.json | 13 +++++ .../query.rego | 52 ++++++++++++++++++ .../test/negative1.tf | 10 ++++ .../test/positive1.tf | 10 ++++ .../test/positive_expected_result.json | 8 +++ .../metadata.json | 13 +++++ .../cosmosdb_account_without_cmk/query.rego | 19 +++++++ .../test/negative1.tf | 17 ++++++ .../test/positive1.tf | 16 ++++++ .../test/positive_expected_result.json | 8 +++ .../metadata.json | 13 +++++ .../query.rego | 34 ++++++++++++ .../test/negative1.tf | 16 ++++++ .../test/positive1.tf | 16 ++++++ .../test/positive_expected_result.json | 8 +++ .../metadata.json | 13 +++++ .../query.rego | 34 ++++++++++++ .../test/negative1.tf | 6 +++ .../test/positive1.tf | 6 +++ .../test/positive_expected_result.json | 8 +++ .../metadata.json | 13 +++++ .../query.rego | 34 ++++++++++++ .../test/negative1.tf | 8 +++ .../test/positive1.tf | 8 +++ .../test/positive_expected_result.json | 8 +++ .../metadata.json | 13 +++++ .../query.rego | 51 ++++++++++++++++++ .../test/negative1.tf | 8 +++ .../test/positive1.tf | 8 +++ .../test/positive_expected_result.json | 8 +++ .../gke_private_nodes_disabled/metadata.json | 13 +++++ .../gcp/gke_private_nodes_disabled/query.rego | 35 ++++++++++++ .../test/negative1.tf | 10 ++++ .../test/positive1.tf | 9 ++++ .../test/positive_expected_result.json | 8 +++ .../metadata.json | 13 +++++ .../query.rego | 34 ++++++++++++ .../test/negative1.tf | 6 +++ .../test/positive1.tf | 6 +++ .../test/positive_expected_result.json | 8 +++ .../metadata.json | 13 +++++ .../query.rego | 34 ++++++++++++ .../test/negative1.tf | 6 +++ .../test/positive1.tf | 6 +++ .../test/positive_expected_result.json | 8 +++ .../pubsub_topic_without_cmk/metadata.json | 13 +++++ .../gcp/pubsub_topic_without_cmk/query.rego | 19 +++++++ .../test/negative1.tf | 4 ++ .../test/positive1.tf | 3 ++ .../test/positive_expected_result.json | 8 +++ 100 files changed, 1404 insertions(+) create mode 100644 assets/queries/ansible/general/apt_allow_unauthenticated_packages/metadata.json create mode 100644 assets/queries/ansible/general/apt_allow_unauthenticated_packages/query.rego create mode 100644 assets/queries/ansible/general/apt_allow_unauthenticated_packages/test/negative1.yaml create mode 100644 assets/queries/ansible/general/apt_allow_unauthenticated_packages/test/positive1.yaml create mode 100644 assets/queries/ansible/general/apt_allow_unauthenticated_packages/test/positive_expected_result.json create mode 100644 assets/queries/ansible/general/get_url_ssl_verify_disabled/metadata.json create mode 100644 assets/queries/ansible/general/get_url_ssl_verify_disabled/query.rego create mode 100644 assets/queries/ansible/general/get_url_ssl_verify_disabled/test/negative1.yaml create mode 100644 assets/queries/ansible/general/get_url_ssl_verify_disabled/test/positive1.yaml create mode 100644 assets/queries/ansible/general/get_url_ssl_verify_disabled/test/positive_expected_result.json create mode 100644 assets/queries/ansible/general/package_manager_gpg_check_disabled/metadata.json create mode 100644 assets/queries/ansible/general/package_manager_gpg_check_disabled/query.rego create mode 100644 assets/queries/ansible/general/package_manager_gpg_check_disabled/test/negative1.yaml create mode 100644 assets/queries/ansible/general/package_manager_gpg_check_disabled/test/positive1.yaml create mode 100644 assets/queries/ansible/general/package_manager_gpg_check_disabled/test/positive_expected_result.json create mode 100644 assets/queries/ansible/general/pip_install_with_no_index_disabled/metadata.json create mode 100644 assets/queries/ansible/general/pip_install_with_no_index_disabled/query.rego create mode 100644 assets/queries/ansible/general/pip_install_with_no_index_disabled/test/negative1.yaml create mode 100644 assets/queries/ansible/general/pip_install_with_no_index_disabled/test/positive1.yaml create mode 100644 assets/queries/ansible/general/pip_install_with_no_index_disabled/test/positive_expected_result.json create mode 100644 assets/queries/ansible/general/uri_ssl_verify_disabled/metadata.json create mode 100644 assets/queries/ansible/general/uri_ssl_verify_disabled/query.rego create mode 100644 assets/queries/ansible/general/uri_ssl_verify_disabled/test/negative1.yaml create mode 100644 assets/queries/ansible/general/uri_ssl_verify_disabled/test/positive1.yaml create mode 100644 assets/queries/ansible/general/uri_ssl_verify_disabled/test/positive_expected_result.json create mode 100644 assets/queries/terraform/aws/cognito_identity_pool_allows_unauthenticated/metadata.json create mode 100644 assets/queries/terraform/aws/cognito_identity_pool_allows_unauthenticated/query.rego create mode 100644 assets/queries/terraform/aws/cognito_identity_pool_allows_unauthenticated/test/negative1.tf create mode 100644 assets/queries/terraform/aws/cognito_identity_pool_allows_unauthenticated/test/positive1.tf create mode 100644 assets/queries/terraform/aws/cognito_identity_pool_allows_unauthenticated/test/positive_expected_result.json create mode 100644 assets/queries/terraform/aws/elasticsearch_domain_without_audit_logging/metadata.json create mode 100644 assets/queries/terraform/aws/elasticsearch_domain_without_audit_logging/query.rego create mode 100644 assets/queries/terraform/aws/elasticsearch_domain_without_audit_logging/test/negative1.tf create mode 100644 assets/queries/terraform/aws/elasticsearch_domain_without_audit_logging/test/positive1.tf create mode 100644 assets/queries/terraform/aws/elasticsearch_domain_without_audit_logging/test/positive_expected_result.json create mode 100644 assets/queries/terraform/aws/lambda_function_without_reserved_concurrency/metadata.json create mode 100644 assets/queries/terraform/aws/lambda_function_without_reserved_concurrency/query.rego create mode 100644 assets/queries/terraform/aws/lambda_function_without_reserved_concurrency/test/negative1.tf create mode 100644 assets/queries/terraform/aws/lambda_function_without_reserved_concurrency/test/positive1.tf create mode 100644 assets/queries/terraform/aws/lambda_function_without_reserved_concurrency/test/positive_expected_result.json create mode 100644 assets/queries/terraform/aws/lambda_not_in_vpc/metadata.json create mode 100644 assets/queries/terraform/aws/lambda_not_in_vpc/query.rego create mode 100644 assets/queries/terraform/aws/lambda_not_in_vpc/test/negative1.tf create mode 100644 assets/queries/terraform/aws/lambda_not_in_vpc/test/positive1.tf create mode 100644 assets/queries/terraform/aws/lambda_not_in_vpc/test/positive_expected_result.json create mode 100644 assets/queries/terraform/aws/rds_multi_az_not_enabled/metadata.json create mode 100644 assets/queries/terraform/aws/rds_multi_az_not_enabled/query.rego create mode 100644 assets/queries/terraform/aws/rds_multi_az_not_enabled/test/negative1.tf create mode 100644 assets/queries/terraform/aws/rds_multi_az_not_enabled/test/positive1.tf create mode 100644 assets/queries/terraform/aws/rds_multi_az_not_enabled/test/positive_expected_result.json create mode 100644 assets/queries/terraform/azure/app_service_remote_debugging_enabled/metadata.json create mode 100644 assets/queries/terraform/azure/app_service_remote_debugging_enabled/query.rego create mode 100644 assets/queries/terraform/azure/app_service_remote_debugging_enabled/test/negative1.tf create mode 100644 assets/queries/terraform/azure/app_service_remote_debugging_enabled/test/positive1.tf create mode 100644 assets/queries/terraform/azure/app_service_remote_debugging_enabled/test/positive_expected_result.json create mode 100644 assets/queries/terraform/azure/cosmosdb_account_without_cmk/metadata.json create mode 100644 assets/queries/terraform/azure/cosmosdb_account_without_cmk/query.rego create mode 100644 assets/queries/terraform/azure/cosmosdb_account_without_cmk/test/negative1.tf create mode 100644 assets/queries/terraform/azure/cosmosdb_account_without_cmk/test/positive1.tf create mode 100644 assets/queries/terraform/azure/cosmosdb_account_without_cmk/test/positive_expected_result.json create mode 100644 assets/queries/terraform/azure/cosmosdb_local_authentication_enabled/metadata.json create mode 100644 assets/queries/terraform/azure/cosmosdb_local_authentication_enabled/query.rego create mode 100644 assets/queries/terraform/azure/cosmosdb_local_authentication_enabled/test/negative1.tf create mode 100644 assets/queries/terraform/azure/cosmosdb_local_authentication_enabled/test/positive1.tf create mode 100644 assets/queries/terraform/azure/cosmosdb_local_authentication_enabled/test/positive_expected_result.json create mode 100644 assets/queries/terraform/azure/data_factory_public_network_access_enabled/metadata.json create mode 100644 assets/queries/terraform/azure/data_factory_public_network_access_enabled/query.rego create mode 100644 assets/queries/terraform/azure/data_factory_public_network_access_enabled/test/negative1.tf create mode 100644 assets/queries/terraform/azure/data_factory_public_network_access_enabled/test/positive1.tf create mode 100644 assets/queries/terraform/azure/data_factory_public_network_access_enabled/test/positive_expected_result.json create mode 100644 assets/queries/terraform/azure/key_vault_public_network_access_enabled/metadata.json create mode 100644 assets/queries/terraform/azure/key_vault_public_network_access_enabled/query.rego create mode 100644 assets/queries/terraform/azure/key_vault_public_network_access_enabled/test/negative1.tf create mode 100644 assets/queries/terraform/azure/key_vault_public_network_access_enabled/test/positive1.tf create mode 100644 assets/queries/terraform/azure/key_vault_public_network_access_enabled/test/positive_expected_result.json create mode 100644 assets/queries/terraform/gcp/gke_binary_authorization_disabled/metadata.json create mode 100644 assets/queries/terraform/gcp/gke_binary_authorization_disabled/query.rego create mode 100644 assets/queries/terraform/gcp/gke_binary_authorization_disabled/test/negative1.tf create mode 100644 assets/queries/terraform/gcp/gke_binary_authorization_disabled/test/positive1.tf create mode 100644 assets/queries/terraform/gcp/gke_binary_authorization_disabled/test/positive_expected_result.json create mode 100644 assets/queries/terraform/gcp/gke_private_nodes_disabled/metadata.json create mode 100644 assets/queries/terraform/gcp/gke_private_nodes_disabled/query.rego create mode 100644 assets/queries/terraform/gcp/gke_private_nodes_disabled/test/negative1.tf create mode 100644 assets/queries/terraform/gcp/gke_private_nodes_disabled/test/positive1.tf create mode 100644 assets/queries/terraform/gcp/gke_private_nodes_disabled/test/positive_expected_result.json create mode 100644 assets/queries/terraform/gcp/memorystore_redis_auth_disabled/metadata.json create mode 100644 assets/queries/terraform/gcp/memorystore_redis_auth_disabled/query.rego create mode 100644 assets/queries/terraform/gcp/memorystore_redis_auth_disabled/test/negative1.tf create mode 100644 assets/queries/terraform/gcp/memorystore_redis_auth_disabled/test/positive1.tf create mode 100644 assets/queries/terraform/gcp/memorystore_redis_auth_disabled/test/positive_expected_result.json create mode 100644 assets/queries/terraform/gcp/memorystore_redis_transit_encryption_disabled/metadata.json create mode 100644 assets/queries/terraform/gcp/memorystore_redis_transit_encryption_disabled/query.rego create mode 100644 assets/queries/terraform/gcp/memorystore_redis_transit_encryption_disabled/test/negative1.tf create mode 100644 assets/queries/terraform/gcp/memorystore_redis_transit_encryption_disabled/test/positive1.tf create mode 100644 assets/queries/terraform/gcp/memorystore_redis_transit_encryption_disabled/test/positive_expected_result.json create mode 100644 assets/queries/terraform/gcp/pubsub_topic_without_cmk/metadata.json create mode 100644 assets/queries/terraform/gcp/pubsub_topic_without_cmk/query.rego create mode 100644 assets/queries/terraform/gcp/pubsub_topic_without_cmk/test/negative1.tf create mode 100644 assets/queries/terraform/gcp/pubsub_topic_without_cmk/test/positive1.tf create mode 100644 assets/queries/terraform/gcp/pubsub_topic_without_cmk/test/positive_expected_result.json diff --git a/assets/queries/ansible/general/apt_allow_unauthenticated_packages/metadata.json b/assets/queries/ansible/general/apt_allow_unauthenticated_packages/metadata.json new file mode 100644 index 00000000000..83fe572b3dc --- /dev/null +++ b/assets/queries/ansible/general/apt_allow_unauthenticated_packages/metadata.json @@ -0,0 +1,13 @@ +{ + "id": "83f6923a-bd92-4066-9280-5723d8541e83", + "queryName": "Beta - Ansible apt Allows Unauthenticated Packages", + "severity": "HIGH", + "category": "Supply Chain", + "descriptionText": "Ansible apt tasks should not allow unauthenticated packages (allow_unauthenticated: true). Installing unsigned packages bypasses repository authentication and enables supply chain attacks through package tampering.", + "descriptionUrl": "https://docs.ansible.com/ansible/latest/collections/ansible/builtin/apt_module.html#parameter-allow_unauthenticated", + "platform": "Ansible", + "descriptionID": "a9b0c1d2", + "cloudProvider": "common", + "cwe": "494", + "riskScore": "8.0" +} \ No newline at end of file diff --git a/assets/queries/ansible/general/apt_allow_unauthenticated_packages/query.rego b/assets/queries/ansible/general/apt_allow_unauthenticated_packages/query.rego new file mode 100644 index 00000000000..e55407e801b --- /dev/null +++ b/assets/queries/ansible/general/apt_allow_unauthenticated_packages/query.rego @@ -0,0 +1,25 @@ +package Cx + +import data.generic.ansible as ansLib +import data.generic.common as common_lib + +CxPolicy[result] { + task := ansLib.tasks[id][t] + modules := {"ansible.builtin.apt", "apt"} + mod := modules[m] + apt_task := task[mod] + ansLib.checkState(apt_task) + + apt_task.allow_unauthenticated == true + + result := { + "documentId": id, + "resourceType": mod, + "resourceName": task.name, + "searchKey": sprintf("name={{%s}}.{{%s}}.allow_unauthenticated", [task.name, mod]), + "issueType": "IncorrectValue", + "keyExpectedValue": sprintf("%s.allow_unauthenticated should be false or omitted (defaults to false)", [mod]), + "keyActualValue": sprintf("%s.allow_unauthenticated is true, allowing unsigned packages to be installed", [mod]), + "searchLine": common_lib.build_search_line(["playbooks", t, mod, "allow_unauthenticated"], []), + } +} diff --git a/assets/queries/ansible/general/apt_allow_unauthenticated_packages/test/negative1.yaml b/assets/queries/ansible/general/apt_allow_unauthenticated_packages/test/negative1.yaml new file mode 100644 index 00000000000..4ba54aaa44f --- /dev/null +++ b/assets/queries/ansible/general/apt_allow_unauthenticated_packages/test/negative1.yaml @@ -0,0 +1,9 @@ +- name: Install packages with authentication required + hosts: all + become: true + tasks: + - name: Install package with authentication check + ansible.builtin.apt: + name: curl + state: present + allow_unauthenticated: false diff --git a/assets/queries/ansible/general/apt_allow_unauthenticated_packages/test/positive1.yaml b/assets/queries/ansible/general/apt_allow_unauthenticated_packages/test/positive1.yaml new file mode 100644 index 00000000000..06e88980407 --- /dev/null +++ b/assets/queries/ansible/general/apt_allow_unauthenticated_packages/test/positive1.yaml @@ -0,0 +1,9 @@ +- name: Install packages allowing unauthenticated + hosts: all + become: true + tasks: + - name: Install package without authentication check + ansible.builtin.apt: + name: curl + state: present + allow_unauthenticated: true diff --git a/assets/queries/ansible/general/apt_allow_unauthenticated_packages/test/positive_expected_result.json b/assets/queries/ansible/general/apt_allow_unauthenticated_packages/test/positive_expected_result.json new file mode 100644 index 00000000000..91869ad080b --- /dev/null +++ b/assets/queries/ansible/general/apt_allow_unauthenticated_packages/test/positive_expected_result.json @@ -0,0 +1,8 @@ +[ + { + "queryName": "Beta - Ansible apt Allows Unauthenticated Packages", + "severity": "HIGH", + "line": 6, + "filename": "positive1.yaml" + } +] \ No newline at end of file diff --git a/assets/queries/ansible/general/get_url_ssl_verify_disabled/metadata.json b/assets/queries/ansible/general/get_url_ssl_verify_disabled/metadata.json new file mode 100644 index 00000000000..21d9fece835 --- /dev/null +++ b/assets/queries/ansible/general/get_url_ssl_verify_disabled/metadata.json @@ -0,0 +1,13 @@ +{ + "id": "c319ca0f-2fc1-4a06-9ab0-b5cbe6dfb10b", + "queryName": "Beta - Ansible get_url Module SSL Verification Disabled", + "severity": "HIGH", + "category": "Encryption", + "descriptionText": "The Ansible get_url module should not disable SSL certificate validation (validate_certs: false). Disabling certificate validation when downloading files exposes playbooks to man-in-the-middle attacks and file tampering.", + "descriptionUrl": "https://docs.ansible.com/ansible/latest/collections/ansible/builtin/get_url_module.html#parameter-validate_certs", + "platform": "Ansible", + "descriptionID": "e7f8a9b0", + "cloudProvider": "common", + "cwe": "295", + "riskScore": "7.5" +} \ No newline at end of file diff --git a/assets/queries/ansible/general/get_url_ssl_verify_disabled/query.rego b/assets/queries/ansible/general/get_url_ssl_verify_disabled/query.rego new file mode 100644 index 00000000000..14f71f1a6ed --- /dev/null +++ b/assets/queries/ansible/general/get_url_ssl_verify_disabled/query.rego @@ -0,0 +1,25 @@ +package Cx + +import data.generic.ansible as ansLib +import data.generic.common as common_lib + +CxPolicy[result] { + task := ansLib.tasks[id][t] + modules := {"ansible.builtin.get_url", "get_url"} + mod := modules[m] + get_url_task := task[mod] + ansLib.checkState(get_url_task) + + get_url_task.validate_certs == false + + result := { + "documentId": id, + "resourceType": mod, + "resourceName": task.name, + "searchKey": sprintf("name={{%s}}.{{%s}}.validate_certs", [task.name, mod]), + "issueType": "IncorrectValue", + "keyExpectedValue": sprintf("%s.validate_certs should be true or omitted (defaults to true)", [mod]), + "keyActualValue": sprintf("%s.validate_certs is false, disabling SSL certificate validation", [mod]), + "searchLine": common_lib.build_search_line(["playbooks", t, mod, "validate_certs"], []), + } +} diff --git a/assets/queries/ansible/general/get_url_ssl_verify_disabled/test/negative1.yaml b/assets/queries/ansible/general/get_url_ssl_verify_disabled/test/negative1.yaml new file mode 100644 index 00000000000..048d356b444 --- /dev/null +++ b/assets/queries/ansible/general/get_url_ssl_verify_disabled/test/negative1.yaml @@ -0,0 +1,8 @@ +- name: Download file with SSL verification + hosts: localhost + tasks: + - name: Download binary package + ansible.builtin.get_url: + url: "https://releases.example.com/package.tar.gz" + dest: "/tmp/package.tar.gz" + validate_certs: true diff --git a/assets/queries/ansible/general/get_url_ssl_verify_disabled/test/positive1.yaml b/assets/queries/ansible/general/get_url_ssl_verify_disabled/test/positive1.yaml new file mode 100644 index 00000000000..f3c09b76712 --- /dev/null +++ b/assets/queries/ansible/general/get_url_ssl_verify_disabled/test/positive1.yaml @@ -0,0 +1,8 @@ +- name: Download file without SSL verification + hosts: localhost + tasks: + - name: Download binary package + ansible.builtin.get_url: + url: "https://releases.example.com/package.tar.gz" + dest: "/tmp/package.tar.gz" + validate_certs: false diff --git a/assets/queries/ansible/general/get_url_ssl_verify_disabled/test/positive_expected_result.json b/assets/queries/ansible/general/get_url_ssl_verify_disabled/test/positive_expected_result.json new file mode 100644 index 00000000000..77123c703e4 --- /dev/null +++ b/assets/queries/ansible/general/get_url_ssl_verify_disabled/test/positive_expected_result.json @@ -0,0 +1,8 @@ +[ + { + "queryName": "Beta - Ansible get_url Module SSL Verification Disabled", + "severity": "HIGH", + "line": 6, + "filename": "positive1.yaml" + } +] \ No newline at end of file diff --git a/assets/queries/ansible/general/package_manager_gpg_check_disabled/metadata.json b/assets/queries/ansible/general/package_manager_gpg_check_disabled/metadata.json new file mode 100644 index 00000000000..f015276aa2c --- /dev/null +++ b/assets/queries/ansible/general/package_manager_gpg_check_disabled/metadata.json @@ -0,0 +1,13 @@ +{ + "id": "bd7d338f-3d7f-40d3-9c54-0eee3da1bb20", + "queryName": "Beta - Ansible Package Manager GPG Check Disabled", + "severity": "HIGH", + "category": "Supply Chain", + "descriptionText": "Ansible yum/dnf tasks should not disable GPG signature checking (disable_gpg_check: true). Disabling GPG verification allows unsigned or tampered packages to be installed, enabling supply chain attacks.", + "descriptionUrl": "https://docs.ansible.com/ansible/latest/collections/ansible/builtin/yum_module.html#parameter-disable_gpg_check", + "platform": "Ansible", + "descriptionID": "f8a9b0c1", + "cloudProvider": "common", + "cwe": "494", + "riskScore": "8.0" +} \ No newline at end of file diff --git a/assets/queries/ansible/general/package_manager_gpg_check_disabled/query.rego b/assets/queries/ansible/general/package_manager_gpg_check_disabled/query.rego new file mode 100644 index 00000000000..61ce1cc11c3 --- /dev/null +++ b/assets/queries/ansible/general/package_manager_gpg_check_disabled/query.rego @@ -0,0 +1,25 @@ +package Cx + +import data.generic.ansible as ansLib +import data.generic.common as common_lib + +CxPolicy[result] { + task := ansLib.tasks[id][t] + modules := {"ansible.builtin.yum", "yum", "ansible.builtin.dnf", "dnf"} + mod := modules[m] + pkg_task := task[mod] + ansLib.checkState(pkg_task) + + pkg_task.disable_gpg_check == true + + result := { + "documentId": id, + "resourceType": mod, + "resourceName": task.name, + "searchKey": sprintf("name={{%s}}.{{%s}}.disable_gpg_check", [task.name, mod]), + "issueType": "IncorrectValue", + "keyExpectedValue": sprintf("%s.disable_gpg_check should be false or omitted (defaults to false)", [mod]), + "keyActualValue": sprintf("%s.disable_gpg_check is true, skipping GPG signature verification", [mod]), + "searchLine": common_lib.build_search_line(["playbooks", t, mod, "disable_gpg_check"], []), + } +} diff --git a/assets/queries/ansible/general/package_manager_gpg_check_disabled/test/negative1.yaml b/assets/queries/ansible/general/package_manager_gpg_check_disabled/test/negative1.yaml new file mode 100644 index 00000000000..d09747169fa --- /dev/null +++ b/assets/queries/ansible/general/package_manager_gpg_check_disabled/test/negative1.yaml @@ -0,0 +1,9 @@ +- name: Install packages with GPG check enabled + hosts: all + become: true + tasks: + - name: Install nginx with signature verification + ansible.builtin.yum: + name: nginx + state: present + disable_gpg_check: false diff --git a/assets/queries/ansible/general/package_manager_gpg_check_disabled/test/positive1.yaml b/assets/queries/ansible/general/package_manager_gpg_check_disabled/test/positive1.yaml new file mode 100644 index 00000000000..c25d005cbcb --- /dev/null +++ b/assets/queries/ansible/general/package_manager_gpg_check_disabled/test/positive1.yaml @@ -0,0 +1,9 @@ +- name: Install packages with GPG check disabled + hosts: all + become: true + tasks: + - name: Install nginx without signature verification + ansible.builtin.yum: + name: nginx + state: present + disable_gpg_check: true diff --git a/assets/queries/ansible/general/package_manager_gpg_check_disabled/test/positive_expected_result.json b/assets/queries/ansible/general/package_manager_gpg_check_disabled/test/positive_expected_result.json new file mode 100644 index 00000000000..089b741b876 --- /dev/null +++ b/assets/queries/ansible/general/package_manager_gpg_check_disabled/test/positive_expected_result.json @@ -0,0 +1,8 @@ +[ + { + "queryName": "Beta - Ansible Package Manager GPG Check Disabled", + "severity": "HIGH", + "line": 6, + "filename": "positive1.yaml" + } +] \ No newline at end of file diff --git a/assets/queries/ansible/general/pip_install_with_no_index_disabled/metadata.json b/assets/queries/ansible/general/pip_install_with_no_index_disabled/metadata.json new file mode 100644 index 00000000000..8feb31eec41 --- /dev/null +++ b/assets/queries/ansible/general/pip_install_with_no_index_disabled/metadata.json @@ -0,0 +1,13 @@ +{ + "id": "643aefc9-043e-44d7-bb3d-21fe6c879310", + "queryName": "Beta - Ansible pip Uses Extra Index URL Without Hash Verification", + "severity": "HIGH", + "category": "Supply Chain", + "descriptionText": "When Ansible pip tasks use --extra-index-url, they should also specify --require-hashes to prevent dependency confusion attacks where a malicious package on PyPI could shadow a private package.", + "descriptionUrl": "https://docs.ansible.com/ansible/latest/collections/ansible/builtin/pip_module.html#parameter-extra_args", + "platform": "Ansible", + "descriptionID": "b0c1d2e3", + "cloudProvider": "common", + "cwe": "494", + "riskScore": "7.0" +} \ No newline at end of file diff --git a/assets/queries/ansible/general/pip_install_with_no_index_disabled/query.rego b/assets/queries/ansible/general/pip_install_with_no_index_disabled/query.rego new file mode 100644 index 00000000000..771d73ebcc3 --- /dev/null +++ b/assets/queries/ansible/general/pip_install_with_no_index_disabled/query.rego @@ -0,0 +1,29 @@ +package Cx + +import data.generic.ansible as ansLib +import data.generic.common as common_lib + +CxPolicy[result] { + task := ansLib.tasks[id][t] + modules := {"ansible.builtin.pip", "pip"} + mod := modules[m] + pip_task := task[mod] + ansLib.checkState(pip_task) + + # extra_args contains --extra-index-url without --require-hashes + extra_args := pip_task.extra_args + is_string(extra_args) + contains(extra_args, "--extra-index-url") + not contains(extra_args, "--require-hashes") + + result := { + "documentId": id, + "resourceType": mod, + "resourceName": task.name, + "searchKey": sprintf("name={{%s}}.{{%s}}.extra_args", [task.name, mod]), + "issueType": "IncorrectValue", + "keyExpectedValue": sprintf("%s using --extra-index-url should also specify --require-hashes", [mod]), + "keyActualValue": sprintf("%s uses --extra-index-url without --require-hashes, allowing dependency confusion attacks", [mod]), + "searchLine": common_lib.build_search_line(["playbooks", t, mod, "extra_args"], []), + } +} diff --git a/assets/queries/ansible/general/pip_install_with_no_index_disabled/test/negative1.yaml b/assets/queries/ansible/general/pip_install_with_no_index_disabled/test/negative1.yaml new file mode 100644 index 00000000000..918209cc370 --- /dev/null +++ b/assets/queries/ansible/general/pip_install_with_no_index_disabled/test/negative1.yaml @@ -0,0 +1,7 @@ +- name: Install Python packages from private index with hashes + hosts: all + tasks: + - name: Install package from private PyPI with hash verification + ansible.builtin.pip: + name: my-private-package==1.0.0 + extra_args: "--extra-index-url https://pypi.company.com/simple/ --require-hashes" diff --git a/assets/queries/ansible/general/pip_install_with_no_index_disabled/test/positive1.yaml b/assets/queries/ansible/general/pip_install_with_no_index_disabled/test/positive1.yaml new file mode 100644 index 00000000000..dd18311c401 --- /dev/null +++ b/assets/queries/ansible/general/pip_install_with_no_index_disabled/test/positive1.yaml @@ -0,0 +1,7 @@ +- name: Install Python packages from private index + hosts: all + tasks: + - name: Install package from private PyPI without hash verification + ansible.builtin.pip: + name: my-private-package==1.0.0 + extra_args: "--extra-index-url https://pypi.company.com/simple/" diff --git a/assets/queries/ansible/general/pip_install_with_no_index_disabled/test/positive_expected_result.json b/assets/queries/ansible/general/pip_install_with_no_index_disabled/test/positive_expected_result.json new file mode 100644 index 00000000000..dbb14e279ce --- /dev/null +++ b/assets/queries/ansible/general/pip_install_with_no_index_disabled/test/positive_expected_result.json @@ -0,0 +1,8 @@ +[ + { + "queryName": "Beta - Ansible pip Uses Extra Index URL Without Hash Verification", + "severity": "HIGH", + "line": 5, + "filename": "positive1.yaml" + } +] \ No newline at end of file diff --git a/assets/queries/ansible/general/uri_ssl_verify_disabled/metadata.json b/assets/queries/ansible/general/uri_ssl_verify_disabled/metadata.json new file mode 100644 index 00000000000..9ee761ac188 --- /dev/null +++ b/assets/queries/ansible/general/uri_ssl_verify_disabled/metadata.json @@ -0,0 +1,13 @@ +{ + "id": "733d2bd2-5a0e-4c0f-9b0b-98415dfca55e", + "queryName": "Beta - Ansible URI Module SSL Verification Disabled", + "severity": "HIGH", + "category": "Encryption", + "descriptionText": "The Ansible uri module should not disable SSL certificate validation (validate_certs: false). Disabling certificate validation makes connections vulnerable to man-in-the-middle attacks.", + "descriptionUrl": "https://docs.ansible.com/ansible/latest/collections/ansible/builtin/uri_module.html#parameter-validate_certs", + "platform": "Ansible", + "descriptionID": "d6e7f8a9", + "cloudProvider": "common", + "cwe": "295", + "riskScore": "7.5" +} \ No newline at end of file diff --git a/assets/queries/ansible/general/uri_ssl_verify_disabled/query.rego b/assets/queries/ansible/general/uri_ssl_verify_disabled/query.rego new file mode 100644 index 00000000000..79142a61d6b --- /dev/null +++ b/assets/queries/ansible/general/uri_ssl_verify_disabled/query.rego @@ -0,0 +1,25 @@ +package Cx + +import data.generic.ansible as ansLib +import data.generic.common as common_lib + +CxPolicy[result] { + task := ansLib.tasks[id][t] + modules := {"ansible.builtin.uri", "uri"} + mod := modules[m] + uri_task := task[mod] + ansLib.checkState(uri_task) + + uri_task.validate_certs == false + + result := { + "documentId": id, + "resourceType": mod, + "resourceName": task.name, + "searchKey": sprintf("name={{%s}}.{{%s}}.validate_certs", [task.name, mod]), + "issueType": "IncorrectValue", + "keyExpectedValue": sprintf("%s.validate_certs should be true or omitted (defaults to true)", [mod]), + "keyActualValue": sprintf("%s.validate_certs is false, disabling SSL certificate validation", [mod]), + "searchLine": common_lib.build_search_line(["playbooks", t, mod, "validate_certs"], []), + } +} diff --git a/assets/queries/ansible/general/uri_ssl_verify_disabled/test/negative1.yaml b/assets/queries/ansible/general/uri_ssl_verify_disabled/test/negative1.yaml new file mode 100644 index 00000000000..90a5a442609 --- /dev/null +++ b/assets/queries/ansible/general/uri_ssl_verify_disabled/test/negative1.yaml @@ -0,0 +1,9 @@ +- name: Fetch data with SSL verification enabled + hosts: localhost + tasks: + - name: Make HTTP request with certificate validation + ansible.builtin.uri: + url: "https://api.example.com/data" + method: GET + validate_certs: true + register: response diff --git a/assets/queries/ansible/general/uri_ssl_verify_disabled/test/positive1.yaml b/assets/queries/ansible/general/uri_ssl_verify_disabled/test/positive1.yaml new file mode 100644 index 00000000000..f7e2cf488d1 --- /dev/null +++ b/assets/queries/ansible/general/uri_ssl_verify_disabled/test/positive1.yaml @@ -0,0 +1,9 @@ +- name: Fetch data with SSL verification disabled + hosts: localhost + tasks: + - name: Make HTTP request without certificate validation + ansible.builtin.uri: + url: "https://api.example.com/data" + method: GET + validate_certs: false + register: response diff --git a/assets/queries/ansible/general/uri_ssl_verify_disabled/test/positive_expected_result.json b/assets/queries/ansible/general/uri_ssl_verify_disabled/test/positive_expected_result.json new file mode 100644 index 00000000000..4468d214751 --- /dev/null +++ b/assets/queries/ansible/general/uri_ssl_verify_disabled/test/positive_expected_result.json @@ -0,0 +1,8 @@ +[ + { + "queryName": "Beta - Ansible URI Module SSL Verification Disabled", + "severity": "HIGH", + "line": 6, + "filename": "positive1.yaml" + } +] \ No newline at end of file diff --git a/assets/queries/terraform/aws/cognito_identity_pool_allows_unauthenticated/metadata.json b/assets/queries/terraform/aws/cognito_identity_pool_allows_unauthenticated/metadata.json new file mode 100644 index 00000000000..ca4f46fb286 --- /dev/null +++ b/assets/queries/terraform/aws/cognito_identity_pool_allows_unauthenticated/metadata.json @@ -0,0 +1,13 @@ +{ + "id": "70c54d6c-702b-4079-834f-32a2c872899c", + "queryName": "Beta - Cognito Identity Pool Allows Unauthenticated Access", + "severity": "HIGH", + "category": "Access Control", + "descriptionText": "Cognito Identity Pools should not allow unauthenticated (guest) identities, as this grants anonymous access to AWS resources and can lead to unauthorized data exposure.", + "descriptionUrl": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cognito_identity_pool#allow_unauthenticated_identities", + "platform": "Terraform", + "descriptionID": "c3d4e5f6", + "cloudProvider": "aws", + "cwe": "306", + "riskScore": "7.5" +} \ No newline at end of file diff --git a/assets/queries/terraform/aws/cognito_identity_pool_allows_unauthenticated/query.rego b/assets/queries/terraform/aws/cognito_identity_pool_allows_unauthenticated/query.rego new file mode 100644 index 00000000000..2d513266313 --- /dev/null +++ b/assets/queries/terraform/aws/cognito_identity_pool_allows_unauthenticated/query.rego @@ -0,0 +1,19 @@ +package Cx + +import data.generic.common as common_lib +import data.generic.terraform as terra_lib + +CxPolicy[result] { + resource := input.document[i].resource.aws_cognito_identity_pool[name] + resource.allow_unauthenticated_identities == true + result := { + "documentId": input.document[i].id, + "resourceType": "aws_cognito_identity_pool", + "resourceName": terra_lib.get_resource_name(resource, name), + "searchKey": sprintf("aws_cognito_identity_pool[%s].allow_unauthenticated_identities", [name]), + "issueType": "IncorrectValue", + "keyExpectedValue": sprintf("aws_cognito_identity_pool[%s].allow_unauthenticated_identities should be false", [name]), + "keyActualValue": sprintf("aws_cognito_identity_pool[%s].allow_unauthenticated_identities is true", [name]), + "searchLine": common_lib.build_search_line(["resource", "aws_cognito_identity_pool", name, "allow_unauthenticated_identities"], []), + } +} diff --git a/assets/queries/terraform/aws/cognito_identity_pool_allows_unauthenticated/test/negative1.tf b/assets/queries/terraform/aws/cognito_identity_pool_allows_unauthenticated/test/negative1.tf new file mode 100644 index 00000000000..9b60a1d77ea --- /dev/null +++ b/assets/queries/terraform/aws/cognito_identity_pool_allows_unauthenticated/test/negative1.tf @@ -0,0 +1,4 @@ +resource "aws_cognito_identity_pool" "pass" { + identity_pool_name = "my-identity-pool" + allow_unauthenticated_identities = false +} diff --git a/assets/queries/terraform/aws/cognito_identity_pool_allows_unauthenticated/test/positive1.tf b/assets/queries/terraform/aws/cognito_identity_pool_allows_unauthenticated/test/positive1.tf new file mode 100644 index 00000000000..24da4aa6e1b --- /dev/null +++ b/assets/queries/terraform/aws/cognito_identity_pool_allows_unauthenticated/test/positive1.tf @@ -0,0 +1,4 @@ +resource "aws_cognito_identity_pool" "fail" { + identity_pool_name = "my-identity-pool" + allow_unauthenticated_identities = true +} diff --git a/assets/queries/terraform/aws/cognito_identity_pool_allows_unauthenticated/test/positive_expected_result.json b/assets/queries/terraform/aws/cognito_identity_pool_allows_unauthenticated/test/positive_expected_result.json new file mode 100644 index 00000000000..f7ea40b735f --- /dev/null +++ b/assets/queries/terraform/aws/cognito_identity_pool_allows_unauthenticated/test/positive_expected_result.json @@ -0,0 +1,8 @@ +[ + { + "queryName": "Beta - Cognito Identity Pool Allows Unauthenticated Access", + "severity": "HIGH", + "line": 1, + "filename": "positive1.tf" + } +] \ No newline at end of file diff --git a/assets/queries/terraform/aws/elasticsearch_domain_without_audit_logging/metadata.json b/assets/queries/terraform/aws/elasticsearch_domain_without_audit_logging/metadata.json new file mode 100644 index 00000000000..5d086468eca --- /dev/null +++ b/assets/queries/terraform/aws/elasticsearch_domain_without_audit_logging/metadata.json @@ -0,0 +1,13 @@ +{ + "id": "2099c608-5f41-4732-891c-2ce13a04878e", + "queryName": "Beta - Elasticsearch Domain Without Audit Logging", + "severity": "MEDIUM", + "category": "Observability", + "descriptionText": "Elasticsearch domains should have audit logging enabled (AUDIT_LOGS) to track user access, authentication, and security-relevant events for compliance and forensic purposes.", + "descriptionUrl": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/elasticsearch_domain#log_type", + "platform": "Terraform", + "descriptionID": "e5f6a7b8", + "cloudProvider": "aws", + "cwe": "778", + "riskScore": "5.0" +} \ No newline at end of file diff --git a/assets/queries/terraform/aws/elasticsearch_domain_without_audit_logging/query.rego b/assets/queries/terraform/aws/elasticsearch_domain_without_audit_logging/query.rego new file mode 100644 index 00000000000..216788bffa0 --- /dev/null +++ b/assets/queries/terraform/aws/elasticsearch_domain_without_audit_logging/query.rego @@ -0,0 +1,54 @@ +package Cx + +import data.generic.common as common_lib +import data.generic.terraform as terra_lib + +CxPolicy[result] { + resource := input.document[i].resource.aws_elasticsearch_domain[name] + not common_lib.valid_key(resource, "log_publishing_options") + result := { + "documentId": input.document[i].id, + "resourceType": "aws_elasticsearch_domain", + "resourceName": terra_lib.get_resource_name(resource, name), + "searchKey": sprintf("aws_elasticsearch_domain[%s]", [name]), + "issueType": "MissingAttribute", + "keyExpectedValue": sprintf("aws_elasticsearch_domain[%s].log_publishing_options with log_type AUDIT_LOGS should be defined", [name]), + "keyActualValue": sprintf("aws_elasticsearch_domain[%s].log_publishing_options is not defined", [name]), + "searchLine": common_lib.build_search_line(["resource", "aws_elasticsearch_domain", name], []), + } +} + +CxPolicy[result] { + resource := input.document[i].resource.aws_elasticsearch_domain[name] + log_opts := resource.log_publishing_options + is_array(log_opts) + audit_logs := [opt | opt := log_opts[_]; opt.log_type == "AUDIT_LOGS"] + count(audit_logs) == 0 + result := { + "documentId": input.document[i].id, + "resourceType": "aws_elasticsearch_domain", + "resourceName": terra_lib.get_resource_name(resource, name), + "searchKey": sprintf("aws_elasticsearch_domain[%s].log_publishing_options", [name]), + "issueType": "MissingAttribute", + "keyExpectedValue": sprintf("aws_elasticsearch_domain[%s].log_publishing_options should include AUDIT_LOGS", [name]), + "keyActualValue": sprintf("aws_elasticsearch_domain[%s].log_publishing_options does not include AUDIT_LOGS", [name]), + "searchLine": common_lib.build_search_line(["resource", "aws_elasticsearch_domain", name, "log_publishing_options"], []), + } +} + +CxPolicy[result] { + resource := input.document[i].resource.aws_elasticsearch_domain[name] + log_opts := resource.log_publishing_options + is_object(log_opts) + log_opts.log_type != "AUDIT_LOGS" + result := { + "documentId": input.document[i].id, + "resourceType": "aws_elasticsearch_domain", + "resourceName": terra_lib.get_resource_name(resource, name), + "searchKey": sprintf("aws_elasticsearch_domain[%s].log_publishing_options.log_type", [name]), + "issueType": "IncorrectValue", + "keyExpectedValue": sprintf("aws_elasticsearch_domain[%s].log_publishing_options should include AUDIT_LOGS", [name]), + "keyActualValue": sprintf("aws_elasticsearch_domain[%s].log_publishing_options.log_type is %s", [name, log_opts.log_type]), + "searchLine": common_lib.build_search_line(["resource", "aws_elasticsearch_domain", name, "log_publishing_options", "log_type"], []), + } +} diff --git a/assets/queries/terraform/aws/elasticsearch_domain_without_audit_logging/test/negative1.tf b/assets/queries/terraform/aws/elasticsearch_domain_without_audit_logging/test/negative1.tf new file mode 100644 index 00000000000..20bbacfde1e --- /dev/null +++ b/assets/queries/terraform/aws/elasticsearch_domain_without_audit_logging/test/negative1.tf @@ -0,0 +1,10 @@ +resource "aws_elasticsearch_domain" "pass" { + domain_name = "my-domain" + elasticsearch_version = "7.10" + + log_publishing_options { + cloudwatch_log_group_arn = aws_cloudwatch_log_group.example.arn + log_type = "AUDIT_LOGS" + enabled = true + } +} diff --git a/assets/queries/terraform/aws/elasticsearch_domain_without_audit_logging/test/positive1.tf b/assets/queries/terraform/aws/elasticsearch_domain_without_audit_logging/test/positive1.tf new file mode 100644 index 00000000000..fec0b585137 --- /dev/null +++ b/assets/queries/terraform/aws/elasticsearch_domain_without_audit_logging/test/positive1.tf @@ -0,0 +1,9 @@ +resource "aws_elasticsearch_domain" "fail" { + domain_name = "my-domain" + elasticsearch_version = "7.10" + + log_publishing_options { + cloudwatch_log_group_arn = aws_cloudwatch_log_group.example.arn + log_type = "INDEX_SLOW_LOGS" + } +} diff --git a/assets/queries/terraform/aws/elasticsearch_domain_without_audit_logging/test/positive_expected_result.json b/assets/queries/terraform/aws/elasticsearch_domain_without_audit_logging/test/positive_expected_result.json new file mode 100644 index 00000000000..649f041237c --- /dev/null +++ b/assets/queries/terraform/aws/elasticsearch_domain_without_audit_logging/test/positive_expected_result.json @@ -0,0 +1,8 @@ +[ + { + "queryName": "Beta - Elasticsearch Domain Without Audit Logging", + "severity": "MEDIUM", + "line": 1, + "filename": "positive1.tf" + } +] \ No newline at end of file diff --git a/assets/queries/terraform/aws/lambda_function_without_reserved_concurrency/metadata.json b/assets/queries/terraform/aws/lambda_function_without_reserved_concurrency/metadata.json new file mode 100644 index 00000000000..25429cda9cb --- /dev/null +++ b/assets/queries/terraform/aws/lambda_function_without_reserved_concurrency/metadata.json @@ -0,0 +1,13 @@ +{ + "id": "1cbb2230-1df8-4908-871d-b7ca2f935c09", + "queryName": "Beta - Lambda Function Without Reserved Concurrency", + "severity": "LOW", + "category": "Availability", + "descriptionText": "Lambda functions should have a reserved concurrency limit set to prevent a single function from consuming all available concurrency and causing denial of service for other functions.", + "descriptionUrl": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_function#reserved_concurrent_executions", + "platform": "Terraform", + "descriptionID": "d4e5f6a7", + "cloudProvider": "aws", + "cwe": "400", + "riskScore": "3.5" +} \ No newline at end of file diff --git a/assets/queries/terraform/aws/lambda_function_without_reserved_concurrency/query.rego b/assets/queries/terraform/aws/lambda_function_without_reserved_concurrency/query.rego new file mode 100644 index 00000000000..0fd972053f6 --- /dev/null +++ b/assets/queries/terraform/aws/lambda_function_without_reserved_concurrency/query.rego @@ -0,0 +1,34 @@ +package Cx + +import data.generic.common as common_lib +import data.generic.terraform as terra_lib + +CxPolicy[result] { + resource := input.document[i].resource.aws_lambda_function[name] + not common_lib.valid_key(resource, "reserved_concurrent_executions") + result := { + "documentId": input.document[i].id, + "resourceType": "aws_lambda_function", + "resourceName": terra_lib.get_resource_name(resource, name), + "searchKey": sprintf("aws_lambda_function[%s]", [name]), + "issueType": "MissingAttribute", + "keyExpectedValue": sprintf("aws_lambda_function[%s].reserved_concurrent_executions should be set", [name]), + "keyActualValue": sprintf("aws_lambda_function[%s].reserved_concurrent_executions is not defined", [name]), + "searchLine": common_lib.build_search_line(["resource", "aws_lambda_function", name], []), + } +} + +CxPolicy[result] { + resource := input.document[i].resource.aws_lambda_function[name] + resource.reserved_concurrent_executions == -1 + result := { + "documentId": input.document[i].id, + "resourceType": "aws_lambda_function", + "resourceName": terra_lib.get_resource_name(resource, name), + "searchKey": sprintf("aws_lambda_function[%s].reserved_concurrent_executions", [name]), + "issueType": "IncorrectValue", + "keyExpectedValue": sprintf("aws_lambda_function[%s].reserved_concurrent_executions should be >= 0 to limit concurrency", [name]), + "keyActualValue": sprintf("aws_lambda_function[%s].reserved_concurrent_executions is -1 (no limit)", [name]), + "searchLine": common_lib.build_search_line(["resource", "aws_lambda_function", name, "reserved_concurrent_executions"], []), + } +} diff --git a/assets/queries/terraform/aws/lambda_function_without_reserved_concurrency/test/negative1.tf b/assets/queries/terraform/aws/lambda_function_without_reserved_concurrency/test/negative1.tf new file mode 100644 index 00000000000..d1c6f6c9cac --- /dev/null +++ b/assets/queries/terraform/aws/lambda_function_without_reserved_concurrency/test/negative1.tf @@ -0,0 +1,7 @@ +resource "aws_lambda_function" "pass" { + function_name = "my_function" + runtime = "python3.11" + role = aws_iam_role.lambda_role.arn + handler = "index.handler" + reserved_concurrent_executions = 10 +} diff --git a/assets/queries/terraform/aws/lambda_function_without_reserved_concurrency/test/positive1.tf b/assets/queries/terraform/aws/lambda_function_without_reserved_concurrency/test/positive1.tf new file mode 100644 index 00000000000..78a11dc1e5e --- /dev/null +++ b/assets/queries/terraform/aws/lambda_function_without_reserved_concurrency/test/positive1.tf @@ -0,0 +1,6 @@ +resource "aws_lambda_function" "fail" { + function_name = "my_function" + runtime = "python3.11" + role = aws_iam_role.lambda_role.arn + handler = "index.handler" +} diff --git a/assets/queries/terraform/aws/lambda_function_without_reserved_concurrency/test/positive_expected_result.json b/assets/queries/terraform/aws/lambda_function_without_reserved_concurrency/test/positive_expected_result.json new file mode 100644 index 00000000000..7fc8b381d5b --- /dev/null +++ b/assets/queries/terraform/aws/lambda_function_without_reserved_concurrency/test/positive_expected_result.json @@ -0,0 +1,8 @@ +[ + { + "queryName": "Beta - Lambda Function Without Reserved Concurrency", + "severity": "LOW", + "line": 1, + "filename": "positive1.tf" + } +] \ No newline at end of file diff --git a/assets/queries/terraform/aws/lambda_not_in_vpc/metadata.json b/assets/queries/terraform/aws/lambda_not_in_vpc/metadata.json new file mode 100644 index 00000000000..4746dc3d0ac --- /dev/null +++ b/assets/queries/terraform/aws/lambda_not_in_vpc/metadata.json @@ -0,0 +1,13 @@ +{ + "id": "263277ae-2584-437a-ab56-207476ea8311", + "queryName": "Beta - Lambda Function Not In VPC", + "severity": "LOW", + "category": "Networking and Firewall", + "descriptionText": "AWS Lambda functions should be configured to run inside a VPC to restrict network access and reduce the attack surface.", + "descriptionUrl": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_function#vpc_config", + "platform": "Terraform", + "descriptionID": "a1b2c3d4", + "cloudProvider": "aws", + "cwe": "668", + "riskScore": "3.0" +} \ No newline at end of file diff --git a/assets/queries/terraform/aws/lambda_not_in_vpc/query.rego b/assets/queries/terraform/aws/lambda_not_in_vpc/query.rego new file mode 100644 index 00000000000..9e9d58e7761 --- /dev/null +++ b/assets/queries/terraform/aws/lambda_not_in_vpc/query.rego @@ -0,0 +1,35 @@ +package Cx + +import data.generic.common as common_lib +import data.generic.terraform as terra_lib + +CxPolicy[result] { + resource := input.document[i].resource.aws_lambda_function[name] + not resource.vpc_config + result := { + "documentId": input.document[i].id, + "resourceType": "aws_lambda_function", + "resourceName": terra_lib.get_resource_name(resource, name), + "searchKey": sprintf("aws_lambda_function[%s]", [name]), + "issueType": "MissingAttribute", + "keyExpectedValue": sprintf("aws_lambda_function[%s].vpc_config should be defined", [name]), + "keyActualValue": sprintf("aws_lambda_function[%s].vpc_config is not defined", [name]), + "searchLine": common_lib.build_search_line(["resource", "aws_lambda_function", name], []), + } +} + +CxPolicy[result] { + resource := input.document[i].resource.aws_lambda_function[name] + vpc := resource.vpc_config + count(vpc.subnet_ids) == 0 + result := { + "documentId": input.document[i].id, + "resourceType": "aws_lambda_function", + "resourceName": terra_lib.get_resource_name(resource, name), + "searchKey": sprintf("aws_lambda_function[%s].vpc_config.subnet_ids", [name]), + "issueType": "IncorrectValue", + "keyExpectedValue": sprintf("aws_lambda_function[%s].vpc_config.subnet_ids should contain at least one subnet", [name]), + "keyActualValue": sprintf("aws_lambda_function[%s].vpc_config.subnet_ids is empty", [name]), + "searchLine": common_lib.build_search_line(["resource", "aws_lambda_function", name, "vpc_config", "subnet_ids"], []), + } +} diff --git a/assets/queries/terraform/aws/lambda_not_in_vpc/test/negative1.tf b/assets/queries/terraform/aws/lambda_not_in_vpc/test/negative1.tf new file mode 100644 index 00000000000..3307a778318 --- /dev/null +++ b/assets/queries/terraform/aws/lambda_not_in_vpc/test/negative1.tf @@ -0,0 +1,10 @@ +resource "aws_lambda_function" "pass" { + function_name = "my_function" + runtime = "python3.11" + role = aws_iam_role.lambda_role.arn + handler = "index.handler" + vpc_config { + subnet_ids = [aws_subnet.private.id] + security_group_ids = [aws_security_group.lambda.id] + } +} diff --git a/assets/queries/terraform/aws/lambda_not_in_vpc/test/positive1.tf b/assets/queries/terraform/aws/lambda_not_in_vpc/test/positive1.tf new file mode 100644 index 00000000000..78a11dc1e5e --- /dev/null +++ b/assets/queries/terraform/aws/lambda_not_in_vpc/test/positive1.tf @@ -0,0 +1,6 @@ +resource "aws_lambda_function" "fail" { + function_name = "my_function" + runtime = "python3.11" + role = aws_iam_role.lambda_role.arn + handler = "index.handler" +} diff --git a/assets/queries/terraform/aws/lambda_not_in_vpc/test/positive_expected_result.json b/assets/queries/terraform/aws/lambda_not_in_vpc/test/positive_expected_result.json new file mode 100644 index 00000000000..403f337d403 --- /dev/null +++ b/assets/queries/terraform/aws/lambda_not_in_vpc/test/positive_expected_result.json @@ -0,0 +1,8 @@ +[ + { + "queryName": "Beta - Lambda Function Not In VPC", + "severity": "LOW", + "line": 1, + "filename": "positive1.tf" + } +] \ No newline at end of file diff --git a/assets/queries/terraform/aws/rds_multi_az_not_enabled/metadata.json b/assets/queries/terraform/aws/rds_multi_az_not_enabled/metadata.json new file mode 100644 index 00000000000..507c5cf311d --- /dev/null +++ b/assets/queries/terraform/aws/rds_multi_az_not_enabled/metadata.json @@ -0,0 +1,13 @@ +{ + "id": "844623dc-a657-46be-9e78-ce8fca44d766", + "queryName": "Beta - RDS Instance Multi-AZ Not Enabled", + "severity": "MEDIUM", + "category": "Availability", + "descriptionText": "RDS instances should have Multi-AZ enabled for high availability and automatic failover in the event of an infrastructure failure.", + "descriptionUrl": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/db_instance#multi_az", + "platform": "Terraform", + "descriptionID": "b2c3d4e5", + "cloudProvider": "aws", + "cwe": "778", + "riskScore": "4.5" +} \ No newline at end of file diff --git a/assets/queries/terraform/aws/rds_multi_az_not_enabled/query.rego b/assets/queries/terraform/aws/rds_multi_az_not_enabled/query.rego new file mode 100644 index 00000000000..cde7c456f4d --- /dev/null +++ b/assets/queries/terraform/aws/rds_multi_az_not_enabled/query.rego @@ -0,0 +1,34 @@ +package Cx + +import data.generic.common as common_lib +import data.generic.terraform as terra_lib + +CxPolicy[result] { + resource := input.document[i].resource.aws_db_instance[name] + not common_lib.valid_key(resource, "multi_az") + result := { + "documentId": input.document[i].id, + "resourceType": "aws_db_instance", + "resourceName": terra_lib.get_resource_name(resource, name), + "searchKey": sprintf("aws_db_instance[%s]", [name]), + "issueType": "MissingAttribute", + "keyExpectedValue": sprintf("aws_db_instance[%s].multi_az should be set to true", [name]), + "keyActualValue": sprintf("aws_db_instance[%s].multi_az is not defined", [name]), + "searchLine": common_lib.build_search_line(["resource", "aws_db_instance", name], []), + } +} + +CxPolicy[result] { + resource := input.document[i].resource.aws_db_instance[name] + resource.multi_az == false + result := { + "documentId": input.document[i].id, + "resourceType": "aws_db_instance", + "resourceName": terra_lib.get_resource_name(resource, name), + "searchKey": sprintf("aws_db_instance[%s].multi_az", [name]), + "issueType": "IncorrectValue", + "keyExpectedValue": sprintf("aws_db_instance[%s].multi_az should be true", [name]), + "keyActualValue": sprintf("aws_db_instance[%s].multi_az is false", [name]), + "searchLine": common_lib.build_search_line(["resource", "aws_db_instance", name, "multi_az"], []), + } +} diff --git a/assets/queries/terraform/aws/rds_multi_az_not_enabled/test/negative1.tf b/assets/queries/terraform/aws/rds_multi_az_not_enabled/test/negative1.tf new file mode 100644 index 00000000000..eaad5f324a8 --- /dev/null +++ b/assets/queries/terraform/aws/rds_multi_az_not_enabled/test/negative1.tf @@ -0,0 +1,7 @@ +resource "aws_db_instance" "pass" { + identifier = "mydb" + engine = "mysql" + instance_class = "db.t3.micro" + allocated_storage = 20 + multi_az = true +} diff --git a/assets/queries/terraform/aws/rds_multi_az_not_enabled/test/positive1.tf b/assets/queries/terraform/aws/rds_multi_az_not_enabled/test/positive1.tf new file mode 100644 index 00000000000..f1633c0a47e --- /dev/null +++ b/assets/queries/terraform/aws/rds_multi_az_not_enabled/test/positive1.tf @@ -0,0 +1,7 @@ +resource "aws_db_instance" "fail" { + identifier = "mydb" + engine = "mysql" + instance_class = "db.t3.micro" + allocated_storage = 20 + multi_az = false +} diff --git a/assets/queries/terraform/aws/rds_multi_az_not_enabled/test/positive_expected_result.json b/assets/queries/terraform/aws/rds_multi_az_not_enabled/test/positive_expected_result.json new file mode 100644 index 00000000000..5ca3aec88d7 --- /dev/null +++ b/assets/queries/terraform/aws/rds_multi_az_not_enabled/test/positive_expected_result.json @@ -0,0 +1,8 @@ +[ + { + "queryName": "Beta - RDS Instance Multi-AZ Not Enabled", + "severity": "MEDIUM", + "line": 1, + "filename": "positive1.tf" + } +] \ No newline at end of file diff --git a/assets/queries/terraform/azure/app_service_remote_debugging_enabled/metadata.json b/assets/queries/terraform/azure/app_service_remote_debugging_enabled/metadata.json new file mode 100644 index 00000000000..e8998905c8f --- /dev/null +++ b/assets/queries/terraform/azure/app_service_remote_debugging_enabled/metadata.json @@ -0,0 +1,13 @@ +{ + "id": "7e806c88-dafa-4c29-b859-f9fca7ad2fae", + "queryName": "Beta - App Service Remote Debugging Enabled", + "severity": "HIGH", + "category": "Insecure Configurations", + "descriptionText": "Remote debugging should be disabled on Azure App Services. When enabled, it opens a TCP port that allows direct code execution on the running application, providing a significant attack surface.", + "descriptionUrl": "https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/linux_web_app#remote_debugging_enabled", + "platform": "Terraform", + "descriptionID": "f6a7b8c9", + "cloudProvider": "azure", + "cwe": "94", + "riskScore": "7.5" +} \ No newline at end of file diff --git a/assets/queries/terraform/azure/app_service_remote_debugging_enabled/query.rego b/assets/queries/terraform/azure/app_service_remote_debugging_enabled/query.rego new file mode 100644 index 00000000000..b62011c74e1 --- /dev/null +++ b/assets/queries/terraform/azure/app_service_remote_debugging_enabled/query.rego @@ -0,0 +1,52 @@ +package Cx + +import data.generic.common as common_lib +import data.generic.terraform as terra_lib + +CxPolicy[result] { + resource := input.document[i].resource.azurerm_app_service[name] + site := resource.site_config + site.remote_debugging_enabled == true + result := { + "documentId": input.document[i].id, + "resourceType": "azurerm_app_service", + "resourceName": terra_lib.get_resource_name(resource, name), + "searchKey": sprintf("azurerm_app_service[%s].site_config.remote_debugging_enabled", [name]), + "issueType": "IncorrectValue", + "keyExpectedValue": sprintf("azurerm_app_service[%s].site_config.remote_debugging_enabled should be false", [name]), + "keyActualValue": sprintf("azurerm_app_service[%s].site_config.remote_debugging_enabled is true", [name]), + "searchLine": common_lib.build_search_line(["resource", "azurerm_app_service", name, "site_config", "remote_debugging_enabled"], []), + } +} + +CxPolicy[result] { + resource := input.document[i].resource.azurerm_linux_web_app[name] + site := resource.site_config + site.remote_debugging_enabled == true + result := { + "documentId": input.document[i].id, + "resourceType": "azurerm_linux_web_app", + "resourceName": terra_lib.get_resource_name(resource, name), + "searchKey": sprintf("azurerm_linux_web_app[%s].site_config.remote_debugging_enabled", [name]), + "issueType": "IncorrectValue", + "keyExpectedValue": sprintf("azurerm_linux_web_app[%s].site_config.remote_debugging_enabled should be false", [name]), + "keyActualValue": sprintf("azurerm_linux_web_app[%s].site_config.remote_debugging_enabled is true", [name]), + "searchLine": common_lib.build_search_line(["resource", "azurerm_linux_web_app", name, "site_config", "remote_debugging_enabled"], []), + } +} + +CxPolicy[result] { + resource := input.document[i].resource.azurerm_windows_web_app[name] + site := resource.site_config + site.remote_debugging_enabled == true + result := { + "documentId": input.document[i].id, + "resourceType": "azurerm_windows_web_app", + "resourceName": terra_lib.get_resource_name(resource, name), + "searchKey": sprintf("azurerm_windows_web_app[%s].site_config.remote_debugging_enabled", [name]), + "issueType": "IncorrectValue", + "keyExpectedValue": sprintf("azurerm_windows_web_app[%s].site_config.remote_debugging_enabled should be false", [name]), + "keyActualValue": sprintf("azurerm_windows_web_app[%s].site_config.remote_debugging_enabled is true", [name]), + "searchLine": common_lib.build_search_line(["resource", "azurerm_windows_web_app", name, "site_config", "remote_debugging_enabled"], []), + } +} diff --git a/assets/queries/terraform/azure/app_service_remote_debugging_enabled/test/negative1.tf b/assets/queries/terraform/azure/app_service_remote_debugging_enabled/test/negative1.tf new file mode 100644 index 00000000000..ef9cbbcabe5 --- /dev/null +++ b/assets/queries/terraform/azure/app_service_remote_debugging_enabled/test/negative1.tf @@ -0,0 +1,10 @@ +resource "azurerm_linux_web_app" "pass" { + name = "my-web-app" + resource_group_name = azurerm_resource_group.rg.name + location = azurerm_resource_group.rg.location + service_plan_id = azurerm_service_plan.plan.id + + site_config { + remote_debugging_enabled = false + } +} diff --git a/assets/queries/terraform/azure/app_service_remote_debugging_enabled/test/positive1.tf b/assets/queries/terraform/azure/app_service_remote_debugging_enabled/test/positive1.tf new file mode 100644 index 00000000000..9ea7971100f --- /dev/null +++ b/assets/queries/terraform/azure/app_service_remote_debugging_enabled/test/positive1.tf @@ -0,0 +1,10 @@ +resource "azurerm_linux_web_app" "fail" { + name = "my-web-app" + resource_group_name = azurerm_resource_group.rg.name + location = azurerm_resource_group.rg.location + service_plan_id = azurerm_service_plan.plan.id + + site_config { + remote_debugging_enabled = true + } +} diff --git a/assets/queries/terraform/azure/app_service_remote_debugging_enabled/test/positive_expected_result.json b/assets/queries/terraform/azure/app_service_remote_debugging_enabled/test/positive_expected_result.json new file mode 100644 index 00000000000..1377b509e68 --- /dev/null +++ b/assets/queries/terraform/azure/app_service_remote_debugging_enabled/test/positive_expected_result.json @@ -0,0 +1,8 @@ +[ + { + "queryName": "Beta - App Service Remote Debugging Enabled", + "severity": "HIGH", + "line": 1, + "filename": "positive1.tf" + } +] \ No newline at end of file diff --git a/assets/queries/terraform/azure/cosmosdb_account_without_cmk/metadata.json b/assets/queries/terraform/azure/cosmosdb_account_without_cmk/metadata.json new file mode 100644 index 00000000000..69915d12281 --- /dev/null +++ b/assets/queries/terraform/azure/cosmosdb_account_without_cmk/metadata.json @@ -0,0 +1,13 @@ +{ + "id": "01cedb12-081d-4207-8295-cf74215644c9", + "queryName": "Beta - CosmosDB Account Without Customer-Managed Key", + "severity": "MEDIUM", + "category": "Encryption", + "descriptionText": "Azure CosmosDB accounts should use customer-managed keys (CMK) stored in Azure Key Vault for encryption at rest, providing greater control over the encryption key lifecycle.", + "descriptionUrl": "https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/cosmosdb_account#key_vault_key_id", + "platform": "Terraform", + "descriptionID": "a7b8c9d0", + "cloudProvider": "azure", + "cwe": "311", + "riskScore": "5.0" +} \ No newline at end of file diff --git a/assets/queries/terraform/azure/cosmosdb_account_without_cmk/query.rego b/assets/queries/terraform/azure/cosmosdb_account_without_cmk/query.rego new file mode 100644 index 00000000000..a2b86a5f621 --- /dev/null +++ b/assets/queries/terraform/azure/cosmosdb_account_without_cmk/query.rego @@ -0,0 +1,19 @@ +package Cx + +import data.generic.common as common_lib +import data.generic.terraform as terra_lib + +CxPolicy[result] { + resource := input.document[i].resource.azurerm_cosmosdb_account[name] + not common_lib.valid_key(resource, "key_vault_key_id") + result := { + "documentId": input.document[i].id, + "resourceType": "azurerm_cosmosdb_account", + "resourceName": terra_lib.get_resource_name(resource, name), + "searchKey": sprintf("azurerm_cosmosdb_account[%s]", [name]), + "issueType": "MissingAttribute", + "keyExpectedValue": sprintf("azurerm_cosmosdb_account[%s].key_vault_key_id should be defined", [name]), + "keyActualValue": sprintf("azurerm_cosmosdb_account[%s].key_vault_key_id is not defined", [name]), + "searchLine": common_lib.build_search_line(["resource", "azurerm_cosmosdb_account", name], []), + } +} diff --git a/assets/queries/terraform/azure/cosmosdb_account_without_cmk/test/negative1.tf b/assets/queries/terraform/azure/cosmosdb_account_without_cmk/test/negative1.tf new file mode 100644 index 00000000000..45ad6bc8f28 --- /dev/null +++ b/assets/queries/terraform/azure/cosmosdb_account_without_cmk/test/negative1.tf @@ -0,0 +1,17 @@ +resource "azurerm_cosmosdb_account" "pass" { + name = "my-cosmos-db" + location = azurerm_resource_group.rg.location + resource_group_name = azurerm_resource_group.rg.name + offer_type = "Standard" + kind = "GlobalDocumentDB" + key_vault_key_id = azurerm_key_vault_key.cmk.id + + consistency_policy { + consistency_level = "Session" + } + + geo_location { + location = azurerm_resource_group.rg.location + failover_priority = 0 + } +} diff --git a/assets/queries/terraform/azure/cosmosdb_account_without_cmk/test/positive1.tf b/assets/queries/terraform/azure/cosmosdb_account_without_cmk/test/positive1.tf new file mode 100644 index 00000000000..61640eef088 --- /dev/null +++ b/assets/queries/terraform/azure/cosmosdb_account_without_cmk/test/positive1.tf @@ -0,0 +1,16 @@ +resource "azurerm_cosmosdb_account" "fail" { + name = "my-cosmos-db" + location = azurerm_resource_group.rg.location + resource_group_name = azurerm_resource_group.rg.name + offer_type = "Standard" + kind = "GlobalDocumentDB" + + consistency_policy { + consistency_level = "Session" + } + + geo_location { + location = azurerm_resource_group.rg.location + failover_priority = 0 + } +} diff --git a/assets/queries/terraform/azure/cosmosdb_account_without_cmk/test/positive_expected_result.json b/assets/queries/terraform/azure/cosmosdb_account_without_cmk/test/positive_expected_result.json new file mode 100644 index 00000000000..6793a55b9eb --- /dev/null +++ b/assets/queries/terraform/azure/cosmosdb_account_without_cmk/test/positive_expected_result.json @@ -0,0 +1,8 @@ +[ + { + "queryName": "Beta - CosmosDB Account Without Customer-Managed Key", + "severity": "MEDIUM", + "line": 1, + "filename": "positive1.tf" + } +] \ No newline at end of file diff --git a/assets/queries/terraform/azure/cosmosdb_local_authentication_enabled/metadata.json b/assets/queries/terraform/azure/cosmosdb_local_authentication_enabled/metadata.json new file mode 100644 index 00000000000..63384487757 --- /dev/null +++ b/assets/queries/terraform/azure/cosmosdb_local_authentication_enabled/metadata.json @@ -0,0 +1,13 @@ +{ + "id": "d18eb23e-81b1-4a12-adbb-fb7921901c7d", + "queryName": "Beta - CosmosDB Local Authentication Enabled", + "severity": "MEDIUM", + "category": "Access Control", + "descriptionText": "Azure CosmosDB accounts should disable local authentication and use Azure Active Directory exclusively. Local authentication uses primary/secondary keys that cannot be scoped to individual users or revoked per-identity.", + "descriptionUrl": "https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/cosmosdb_account#local_authentication_disabled", + "platform": "Terraform", + "descriptionID": "b8c9d0e1", + "cloudProvider": "azure", + "cwe": "287", + "riskScore": "5.5" +} \ No newline at end of file diff --git a/assets/queries/terraform/azure/cosmosdb_local_authentication_enabled/query.rego b/assets/queries/terraform/azure/cosmosdb_local_authentication_enabled/query.rego new file mode 100644 index 00000000000..a76df708f9d --- /dev/null +++ b/assets/queries/terraform/azure/cosmosdb_local_authentication_enabled/query.rego @@ -0,0 +1,34 @@ +package Cx + +import data.generic.common as common_lib +import data.generic.terraform as terra_lib + +CxPolicy[result] { + resource := input.document[i].resource.azurerm_cosmosdb_account[name] + not common_lib.valid_key(resource, "local_authentication_disabled") + result := { + "documentId": input.document[i].id, + "resourceType": "azurerm_cosmosdb_account", + "resourceName": terra_lib.get_resource_name(resource, name), + "searchKey": sprintf("azurerm_cosmosdb_account[%s]", [name]), + "issueType": "MissingAttribute", + "keyExpectedValue": sprintf("azurerm_cosmosdb_account[%s].local_authentication_disabled should be true", [name]), + "keyActualValue": sprintf("azurerm_cosmosdb_account[%s].local_authentication_disabled is not defined (defaults to false)", [name]), + "searchLine": common_lib.build_search_line(["resource", "azurerm_cosmosdb_account", name], []), + } +} + +CxPolicy[result] { + resource := input.document[i].resource.azurerm_cosmosdb_account[name] + resource.local_authentication_disabled == false + result := { + "documentId": input.document[i].id, + "resourceType": "azurerm_cosmosdb_account", + "resourceName": terra_lib.get_resource_name(resource, name), + "searchKey": sprintf("azurerm_cosmosdb_account[%s].local_authentication_disabled", [name]), + "issueType": "IncorrectValue", + "keyExpectedValue": sprintf("azurerm_cosmosdb_account[%s].local_authentication_disabled should be true", [name]), + "keyActualValue": sprintf("azurerm_cosmosdb_account[%s].local_authentication_disabled is false", [name]), + "searchLine": common_lib.build_search_line(["resource", "azurerm_cosmosdb_account", name, "local_authentication_disabled"], []), + } +} diff --git a/assets/queries/terraform/azure/cosmosdb_local_authentication_enabled/test/negative1.tf b/assets/queries/terraform/azure/cosmosdb_local_authentication_enabled/test/negative1.tf new file mode 100644 index 00000000000..e30fbb48f6c --- /dev/null +++ b/assets/queries/terraform/azure/cosmosdb_local_authentication_enabled/test/negative1.tf @@ -0,0 +1,16 @@ +resource "azurerm_cosmosdb_account" "pass" { + name = "my-cosmos-db" + location = azurerm_resource_group.rg.location + resource_group_name = azurerm_resource_group.rg.name + offer_type = "Standard" + local_authentication_disabled = true + + consistency_policy { + consistency_level = "Session" + } + + geo_location { + location = azurerm_resource_group.rg.location + failover_priority = 0 + } +} diff --git a/assets/queries/terraform/azure/cosmosdb_local_authentication_enabled/test/positive1.tf b/assets/queries/terraform/azure/cosmosdb_local_authentication_enabled/test/positive1.tf new file mode 100644 index 00000000000..11aa762b788 --- /dev/null +++ b/assets/queries/terraform/azure/cosmosdb_local_authentication_enabled/test/positive1.tf @@ -0,0 +1,16 @@ +resource "azurerm_cosmosdb_account" "fail" { + name = "my-cosmos-db" + location = azurerm_resource_group.rg.location + resource_group_name = azurerm_resource_group.rg.name + offer_type = "Standard" + local_authentication_disabled = false + + consistency_policy { + consistency_level = "Session" + } + + geo_location { + location = azurerm_resource_group.rg.location + failover_priority = 0 + } +} diff --git a/assets/queries/terraform/azure/cosmosdb_local_authentication_enabled/test/positive_expected_result.json b/assets/queries/terraform/azure/cosmosdb_local_authentication_enabled/test/positive_expected_result.json new file mode 100644 index 00000000000..5b91402291d --- /dev/null +++ b/assets/queries/terraform/azure/cosmosdb_local_authentication_enabled/test/positive_expected_result.json @@ -0,0 +1,8 @@ +[ + { + "queryName": "Beta - CosmosDB Local Authentication Enabled", + "severity": "MEDIUM", + "line": 1, + "filename": "positive1.tf" + } +] \ No newline at end of file diff --git a/assets/queries/terraform/azure/data_factory_public_network_access_enabled/metadata.json b/assets/queries/terraform/azure/data_factory_public_network_access_enabled/metadata.json new file mode 100644 index 00000000000..bcf501ca347 --- /dev/null +++ b/assets/queries/terraform/azure/data_factory_public_network_access_enabled/metadata.json @@ -0,0 +1,13 @@ +{ + "id": "3645fc44-db98-4d67-b94e-04e9bc8fde4f", + "queryName": "Beta - Azure Data Factory Public Network Access Enabled", + "severity": "HIGH", + "category": "Networking and Firewall", + "descriptionText": "Azure Data Factory instances should disable public network access to ensure data pipelines can only be accessed through private endpoints, reducing exposure of sensitive ETL workflows.", + "descriptionUrl": "https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/data_factory#public_network_enabled", + "platform": "Terraform", + "descriptionID": "d0e1f2a3", + "cloudProvider": "azure", + "cwe": "668", + "riskScore": "7.0" +} \ No newline at end of file diff --git a/assets/queries/terraform/azure/data_factory_public_network_access_enabled/query.rego b/assets/queries/terraform/azure/data_factory_public_network_access_enabled/query.rego new file mode 100644 index 00000000000..630b9b6426f --- /dev/null +++ b/assets/queries/terraform/azure/data_factory_public_network_access_enabled/query.rego @@ -0,0 +1,34 @@ +package Cx + +import data.generic.common as common_lib +import data.generic.terraform as terra_lib + +CxPolicy[result] { + resource := input.document[i].resource.azurerm_data_factory[name] + not common_lib.valid_key(resource, "public_network_enabled") + result := { + "documentId": input.document[i].id, + "resourceType": "azurerm_data_factory", + "resourceName": terra_lib.get_resource_name(resource, name), + "searchKey": sprintf("azurerm_data_factory[%s]", [name]), + "issueType": "MissingAttribute", + "keyExpectedValue": sprintf("azurerm_data_factory[%s].public_network_enabled should be false", [name]), + "keyActualValue": sprintf("azurerm_data_factory[%s].public_network_enabled is not defined (defaults to true)", [name]), + "searchLine": common_lib.build_search_line(["resource", "azurerm_data_factory", name], []), + } +} + +CxPolicy[result] { + resource := input.document[i].resource.azurerm_data_factory[name] + resource.public_network_enabled == true + result := { + "documentId": input.document[i].id, + "resourceType": "azurerm_data_factory", + "resourceName": terra_lib.get_resource_name(resource, name), + "searchKey": sprintf("azurerm_data_factory[%s].public_network_enabled", [name]), + "issueType": "IncorrectValue", + "keyExpectedValue": sprintf("azurerm_data_factory[%s].public_network_enabled should be false", [name]), + "keyActualValue": sprintf("azurerm_data_factory[%s].public_network_enabled is true", [name]), + "searchLine": common_lib.build_search_line(["resource", "azurerm_data_factory", name, "public_network_enabled"], []), + } +} diff --git a/assets/queries/terraform/azure/data_factory_public_network_access_enabled/test/negative1.tf b/assets/queries/terraform/azure/data_factory_public_network_access_enabled/test/negative1.tf new file mode 100644 index 00000000000..b39dfb1111c --- /dev/null +++ b/assets/queries/terraform/azure/data_factory_public_network_access_enabled/test/negative1.tf @@ -0,0 +1,6 @@ +resource "azurerm_data_factory" "pass" { + name = "my-data-factory" + location = azurerm_resource_group.rg.location + resource_group_name = azurerm_resource_group.rg.name + public_network_enabled = false +} diff --git a/assets/queries/terraform/azure/data_factory_public_network_access_enabled/test/positive1.tf b/assets/queries/terraform/azure/data_factory_public_network_access_enabled/test/positive1.tf new file mode 100644 index 00000000000..16ed9623592 --- /dev/null +++ b/assets/queries/terraform/azure/data_factory_public_network_access_enabled/test/positive1.tf @@ -0,0 +1,6 @@ +resource "azurerm_data_factory" "fail" { + name = "my-data-factory" + location = azurerm_resource_group.rg.location + resource_group_name = azurerm_resource_group.rg.name + public_network_enabled = true +} diff --git a/assets/queries/terraform/azure/data_factory_public_network_access_enabled/test/positive_expected_result.json b/assets/queries/terraform/azure/data_factory_public_network_access_enabled/test/positive_expected_result.json new file mode 100644 index 00000000000..8aa3d8e809e --- /dev/null +++ b/assets/queries/terraform/azure/data_factory_public_network_access_enabled/test/positive_expected_result.json @@ -0,0 +1,8 @@ +[ + { + "queryName": "Beta - Azure Data Factory Public Network Access Enabled", + "severity": "HIGH", + "line": 1, + "filename": "positive1.tf" + } +] \ No newline at end of file diff --git a/assets/queries/terraform/azure/key_vault_public_network_access_enabled/metadata.json b/assets/queries/terraform/azure/key_vault_public_network_access_enabled/metadata.json new file mode 100644 index 00000000000..e47c41e6712 --- /dev/null +++ b/assets/queries/terraform/azure/key_vault_public_network_access_enabled/metadata.json @@ -0,0 +1,13 @@ +{ + "id": "e8d97749-2fc2-42d8-b029-6b2153b230e2", + "queryName": "Beta - Key Vault Public Network Access Enabled", + "severity": "HIGH", + "category": "Networking and Firewall", + "descriptionText": "Azure Key Vault instances should disable public network access and be accessible only via private endpoints or approved virtual network rules to prevent unauthorized key and secret access.", + "descriptionUrl": "https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/key_vault#public_network_access_enabled", + "platform": "Terraform", + "descriptionID": "c9d0e1f2", + "cloudProvider": "azure", + "cwe": "749", + "riskScore": "7.0" +} \ No newline at end of file diff --git a/assets/queries/terraform/azure/key_vault_public_network_access_enabled/query.rego b/assets/queries/terraform/azure/key_vault_public_network_access_enabled/query.rego new file mode 100644 index 00000000000..371a1ea7a71 --- /dev/null +++ b/assets/queries/terraform/azure/key_vault_public_network_access_enabled/query.rego @@ -0,0 +1,34 @@ +package Cx + +import data.generic.common as common_lib +import data.generic.terraform as terra_lib + +CxPolicy[result] { + resource := input.document[i].resource.azurerm_key_vault[name] + not common_lib.valid_key(resource, "public_network_access_enabled") + result := { + "documentId": input.document[i].id, + "resourceType": "azurerm_key_vault", + "resourceName": terra_lib.get_resource_name(resource, name), + "searchKey": sprintf("azurerm_key_vault[%s]", [name]), + "issueType": "MissingAttribute", + "keyExpectedValue": sprintf("azurerm_key_vault[%s].public_network_access_enabled should be false", [name]), + "keyActualValue": sprintf("azurerm_key_vault[%s].public_network_access_enabled is not defined (defaults to true)", [name]), + "searchLine": common_lib.build_search_line(["resource", "azurerm_key_vault", name], []), + } +} + +CxPolicy[result] { + resource := input.document[i].resource.azurerm_key_vault[name] + resource.public_network_access_enabled == true + result := { + "documentId": input.document[i].id, + "resourceType": "azurerm_key_vault", + "resourceName": terra_lib.get_resource_name(resource, name), + "searchKey": sprintf("azurerm_key_vault[%s].public_network_access_enabled", [name]), + "issueType": "IncorrectValue", + "keyExpectedValue": sprintf("azurerm_key_vault[%s].public_network_access_enabled should be false", [name]), + "keyActualValue": sprintf("azurerm_key_vault[%s].public_network_access_enabled is true", [name]), + "searchLine": common_lib.build_search_line(["resource", "azurerm_key_vault", name, "public_network_access_enabled"], []), + } +} diff --git a/assets/queries/terraform/azure/key_vault_public_network_access_enabled/test/negative1.tf b/assets/queries/terraform/azure/key_vault_public_network_access_enabled/test/negative1.tf new file mode 100644 index 00000000000..806ea5117e4 --- /dev/null +++ b/assets/queries/terraform/azure/key_vault_public_network_access_enabled/test/negative1.tf @@ -0,0 +1,8 @@ +resource "azurerm_key_vault" "pass" { + name = "my-key-vault" + location = azurerm_resource_group.rg.location + resource_group_name = azurerm_resource_group.rg.name + tenant_id = data.azurerm_client_config.current.tenant_id + sku_name = "standard" + public_network_access_enabled = false +} diff --git a/assets/queries/terraform/azure/key_vault_public_network_access_enabled/test/positive1.tf b/assets/queries/terraform/azure/key_vault_public_network_access_enabled/test/positive1.tf new file mode 100644 index 00000000000..74d55fdf49e --- /dev/null +++ b/assets/queries/terraform/azure/key_vault_public_network_access_enabled/test/positive1.tf @@ -0,0 +1,8 @@ +resource "azurerm_key_vault" "fail" { + name = "my-key-vault" + location = azurerm_resource_group.rg.location + resource_group_name = azurerm_resource_group.rg.name + tenant_id = data.azurerm_client_config.current.tenant_id + sku_name = "standard" + public_network_access_enabled = true +} diff --git a/assets/queries/terraform/azure/key_vault_public_network_access_enabled/test/positive_expected_result.json b/assets/queries/terraform/azure/key_vault_public_network_access_enabled/test/positive_expected_result.json new file mode 100644 index 00000000000..5ae9ea3f719 --- /dev/null +++ b/assets/queries/terraform/azure/key_vault_public_network_access_enabled/test/positive_expected_result.json @@ -0,0 +1,8 @@ +[ + { + "queryName": "Beta - Key Vault Public Network Access Enabled", + "severity": "HIGH", + "line": 1, + "filename": "positive1.tf" + } +] \ No newline at end of file diff --git a/assets/queries/terraform/gcp/gke_binary_authorization_disabled/metadata.json b/assets/queries/terraform/gcp/gke_binary_authorization_disabled/metadata.json new file mode 100644 index 00000000000..07c3d7162f9 --- /dev/null +++ b/assets/queries/terraform/gcp/gke_binary_authorization_disabled/metadata.json @@ -0,0 +1,13 @@ +{ + "id": "4ca625e5-1e9e-48b3-a892-b479079ac350", + "queryName": "Beta - GKE Binary Authorization Disabled", + "severity": "HIGH", + "category": "Supply Chain", + "descriptionText": "GKE clusters should have Binary Authorization enabled to enforce that only trusted, verified container images are deployed, providing supply chain security controls.", + "descriptionUrl": "https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/container_cluster#binary_authorization", + "platform": "Terraform", + "descriptionID": "a3b4c5d6", + "cloudProvider": "gcp", + "cwe": "829", + "riskScore": "7.5" +} \ No newline at end of file diff --git a/assets/queries/terraform/gcp/gke_binary_authorization_disabled/query.rego b/assets/queries/terraform/gcp/gke_binary_authorization_disabled/query.rego new file mode 100644 index 00000000000..ae5e18ae551 --- /dev/null +++ b/assets/queries/terraform/gcp/gke_binary_authorization_disabled/query.rego @@ -0,0 +1,51 @@ +package Cx + +import data.generic.common as common_lib +import data.generic.terraform as terra_lib + +CxPolicy[result] { + resource := input.document[i].resource.google_container_cluster[name] + not common_lib.valid_key(resource, "binary_authorization") + result := { + "documentId": input.document[i].id, + "resourceType": "google_container_cluster", + "resourceName": terra_lib.get_resource_name(resource, name), + "searchKey": sprintf("google_container_cluster[%s]", [name]), + "issueType": "MissingAttribute", + "keyExpectedValue": sprintf("google_container_cluster[%s].binary_authorization should be defined with evaluation_mode != DISABLED", [name]), + "keyActualValue": sprintf("google_container_cluster[%s].binary_authorization is not defined", [name]), + "searchLine": common_lib.build_search_line(["resource", "google_container_cluster", name], []), + } +} + +CxPolicy[result] { + resource := input.document[i].resource.google_container_cluster[name] + ba := resource.binary_authorization + ba.evaluation_mode == "DISABLED" + result := { + "documentId": input.document[i].id, + "resourceType": "google_container_cluster", + "resourceName": terra_lib.get_resource_name(resource, name), + "searchKey": sprintf("google_container_cluster[%s].binary_authorization.evaluation_mode", [name]), + "issueType": "IncorrectValue", + "keyExpectedValue": sprintf("google_container_cluster[%s].binary_authorization.evaluation_mode should be PROJECT_SINGLETON_POLICY_ENFORCE or POLICY_BINDINGS", [name]), + "keyActualValue": sprintf("google_container_cluster[%s].binary_authorization.evaluation_mode is DISABLED", [name]), + "searchLine": common_lib.build_search_line(["resource", "google_container_cluster", name, "binary_authorization", "evaluation_mode"], []), + } +} + +CxPolicy[result] { + resource := input.document[i].resource.google_container_cluster[name] + ba := resource.binary_authorization + ba.enabled == false + result := { + "documentId": input.document[i].id, + "resourceType": "google_container_cluster", + "resourceName": terra_lib.get_resource_name(resource, name), + "searchKey": sprintf("google_container_cluster[%s].binary_authorization.enabled", [name]), + "issueType": "IncorrectValue", + "keyExpectedValue": sprintf("google_container_cluster[%s].binary_authorization.enabled should be true", [name]), + "keyActualValue": sprintf("google_container_cluster[%s].binary_authorization.enabled is false", [name]), + "searchLine": common_lib.build_search_line(["resource", "google_container_cluster", name, "binary_authorization", "enabled"], []), + } +} diff --git a/assets/queries/terraform/gcp/gke_binary_authorization_disabled/test/negative1.tf b/assets/queries/terraform/gcp/gke_binary_authorization_disabled/test/negative1.tf new file mode 100644 index 00000000000..65dfd4b07f9 --- /dev/null +++ b/assets/queries/terraform/gcp/gke_binary_authorization_disabled/test/negative1.tf @@ -0,0 +1,8 @@ +resource "google_container_cluster" "pass" { + name = "my-gke-cluster" + location = "us-central1" + + binary_authorization { + evaluation_mode = "PROJECT_SINGLETON_POLICY_ENFORCE" + } +} diff --git a/assets/queries/terraform/gcp/gke_binary_authorization_disabled/test/positive1.tf b/assets/queries/terraform/gcp/gke_binary_authorization_disabled/test/positive1.tf new file mode 100644 index 00000000000..4c7b8ca3dd1 --- /dev/null +++ b/assets/queries/terraform/gcp/gke_binary_authorization_disabled/test/positive1.tf @@ -0,0 +1,8 @@ +resource "google_container_cluster" "fail" { + name = "my-gke-cluster" + location = "us-central1" + + binary_authorization { + evaluation_mode = "DISABLED" + } +} diff --git a/assets/queries/terraform/gcp/gke_binary_authorization_disabled/test/positive_expected_result.json b/assets/queries/terraform/gcp/gke_binary_authorization_disabled/test/positive_expected_result.json new file mode 100644 index 00000000000..8efab643824 --- /dev/null +++ b/assets/queries/terraform/gcp/gke_binary_authorization_disabled/test/positive_expected_result.json @@ -0,0 +1,8 @@ +[ + { + "queryName": "Beta - GKE Binary Authorization Disabled", + "severity": "HIGH", + "line": 1, + "filename": "positive1.tf" + } +] \ No newline at end of file diff --git a/assets/queries/terraform/gcp/gke_private_nodes_disabled/metadata.json b/assets/queries/terraform/gcp/gke_private_nodes_disabled/metadata.json new file mode 100644 index 00000000000..ef543a1c98b --- /dev/null +++ b/assets/queries/terraform/gcp/gke_private_nodes_disabled/metadata.json @@ -0,0 +1,13 @@ +{ + "id": "f9905dd3-065c-4d77-825d-61b9d8157e4a", + "queryName": "Beta - GKE Private Nodes Disabled", + "severity": "HIGH", + "category": "Networking and Firewall", + "descriptionText": "GKE cluster nodes should be private (enable_private_nodes=true) so that node VMs only have internal IP addresses, eliminating direct internet exposure and reducing the attack surface.", + "descriptionUrl": "https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/container_cluster#enable_private_nodes", + "platform": "Terraform", + "descriptionID": "b4c5d6e7", + "cloudProvider": "gcp", + "cwe": "284", + "riskScore": "7.0" +} \ No newline at end of file diff --git a/assets/queries/terraform/gcp/gke_private_nodes_disabled/query.rego b/assets/queries/terraform/gcp/gke_private_nodes_disabled/query.rego new file mode 100644 index 00000000000..a51b181851f --- /dev/null +++ b/assets/queries/terraform/gcp/gke_private_nodes_disabled/query.rego @@ -0,0 +1,35 @@ +package Cx + +import data.generic.common as common_lib +import data.generic.terraform as terra_lib + +CxPolicy[result] { + resource := input.document[i].resource.google_container_cluster[name] + not common_lib.valid_key(resource, "private_cluster_config") + result := { + "documentId": input.document[i].id, + "resourceType": "google_container_cluster", + "resourceName": terra_lib.get_resource_name(resource, name), + "searchKey": sprintf("google_container_cluster[%s]", [name]), + "issueType": "MissingAttribute", + "keyExpectedValue": sprintf("google_container_cluster[%s].private_cluster_config.enable_private_nodes should be true", [name]), + "keyActualValue": sprintf("google_container_cluster[%s].private_cluster_config is not defined", [name]), + "searchLine": common_lib.build_search_line(["resource", "google_container_cluster", name], []), + } +} + +CxPolicy[result] { + resource := input.document[i].resource.google_container_cluster[name] + pcc := resource.private_cluster_config + pcc.enable_private_nodes == false + result := { + "documentId": input.document[i].id, + "resourceType": "google_container_cluster", + "resourceName": terra_lib.get_resource_name(resource, name), + "searchKey": sprintf("google_container_cluster[%s].private_cluster_config.enable_private_nodes", [name]), + "issueType": "IncorrectValue", + "keyExpectedValue": sprintf("google_container_cluster[%s].private_cluster_config.enable_private_nodes should be true", [name]), + "keyActualValue": sprintf("google_container_cluster[%s].private_cluster_config.enable_private_nodes is false", [name]), + "searchLine": common_lib.build_search_line(["resource", "google_container_cluster", name, "private_cluster_config", "enable_private_nodes"], []), + } +} diff --git a/assets/queries/terraform/gcp/gke_private_nodes_disabled/test/negative1.tf b/assets/queries/terraform/gcp/gke_private_nodes_disabled/test/negative1.tf new file mode 100644 index 00000000000..2ae4f937f70 --- /dev/null +++ b/assets/queries/terraform/gcp/gke_private_nodes_disabled/test/negative1.tf @@ -0,0 +1,10 @@ +resource "google_container_cluster" "pass" { + name = "my-gke-cluster" + location = "us-central1" + + private_cluster_config { + enable_private_nodes = true + enable_private_endpoint = false + master_ipv4_cidr_block = "172.16.0.0/28" + } +} diff --git a/assets/queries/terraform/gcp/gke_private_nodes_disabled/test/positive1.tf b/assets/queries/terraform/gcp/gke_private_nodes_disabled/test/positive1.tf new file mode 100644 index 00000000000..94ebe26b851 --- /dev/null +++ b/assets/queries/terraform/gcp/gke_private_nodes_disabled/test/positive1.tf @@ -0,0 +1,9 @@ +resource "google_container_cluster" "fail" { + name = "my-gke-cluster" + location = "us-central1" + + private_cluster_config { + enable_private_nodes = false + enable_private_endpoint = false + } +} diff --git a/assets/queries/terraform/gcp/gke_private_nodes_disabled/test/positive_expected_result.json b/assets/queries/terraform/gcp/gke_private_nodes_disabled/test/positive_expected_result.json new file mode 100644 index 00000000000..10b9b4fce02 --- /dev/null +++ b/assets/queries/terraform/gcp/gke_private_nodes_disabled/test/positive_expected_result.json @@ -0,0 +1,8 @@ +[ + { + "queryName": "Beta - GKE Private Nodes Disabled", + "severity": "HIGH", + "line": 1, + "filename": "positive1.tf" + } +] \ No newline at end of file diff --git a/assets/queries/terraform/gcp/memorystore_redis_auth_disabled/metadata.json b/assets/queries/terraform/gcp/memorystore_redis_auth_disabled/metadata.json new file mode 100644 index 00000000000..3f7f1f45f58 --- /dev/null +++ b/assets/queries/terraform/gcp/memorystore_redis_auth_disabled/metadata.json @@ -0,0 +1,13 @@ +{ + "id": "116334b1-bd0a-440c-8e5a-600e1a1ae4e2", + "queryName": "Beta - Memorystore Redis Auth Disabled", + "severity": "HIGH", + "category": "Access Control", + "descriptionText": "Google Cloud Memorystore for Redis instances should have AUTH enabled to require authentication tokens, preventing unauthorized access to Redis data.", + "descriptionUrl": "https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/redis_instance#auth_enabled", + "platform": "Terraform", + "descriptionID": "e1f2a3b4", + "cloudProvider": "gcp", + "cwe": "306", + "riskScore": "7.5" +} \ No newline at end of file diff --git a/assets/queries/terraform/gcp/memorystore_redis_auth_disabled/query.rego b/assets/queries/terraform/gcp/memorystore_redis_auth_disabled/query.rego new file mode 100644 index 00000000000..8e5e4177e1e --- /dev/null +++ b/assets/queries/terraform/gcp/memorystore_redis_auth_disabled/query.rego @@ -0,0 +1,34 @@ +package Cx + +import data.generic.common as common_lib +import data.generic.terraform as terra_lib + +CxPolicy[result] { + resource := input.document[i].resource.google_redis_instance[name] + not common_lib.valid_key(resource, "auth_enabled") + result := { + "documentId": input.document[i].id, + "resourceType": "google_redis_instance", + "resourceName": terra_lib.get_resource_name(resource, name), + "searchKey": sprintf("google_redis_instance[%s]", [name]), + "issueType": "MissingAttribute", + "keyExpectedValue": sprintf("google_redis_instance[%s].auth_enabled should be true", [name]), + "keyActualValue": sprintf("google_redis_instance[%s].auth_enabled is not defined (defaults to false)", [name]), + "searchLine": common_lib.build_search_line(["resource", "google_redis_instance", name], []), + } +} + +CxPolicy[result] { + resource := input.document[i].resource.google_redis_instance[name] + resource.auth_enabled == false + result := { + "documentId": input.document[i].id, + "resourceType": "google_redis_instance", + "resourceName": terra_lib.get_resource_name(resource, name), + "searchKey": sprintf("google_redis_instance[%s].auth_enabled", [name]), + "issueType": "IncorrectValue", + "keyExpectedValue": sprintf("google_redis_instance[%s].auth_enabled should be true", [name]), + "keyActualValue": sprintf("google_redis_instance[%s].auth_enabled is false", [name]), + "searchLine": common_lib.build_search_line(["resource", "google_redis_instance", name, "auth_enabled"], []), + } +} diff --git a/assets/queries/terraform/gcp/memorystore_redis_auth_disabled/test/negative1.tf b/assets/queries/terraform/gcp/memorystore_redis_auth_disabled/test/negative1.tf new file mode 100644 index 00000000000..ec58db49f9a --- /dev/null +++ b/assets/queries/terraform/gcp/memorystore_redis_auth_disabled/test/negative1.tf @@ -0,0 +1,6 @@ +resource "google_redis_instance" "pass" { + name = "my-redis" + memory_size_gb = 1 + region = "us-central1" + auth_enabled = true +} diff --git a/assets/queries/terraform/gcp/memorystore_redis_auth_disabled/test/positive1.tf b/assets/queries/terraform/gcp/memorystore_redis_auth_disabled/test/positive1.tf new file mode 100644 index 00000000000..3a340a211ec --- /dev/null +++ b/assets/queries/terraform/gcp/memorystore_redis_auth_disabled/test/positive1.tf @@ -0,0 +1,6 @@ +resource "google_redis_instance" "fail" { + name = "my-redis" + memory_size_gb = 1 + region = "us-central1" + auth_enabled = false +} diff --git a/assets/queries/terraform/gcp/memorystore_redis_auth_disabled/test/positive_expected_result.json b/assets/queries/terraform/gcp/memorystore_redis_auth_disabled/test/positive_expected_result.json new file mode 100644 index 00000000000..de69018b597 --- /dev/null +++ b/assets/queries/terraform/gcp/memorystore_redis_auth_disabled/test/positive_expected_result.json @@ -0,0 +1,8 @@ +[ + { + "queryName": "Beta - Memorystore Redis Auth Disabled", + "severity": "HIGH", + "line": 1, + "filename": "positive1.tf" + } +] \ No newline at end of file diff --git a/assets/queries/terraform/gcp/memorystore_redis_transit_encryption_disabled/metadata.json b/assets/queries/terraform/gcp/memorystore_redis_transit_encryption_disabled/metadata.json new file mode 100644 index 00000000000..d7b8df5384d --- /dev/null +++ b/assets/queries/terraform/gcp/memorystore_redis_transit_encryption_disabled/metadata.json @@ -0,0 +1,13 @@ +{ + "id": "82d67071-6e5d-4df5-9bdd-eb9ac86822d7", + "queryName": "Beta - Memorystore Redis Transit Encryption Disabled", + "severity": "HIGH", + "category": "Encryption", + "descriptionText": "Google Cloud Memorystore for Redis instances should enable in-transit encryption (TLS) to protect data transmitted between clients and the Redis instance from interception.", + "descriptionUrl": "https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/redis_instance#transit_encryption_mode", + "platform": "Terraform", + "descriptionID": "f2a3b4c5", + "cloudProvider": "gcp", + "cwe": "319", + "riskScore": "7.0" +} \ No newline at end of file diff --git a/assets/queries/terraform/gcp/memorystore_redis_transit_encryption_disabled/query.rego b/assets/queries/terraform/gcp/memorystore_redis_transit_encryption_disabled/query.rego new file mode 100644 index 00000000000..5ae9e0317e7 --- /dev/null +++ b/assets/queries/terraform/gcp/memorystore_redis_transit_encryption_disabled/query.rego @@ -0,0 +1,34 @@ +package Cx + +import data.generic.common as common_lib +import data.generic.terraform as terra_lib + +CxPolicy[result] { + resource := input.document[i].resource.google_redis_instance[name] + not common_lib.valid_key(resource, "transit_encryption_mode") + result := { + "documentId": input.document[i].id, + "resourceType": "google_redis_instance", + "resourceName": terra_lib.get_resource_name(resource, name), + "searchKey": sprintf("google_redis_instance[%s]", [name]), + "issueType": "MissingAttribute", + "keyExpectedValue": sprintf("google_redis_instance[%s].transit_encryption_mode should be SERVER_AUTHENTICATION", [name]), + "keyActualValue": sprintf("google_redis_instance[%s].transit_encryption_mode is not defined (defaults to DISABLED)", [name]), + "searchLine": common_lib.build_search_line(["resource", "google_redis_instance", name], []), + } +} + +CxPolicy[result] { + resource := input.document[i].resource.google_redis_instance[name] + resource.transit_encryption_mode == "DISABLED" + result := { + "documentId": input.document[i].id, + "resourceType": "google_redis_instance", + "resourceName": terra_lib.get_resource_name(resource, name), + "searchKey": sprintf("google_redis_instance[%s].transit_encryption_mode", [name]), + "issueType": "IncorrectValue", + "keyExpectedValue": sprintf("google_redis_instance[%s].transit_encryption_mode should be SERVER_AUTHENTICATION", [name]), + "keyActualValue": sprintf("google_redis_instance[%s].transit_encryption_mode is DISABLED", [name]), + "searchLine": common_lib.build_search_line(["resource", "google_redis_instance", name, "transit_encryption_mode"], []), + } +} diff --git a/assets/queries/terraform/gcp/memorystore_redis_transit_encryption_disabled/test/negative1.tf b/assets/queries/terraform/gcp/memorystore_redis_transit_encryption_disabled/test/negative1.tf new file mode 100644 index 00000000000..0c3452bc98a --- /dev/null +++ b/assets/queries/terraform/gcp/memorystore_redis_transit_encryption_disabled/test/negative1.tf @@ -0,0 +1,6 @@ +resource "google_redis_instance" "pass" { + name = "my-redis" + memory_size_gb = 1 + region = "us-central1" + transit_encryption_mode = "SERVER_AUTHENTICATION" +} diff --git a/assets/queries/terraform/gcp/memorystore_redis_transit_encryption_disabled/test/positive1.tf b/assets/queries/terraform/gcp/memorystore_redis_transit_encryption_disabled/test/positive1.tf new file mode 100644 index 00000000000..01b530afd08 --- /dev/null +++ b/assets/queries/terraform/gcp/memorystore_redis_transit_encryption_disabled/test/positive1.tf @@ -0,0 +1,6 @@ +resource "google_redis_instance" "fail" { + name = "my-redis" + memory_size_gb = 1 + region = "us-central1" + transit_encryption_mode = "DISABLED" +} diff --git a/assets/queries/terraform/gcp/memorystore_redis_transit_encryption_disabled/test/positive_expected_result.json b/assets/queries/terraform/gcp/memorystore_redis_transit_encryption_disabled/test/positive_expected_result.json new file mode 100644 index 00000000000..80443dd7af0 --- /dev/null +++ b/assets/queries/terraform/gcp/memorystore_redis_transit_encryption_disabled/test/positive_expected_result.json @@ -0,0 +1,8 @@ +[ + { + "queryName": "Beta - Memorystore Redis Transit Encryption Disabled", + "severity": "HIGH", + "line": 1, + "filename": "positive1.tf" + } +] \ No newline at end of file diff --git a/assets/queries/terraform/gcp/pubsub_topic_without_cmk/metadata.json b/assets/queries/terraform/gcp/pubsub_topic_without_cmk/metadata.json new file mode 100644 index 00000000000..a6843976008 --- /dev/null +++ b/assets/queries/terraform/gcp/pubsub_topic_without_cmk/metadata.json @@ -0,0 +1,13 @@ +{ + "id": "e5d8e7db-45c9-4db8-b5ed-4ce32695c07d", + "queryName": "Beta - Cloud PubSub Topic Without Customer-Managed Key", + "severity": "MEDIUM", + "category": "Encryption", + "descriptionText": "Google Cloud Pub/Sub topics should use customer-managed encryption keys (CMEK) to give organizations control over the key lifecycle for messages at rest.", + "descriptionUrl": "https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/pubsub_topic#kms_key_name", + "platform": "Terraform", + "descriptionID": "c5d6e7f8", + "cloudProvider": "gcp", + "cwe": "311", + "riskScore": "4.5" +} \ No newline at end of file diff --git a/assets/queries/terraform/gcp/pubsub_topic_without_cmk/query.rego b/assets/queries/terraform/gcp/pubsub_topic_without_cmk/query.rego new file mode 100644 index 00000000000..8cf362b6a26 --- /dev/null +++ b/assets/queries/terraform/gcp/pubsub_topic_without_cmk/query.rego @@ -0,0 +1,19 @@ +package Cx + +import data.generic.common as common_lib +import data.generic.terraform as terra_lib + +CxPolicy[result] { + resource := input.document[i].resource.google_pubsub_topic[name] + not common_lib.valid_key(resource, "kms_key_name") + result := { + "documentId": input.document[i].id, + "resourceType": "google_pubsub_topic", + "resourceName": terra_lib.get_resource_name(resource, name), + "searchKey": sprintf("google_pubsub_topic[%s]", [name]), + "issueType": "MissingAttribute", + "keyExpectedValue": sprintf("google_pubsub_topic[%s].kms_key_name should be defined", [name]), + "keyActualValue": sprintf("google_pubsub_topic[%s].kms_key_name is not defined", [name]), + "searchLine": common_lib.build_search_line(["resource", "google_pubsub_topic", name], []), + } +} diff --git a/assets/queries/terraform/gcp/pubsub_topic_without_cmk/test/negative1.tf b/assets/queries/terraform/gcp/pubsub_topic_without_cmk/test/negative1.tf new file mode 100644 index 00000000000..253666d9bd6 --- /dev/null +++ b/assets/queries/terraform/gcp/pubsub_topic_without_cmk/test/negative1.tf @@ -0,0 +1,4 @@ +resource "google_pubsub_topic" "pass" { + name = "my-topic" + kms_key_name = google_kms_crypto_key.key.id +} diff --git a/assets/queries/terraform/gcp/pubsub_topic_without_cmk/test/positive1.tf b/assets/queries/terraform/gcp/pubsub_topic_without_cmk/test/positive1.tf new file mode 100644 index 00000000000..11af1564e3c --- /dev/null +++ b/assets/queries/terraform/gcp/pubsub_topic_without_cmk/test/positive1.tf @@ -0,0 +1,3 @@ +resource "google_pubsub_topic" "fail" { + name = "my-topic" +} diff --git a/assets/queries/terraform/gcp/pubsub_topic_without_cmk/test/positive_expected_result.json b/assets/queries/terraform/gcp/pubsub_topic_without_cmk/test/positive_expected_result.json new file mode 100644 index 00000000000..ba8d758d012 --- /dev/null +++ b/assets/queries/terraform/gcp/pubsub_topic_without_cmk/test/positive_expected_result.json @@ -0,0 +1,8 @@ +[ + { + "queryName": "Beta - Cloud PubSub Topic Without Customer-Managed Key", + "severity": "MEDIUM", + "line": 1, + "filename": "positive1.tf" + } +] \ No newline at end of file From 384c6f1668d7840b8643a370cf6571ef2fb4cb84 Mon Sep 17 00:00:00 2001 From: Antero Silva Date: Mon, 9 Mar 2026 22:05:32 +0000 Subject: [PATCH 3/3] feat(queries): add experimental flag to all new Beta query metadata files Set "experimental": "true" in metadata.json for all 32 newly created Beta queries across AWS, Azure, GCP, K8s, Dockerfile, and Ansible platforms, aligning with the existing KICS convention for Beta queries. Co-Authored-By: Claude Sonnet 4.6 --- .../general/apt_allow_unauthenticated_packages/metadata.json | 3 ++- .../ansible/general/get_url_ssl_verify_disabled/metadata.json | 3 ++- .../general/package_manager_gpg_check_disabled/metadata.json | 3 ++- .../general/pip_install_with_no_index_disabled/metadata.json | 3 ++- .../ansible/general/uri_ssl_verify_disabled/metadata.json | 3 ++- .../dockerfile/env_instruction_has_secret/metadata.json | 3 ++- .../k8s/container_seccomp_profile_not_set/metadata.json | 3 ++- assets/queries/k8s/ingress_without_tls/metadata.json | 3 ++- .../terraform/aws/backup_vault_without_kms_key/metadata.json | 3 ++- .../cognito_identity_pool_allows_unauthenticated/metadata.json | 3 ++- .../elasticsearch_domain_without_audit_logging/metadata.json | 3 ++- .../lambda_function_url_without_authentication/metadata.json | 3 ++- .../lambda_function_without_reserved_concurrency/metadata.json | 3 ++- assets/queries/terraform/aws/lambda_not_in_vpc/metadata.json | 3 ++- .../terraform/aws/rds_multi_az_not_enabled/metadata.json | 3 ++- .../transfer_family_server_publicly_accessible/metadata.json | 3 ++- .../azure/aks_defender_profile_disabled/metadata.json | 3 ++- .../azure/app_service_remote_debugging_enabled/metadata.json | 3 ++- .../terraform/azure/cosmosdb_account_without_cmk/metadata.json | 3 ++- .../azure/cosmosdb_local_authentication_enabled/metadata.json | 3 ++- .../data_factory_public_network_access_enabled/metadata.json | 3 ++- .../key_vault_public_network_access_enabled/metadata.json | 3 ++- .../azure/machine_learning_workspace_without_cmk/metadata.json | 3 ++- .../metadata.json | 3 ++- .../azure/service_bus_namespace_without_cmk/metadata.json | 3 ++- .../artifact_registry_repository_public_access/metadata.json | 3 ++- .../gcp/cloud_run_service_allows_unauthenticated/metadata.json | 3 ++- .../gcp/gke_binary_authorization_disabled/metadata.json | 3 ++- .../terraform/gcp/gke_private_nodes_disabled/metadata.json | 3 ++- .../gcp/memorystore_redis_auth_disabled/metadata.json | 3 ++- .../metadata.json | 3 ++- .../terraform/gcp/pubsub_topic_without_cmk/metadata.json | 3 ++- .../gcp/secret_manager_secret_without_cmek/metadata.json | 3 ++- 33 files changed, 66 insertions(+), 33 deletions(-) diff --git a/assets/queries/ansible/general/apt_allow_unauthenticated_packages/metadata.json b/assets/queries/ansible/general/apt_allow_unauthenticated_packages/metadata.json index 83fe572b3dc..a1bd6aa761b 100644 --- a/assets/queries/ansible/general/apt_allow_unauthenticated_packages/metadata.json +++ b/assets/queries/ansible/general/apt_allow_unauthenticated_packages/metadata.json @@ -9,5 +9,6 @@ "descriptionID": "a9b0c1d2", "cloudProvider": "common", "cwe": "494", - "riskScore": "8.0" + "riskScore": "8.0", + "experimental": "true" } \ No newline at end of file diff --git a/assets/queries/ansible/general/get_url_ssl_verify_disabled/metadata.json b/assets/queries/ansible/general/get_url_ssl_verify_disabled/metadata.json index 21d9fece835..f0adb9faf53 100644 --- a/assets/queries/ansible/general/get_url_ssl_verify_disabled/metadata.json +++ b/assets/queries/ansible/general/get_url_ssl_verify_disabled/metadata.json @@ -9,5 +9,6 @@ "descriptionID": "e7f8a9b0", "cloudProvider": "common", "cwe": "295", - "riskScore": "7.5" + "riskScore": "7.5", + "experimental": "true" } \ No newline at end of file diff --git a/assets/queries/ansible/general/package_manager_gpg_check_disabled/metadata.json b/assets/queries/ansible/general/package_manager_gpg_check_disabled/metadata.json index f015276aa2c..31fe2ac26a3 100644 --- a/assets/queries/ansible/general/package_manager_gpg_check_disabled/metadata.json +++ b/assets/queries/ansible/general/package_manager_gpg_check_disabled/metadata.json @@ -9,5 +9,6 @@ "descriptionID": "f8a9b0c1", "cloudProvider": "common", "cwe": "494", - "riskScore": "8.0" + "riskScore": "8.0", + "experimental": "true" } \ No newline at end of file diff --git a/assets/queries/ansible/general/pip_install_with_no_index_disabled/metadata.json b/assets/queries/ansible/general/pip_install_with_no_index_disabled/metadata.json index 8feb31eec41..522a5b18495 100644 --- a/assets/queries/ansible/general/pip_install_with_no_index_disabled/metadata.json +++ b/assets/queries/ansible/general/pip_install_with_no_index_disabled/metadata.json @@ -9,5 +9,6 @@ "descriptionID": "b0c1d2e3", "cloudProvider": "common", "cwe": "494", - "riskScore": "7.0" + "riskScore": "7.0", + "experimental": "true" } \ No newline at end of file diff --git a/assets/queries/ansible/general/uri_ssl_verify_disabled/metadata.json b/assets/queries/ansible/general/uri_ssl_verify_disabled/metadata.json index 9ee761ac188..bee2ed653e8 100644 --- a/assets/queries/ansible/general/uri_ssl_verify_disabled/metadata.json +++ b/assets/queries/ansible/general/uri_ssl_verify_disabled/metadata.json @@ -9,5 +9,6 @@ "descriptionID": "d6e7f8a9", "cloudProvider": "common", "cwe": "295", - "riskScore": "7.5" + "riskScore": "7.5", + "experimental": "true" } \ No newline at end of file diff --git a/assets/queries/dockerfile/env_instruction_has_secret/metadata.json b/assets/queries/dockerfile/env_instruction_has_secret/metadata.json index 5a1db5890cd..e6d01cb3d7b 100644 --- a/assets/queries/dockerfile/env_instruction_has_secret/metadata.json +++ b/assets/queries/dockerfile/env_instruction_has_secret/metadata.json @@ -9,5 +9,6 @@ "descriptionID": "7abccf53", "cloudProvider": "common", "cwe": "798", - "riskScore": "8.5" + "riskScore": "8.5", + "experimental": "true" } \ No newline at end of file diff --git a/assets/queries/k8s/container_seccomp_profile_not_set/metadata.json b/assets/queries/k8s/container_seccomp_profile_not_set/metadata.json index 23da50a48b1..1f696f0ceae 100644 --- a/assets/queries/k8s/container_seccomp_profile_not_set/metadata.json +++ b/assets/queries/k8s/container_seccomp_profile_not_set/metadata.json @@ -9,5 +9,6 @@ "descriptionID": "30a7f7cc", "cloudProvider": "common", "cwe": "250", - "riskScore": "5.5" + "riskScore": "5.5", + "experimental": "true" } \ No newline at end of file diff --git a/assets/queries/k8s/ingress_without_tls/metadata.json b/assets/queries/k8s/ingress_without_tls/metadata.json index af9fe6f2814..bf88ab507af 100644 --- a/assets/queries/k8s/ingress_without_tls/metadata.json +++ b/assets/queries/k8s/ingress_without_tls/metadata.json @@ -9,5 +9,6 @@ "descriptionID": "0dcbf4a2", "cloudProvider": "common", "cwe": "319", - "riskScore": "7.5" + "riskScore": "7.5", + "experimental": "true" } \ No newline at end of file diff --git a/assets/queries/terraform/aws/backup_vault_without_kms_key/metadata.json b/assets/queries/terraform/aws/backup_vault_without_kms_key/metadata.json index 33dbb51be4d..fa257ecf6bb 100644 --- a/assets/queries/terraform/aws/backup_vault_without_kms_key/metadata.json +++ b/assets/queries/terraform/aws/backup_vault_without_kms_key/metadata.json @@ -9,5 +9,6 @@ "descriptionID": "6df8a84c", "cloudProvider": "aws", "cwe": "311", - "riskScore": "5.5" + "riskScore": "5.5", + "experimental": "true" } \ No newline at end of file diff --git a/assets/queries/terraform/aws/cognito_identity_pool_allows_unauthenticated/metadata.json b/assets/queries/terraform/aws/cognito_identity_pool_allows_unauthenticated/metadata.json index ca4f46fb286..884bfeb2cfe 100644 --- a/assets/queries/terraform/aws/cognito_identity_pool_allows_unauthenticated/metadata.json +++ b/assets/queries/terraform/aws/cognito_identity_pool_allows_unauthenticated/metadata.json @@ -9,5 +9,6 @@ "descriptionID": "c3d4e5f6", "cloudProvider": "aws", "cwe": "306", - "riskScore": "7.5" + "riskScore": "7.5", + "experimental": "true" } \ No newline at end of file diff --git a/assets/queries/terraform/aws/elasticsearch_domain_without_audit_logging/metadata.json b/assets/queries/terraform/aws/elasticsearch_domain_without_audit_logging/metadata.json index 5d086468eca..5e3f11eccad 100644 --- a/assets/queries/terraform/aws/elasticsearch_domain_without_audit_logging/metadata.json +++ b/assets/queries/terraform/aws/elasticsearch_domain_without_audit_logging/metadata.json @@ -9,5 +9,6 @@ "descriptionID": "e5f6a7b8", "cloudProvider": "aws", "cwe": "778", - "riskScore": "5.0" + "riskScore": "5.0", + "experimental": "true" } \ No newline at end of file diff --git a/assets/queries/terraform/aws/lambda_function_url_without_authentication/metadata.json b/assets/queries/terraform/aws/lambda_function_url_without_authentication/metadata.json index 20fe36e724d..951bcf7bc3e 100644 --- a/assets/queries/terraform/aws/lambda_function_url_without_authentication/metadata.json +++ b/assets/queries/terraform/aws/lambda_function_url_without_authentication/metadata.json @@ -9,5 +9,6 @@ "descriptionID": "1c4bf6dc", "cloudProvider": "aws", "cwe": "306", - "riskScore": "8.1" + "riskScore": "8.1", + "experimental": "true" } \ No newline at end of file diff --git a/assets/queries/terraform/aws/lambda_function_without_reserved_concurrency/metadata.json b/assets/queries/terraform/aws/lambda_function_without_reserved_concurrency/metadata.json index 25429cda9cb..3af6c654d04 100644 --- a/assets/queries/terraform/aws/lambda_function_without_reserved_concurrency/metadata.json +++ b/assets/queries/terraform/aws/lambda_function_without_reserved_concurrency/metadata.json @@ -9,5 +9,6 @@ "descriptionID": "d4e5f6a7", "cloudProvider": "aws", "cwe": "400", - "riskScore": "3.5" + "riskScore": "3.5", + "experimental": "true" } \ No newline at end of file diff --git a/assets/queries/terraform/aws/lambda_not_in_vpc/metadata.json b/assets/queries/terraform/aws/lambda_not_in_vpc/metadata.json index 4746dc3d0ac..b9fe7b4f66c 100644 --- a/assets/queries/terraform/aws/lambda_not_in_vpc/metadata.json +++ b/assets/queries/terraform/aws/lambda_not_in_vpc/metadata.json @@ -9,5 +9,6 @@ "descriptionID": "a1b2c3d4", "cloudProvider": "aws", "cwe": "668", - "riskScore": "3.0" + "riskScore": "3.0", + "experimental": "true" } \ No newline at end of file diff --git a/assets/queries/terraform/aws/rds_multi_az_not_enabled/metadata.json b/assets/queries/terraform/aws/rds_multi_az_not_enabled/metadata.json index 507c5cf311d..197a4ba171e 100644 --- a/assets/queries/terraform/aws/rds_multi_az_not_enabled/metadata.json +++ b/assets/queries/terraform/aws/rds_multi_az_not_enabled/metadata.json @@ -9,5 +9,6 @@ "descriptionID": "b2c3d4e5", "cloudProvider": "aws", "cwe": "778", - "riskScore": "4.5" + "riskScore": "4.5", + "experimental": "true" } \ No newline at end of file diff --git a/assets/queries/terraform/aws/transfer_family_server_publicly_accessible/metadata.json b/assets/queries/terraform/aws/transfer_family_server_publicly_accessible/metadata.json index 79ef7dfce2a..a19da0c694d 100644 --- a/assets/queries/terraform/aws/transfer_family_server_publicly_accessible/metadata.json +++ b/assets/queries/terraform/aws/transfer_family_server_publicly_accessible/metadata.json @@ -9,5 +9,6 @@ "descriptionID": "1d9081ec", "cloudProvider": "aws", "cwe": "668", - "riskScore": "7.5" + "riskScore": "7.5", + "experimental": "true" } \ No newline at end of file diff --git a/assets/queries/terraform/azure/aks_defender_profile_disabled/metadata.json b/assets/queries/terraform/azure/aks_defender_profile_disabled/metadata.json index ca0c025277d..aa5a36a631c 100644 --- a/assets/queries/terraform/azure/aks_defender_profile_disabled/metadata.json +++ b/assets/queries/terraform/azure/aks_defender_profile_disabled/metadata.json @@ -9,5 +9,6 @@ "descriptionID": "f8f4f141", "cloudProvider": "azure", "cwe": "778", - "riskScore": "5.3" + "riskScore": "5.3", + "experimental": "true" } \ No newline at end of file diff --git a/assets/queries/terraform/azure/app_service_remote_debugging_enabled/metadata.json b/assets/queries/terraform/azure/app_service_remote_debugging_enabled/metadata.json index e8998905c8f..ae6adb77597 100644 --- a/assets/queries/terraform/azure/app_service_remote_debugging_enabled/metadata.json +++ b/assets/queries/terraform/azure/app_service_remote_debugging_enabled/metadata.json @@ -9,5 +9,6 @@ "descriptionID": "f6a7b8c9", "cloudProvider": "azure", "cwe": "94", - "riskScore": "7.5" + "riskScore": "7.5", + "experimental": "true" } \ No newline at end of file diff --git a/assets/queries/terraform/azure/cosmosdb_account_without_cmk/metadata.json b/assets/queries/terraform/azure/cosmosdb_account_without_cmk/metadata.json index 69915d12281..4f9d872b045 100644 --- a/assets/queries/terraform/azure/cosmosdb_account_without_cmk/metadata.json +++ b/assets/queries/terraform/azure/cosmosdb_account_without_cmk/metadata.json @@ -9,5 +9,6 @@ "descriptionID": "a7b8c9d0", "cloudProvider": "azure", "cwe": "311", - "riskScore": "5.0" + "riskScore": "5.0", + "experimental": "true" } \ No newline at end of file diff --git a/assets/queries/terraform/azure/cosmosdb_local_authentication_enabled/metadata.json b/assets/queries/terraform/azure/cosmosdb_local_authentication_enabled/metadata.json index 63384487757..a6995ef75ec 100644 --- a/assets/queries/terraform/azure/cosmosdb_local_authentication_enabled/metadata.json +++ b/assets/queries/terraform/azure/cosmosdb_local_authentication_enabled/metadata.json @@ -9,5 +9,6 @@ "descriptionID": "b8c9d0e1", "cloudProvider": "azure", "cwe": "287", - "riskScore": "5.5" + "riskScore": "5.5", + "experimental": "true" } \ No newline at end of file diff --git a/assets/queries/terraform/azure/data_factory_public_network_access_enabled/metadata.json b/assets/queries/terraform/azure/data_factory_public_network_access_enabled/metadata.json index bcf501ca347..b536a8457a6 100644 --- a/assets/queries/terraform/azure/data_factory_public_network_access_enabled/metadata.json +++ b/assets/queries/terraform/azure/data_factory_public_network_access_enabled/metadata.json @@ -9,5 +9,6 @@ "descriptionID": "d0e1f2a3", "cloudProvider": "azure", "cwe": "668", - "riskScore": "7.0" + "riskScore": "7.0", + "experimental": "true" } \ No newline at end of file diff --git a/assets/queries/terraform/azure/key_vault_public_network_access_enabled/metadata.json b/assets/queries/terraform/azure/key_vault_public_network_access_enabled/metadata.json index e47c41e6712..62ba978d168 100644 --- a/assets/queries/terraform/azure/key_vault_public_network_access_enabled/metadata.json +++ b/assets/queries/terraform/azure/key_vault_public_network_access_enabled/metadata.json @@ -9,5 +9,6 @@ "descriptionID": "c9d0e1f2", "cloudProvider": "azure", "cwe": "749", - "riskScore": "7.0" + "riskScore": "7.0", + "experimental": "true" } \ No newline at end of file diff --git a/assets/queries/terraform/azure/machine_learning_workspace_without_cmk/metadata.json b/assets/queries/terraform/azure/machine_learning_workspace_without_cmk/metadata.json index 917f45a6b1e..7d5b42849c5 100644 --- a/assets/queries/terraform/azure/machine_learning_workspace_without_cmk/metadata.json +++ b/assets/queries/terraform/azure/machine_learning_workspace_without_cmk/metadata.json @@ -9,5 +9,6 @@ "descriptionID": "b489594d", "cloudProvider": "azure", "cwe": "311", - "riskScore": "5.0" + "riskScore": "5.0", + "experimental": "true" } \ No newline at end of file diff --git a/assets/queries/terraform/azure/redis_cache_not_using_latest_tls_encryption_version/metadata.json b/assets/queries/terraform/azure/redis_cache_not_using_latest_tls_encryption_version/metadata.json index 685516d6ae9..3e18691b73a 100644 --- a/assets/queries/terraform/azure/redis_cache_not_using_latest_tls_encryption_version/metadata.json +++ b/assets/queries/terraform/azure/redis_cache_not_using_latest_tls_encryption_version/metadata.json @@ -9,5 +9,6 @@ "descriptionID": "e22e5620", "cloudProvider": "azure", "cwe": "326", - "riskScore": "3.0" + "riskScore": "3.0", + "experimental": "true" } \ No newline at end of file diff --git a/assets/queries/terraform/azure/service_bus_namespace_without_cmk/metadata.json b/assets/queries/terraform/azure/service_bus_namespace_without_cmk/metadata.json index 9e4d6ba56c9..c3894c8e6db 100644 --- a/assets/queries/terraform/azure/service_bus_namespace_without_cmk/metadata.json +++ b/assets/queries/terraform/azure/service_bus_namespace_without_cmk/metadata.json @@ -9,5 +9,6 @@ "descriptionID": "39e5eb1f", "cloudProvider": "azure", "cwe": "311", - "riskScore": "3.0" + "riskScore": "3.0", + "experimental": "true" } \ No newline at end of file diff --git a/assets/queries/terraform/gcp/artifact_registry_repository_public_access/metadata.json b/assets/queries/terraform/gcp/artifact_registry_repository_public_access/metadata.json index 6a084aa512b..cc85c77d0df 100644 --- a/assets/queries/terraform/gcp/artifact_registry_repository_public_access/metadata.json +++ b/assets/queries/terraform/gcp/artifact_registry_repository_public_access/metadata.json @@ -9,5 +9,6 @@ "descriptionID": "3116634b", "cloudProvider": "gcp", "cwe": "284", - "riskScore": "8.2" + "riskScore": "8.2", + "experimental": "true" } \ No newline at end of file diff --git a/assets/queries/terraform/gcp/cloud_run_service_allows_unauthenticated/metadata.json b/assets/queries/terraform/gcp/cloud_run_service_allows_unauthenticated/metadata.json index 06cc2ade65a..ad6bf39b261 100644 --- a/assets/queries/terraform/gcp/cloud_run_service_allows_unauthenticated/metadata.json +++ b/assets/queries/terraform/gcp/cloud_run_service_allows_unauthenticated/metadata.json @@ -9,5 +9,6 @@ "descriptionID": "b89d8324", "cloudProvider": "gcp", "cwe": "306", - "riskScore": "8.0" + "riskScore": "8.0", + "experimental": "true" } \ No newline at end of file diff --git a/assets/queries/terraform/gcp/gke_binary_authorization_disabled/metadata.json b/assets/queries/terraform/gcp/gke_binary_authorization_disabled/metadata.json index 07c3d7162f9..fb3880a2fe2 100644 --- a/assets/queries/terraform/gcp/gke_binary_authorization_disabled/metadata.json +++ b/assets/queries/terraform/gcp/gke_binary_authorization_disabled/metadata.json @@ -9,5 +9,6 @@ "descriptionID": "a3b4c5d6", "cloudProvider": "gcp", "cwe": "829", - "riskScore": "7.5" + "riskScore": "7.5", + "experimental": "true" } \ No newline at end of file diff --git a/assets/queries/terraform/gcp/gke_private_nodes_disabled/metadata.json b/assets/queries/terraform/gcp/gke_private_nodes_disabled/metadata.json index ef543a1c98b..12016ab5bf7 100644 --- a/assets/queries/terraform/gcp/gke_private_nodes_disabled/metadata.json +++ b/assets/queries/terraform/gcp/gke_private_nodes_disabled/metadata.json @@ -9,5 +9,6 @@ "descriptionID": "b4c5d6e7", "cloudProvider": "gcp", "cwe": "284", - "riskScore": "7.0" + "riskScore": "7.0", + "experimental": "true" } \ No newline at end of file diff --git a/assets/queries/terraform/gcp/memorystore_redis_auth_disabled/metadata.json b/assets/queries/terraform/gcp/memorystore_redis_auth_disabled/metadata.json index 3f7f1f45f58..f2d278a230e 100644 --- a/assets/queries/terraform/gcp/memorystore_redis_auth_disabled/metadata.json +++ b/assets/queries/terraform/gcp/memorystore_redis_auth_disabled/metadata.json @@ -9,5 +9,6 @@ "descriptionID": "e1f2a3b4", "cloudProvider": "gcp", "cwe": "306", - "riskScore": "7.5" + "riskScore": "7.5", + "experimental": "true" } \ No newline at end of file diff --git a/assets/queries/terraform/gcp/memorystore_redis_transit_encryption_disabled/metadata.json b/assets/queries/terraform/gcp/memorystore_redis_transit_encryption_disabled/metadata.json index d7b8df5384d..106288e532f 100644 --- a/assets/queries/terraform/gcp/memorystore_redis_transit_encryption_disabled/metadata.json +++ b/assets/queries/terraform/gcp/memorystore_redis_transit_encryption_disabled/metadata.json @@ -9,5 +9,6 @@ "descriptionID": "f2a3b4c5", "cloudProvider": "gcp", "cwe": "319", - "riskScore": "7.0" + "riskScore": "7.0", + "experimental": "true" } \ No newline at end of file diff --git a/assets/queries/terraform/gcp/pubsub_topic_without_cmk/metadata.json b/assets/queries/terraform/gcp/pubsub_topic_without_cmk/metadata.json index a6843976008..939f5a225e6 100644 --- a/assets/queries/terraform/gcp/pubsub_topic_without_cmk/metadata.json +++ b/assets/queries/terraform/gcp/pubsub_topic_without_cmk/metadata.json @@ -9,5 +9,6 @@ "descriptionID": "c5d6e7f8", "cloudProvider": "gcp", "cwe": "311", - "riskScore": "4.5" + "riskScore": "4.5", + "experimental": "true" } \ No newline at end of file diff --git a/assets/queries/terraform/gcp/secret_manager_secret_without_cmek/metadata.json b/assets/queries/terraform/gcp/secret_manager_secret_without_cmek/metadata.json index 81c865d3a7a..e6f9b47e501 100644 --- a/assets/queries/terraform/gcp/secret_manager_secret_without_cmek/metadata.json +++ b/assets/queries/terraform/gcp/secret_manager_secret_without_cmek/metadata.json @@ -9,5 +9,6 @@ "descriptionID": "9be17ab9", "cloudProvider": "gcp", "cwe": "311", - "riskScore": "5.0" + "riskScore": "5.0", + "experimental": "true" } \ No newline at end of file