Skip to content

Commit

Permalink
Merge pull request #64 from DataDog/bahar.shah/K9VULN-3698
Browse files Browse the repository at this point in the history
[K9VULN-3698] Add GCP rules to match cloud security baseline
  • Loading branch information
bahar-shah authored Feb 26, 2025
2 parents a5e0b18 + 22fef00 commit 49c8875
Show file tree
Hide file tree
Showing 80 changed files with 1,759 additions and 1 deletion.
8 changes: 8 additions & 0 deletions assets/libraries/common.rego
Original file line number Diff line number Diff line change
Expand Up @@ -749,3 +749,11 @@ valid_non_empty_key(field, key) = output {
keyObj == ""
output := concat(".", ["", key])
}

# Helper to ensure that a value is always treated as an array.
as_array(x) = y {
is_array(x)
y = x
} else = [x] {
not is_array(x)
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"id": "a2b3c4d5-e6f7-8901-gh23-ijkl456m7890",
"queryName": "Team Tag Missing",
"severity": "MEDIUM",
"severity": "INFO",
"category": "Best Practices",
"descriptionText": "Ensures that every cloud resource has a 'Team' tag for ownership tracking.",
"descriptionUrl": "https://your-cloud-policy-docs.com/enforce-team-tag",
Expand Down
6 changes: 6 additions & 0 deletions assets/queries/terraform/aws/team_tag_not_present/query.rego
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ required_tags := {"Team"}

# Case where "tags" exists but required tags are missing
CxPolicy[result] {
# Only consider resources whose type begins with "aws_"
startswith(resource_name, "aws_")

resource_type := input.document[i].resource[resource_name][name]
common_lib.valid_key(resource_type, "tags")

Expand Down Expand Up @@ -35,6 +38,9 @@ CxPolicy[result] {

# Case where "tags" block is completely missing
CxPolicy[result] {
# Only consider resources whose type begins with "aws_"
startswith(resource_type, "aws_")

resource := input.document[i].resource[resource_type][name]
not common_lib.valid_key(resource, "tags")

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"id": "a7b8c9d0-e1f2-3a4b-5c6d-7e8f90123456",
"queryName": "Artifact Registry Repo Is Public",
"severity": "HIGH",
"category": "Access Control",
"descriptionText": "Artifact Registry repositories must not be publicly accessible. IAM members or bindings should not use public principals such as 'allUsers' or 'allAuthenticatedUsers'.",
"descriptionUrl": "https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/artifact_registry_repository_iam_member",
"platform": "Terraform",
"descriptionID": "a7b8c9d0",
"cloudProvider": "gcp",
"cwe": "284"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package Cx

import data.generic.terraform as tf_lib
import data.generic.common as common_lib

# Check for google_artifact_registry_repository_iam_member
CxPolicy[result] {
resource := input.document[i].resource.google_artifact_registry_repository_iam_member[name]
common_lib.valid_key(resource, "member")
member := resource.member
member == "allAuthenticatedUsers"

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]),
"searchLine": common_lib.build_search_line(["resource", "google_artifact_registry_repository_iam_member", name, "member"], []),
"issueType": "IncorrectValue",
"keyExpectedValue": "IAM member should not be a public principal",
"keyActualValue": sprintf("Found public principal: %s", [member]),
"remediation": json.marshal({
"before": "member = \"allUsers\" or \"allAuthenticatedUsers\"",
"after": "Use a non-public principal"
}),
"remediationType": "replacement"
}
}

# Check for google_artifact_registry_repository_iam_member
CxPolicy[result] {
resource := input.document[i].resource.google_artifact_registry_repository_iam_member[name]
common_lib.valid_key(resource, "member")
member := resource.member
member == "allUsers"

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]),
"searchLine": common_lib.build_search_line(["resource", "google_artifact_registry_repository_iam_member", name, "member"], []),
"issueType": "IncorrectValue",
"keyExpectedValue": "IAM member should not be a public principal",
"keyActualValue": sprintf("Found public principal: %s", [member]),
"remediation": json.marshal({
"before": "member = \"allUsers\" or \"allAuthenticatedUsers\"",
"after": "Use a non-public principal"
}),
"remediationType": "replacement"
}
}

# Check for google_artifact_registry_repository_iam_binding
CxPolicy[result] {
resource := input.document[i].resource.google_artifact_registry_repository_iam_binding[name]
common_lib.valid_key(resource, "members")
member := resource.members[_]
member == "allAuthenticatedUsers"

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]),
"searchLine": common_lib.build_search_line(["resource", "google_artifact_registry_repository_iam_binding", name, "members"], []),
"issueType": "IncorrectValue",
"keyExpectedValue": "IAM binding should not include public principals",
"keyActualValue": "Public principal found in members",
"remediation": json.marshal({
"before": "members includes \"allUsers\" or \"allAuthenticatedUsers\"",
"after": "Remove public principals from members"
}),
"remediationType": "replacement"
}
}

# Check for google_artifact_registry_repository_iam_binding
CxPolicy[result] {
resource := input.document[i].resource.google_artifact_registry_repository_iam_binding[name]
common_lib.valid_key(resource, "members")
member := resource.members[_]
member == "allUsers"

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]),
"searchLine": common_lib.build_search_line(["resource", "google_artifact_registry_repository_iam_binding", name, "members"], []),
"issueType": "IncorrectValue",
"keyExpectedValue": "IAM binding should not include public principals",
"keyActualValue": "Public principal found in members",
"remediation": json.marshal({
"before": "members includes \"allUsers\" or \"allAuthenticatedUsers\"",
"after": "Remove public principals from members"
}),
"remediationType": "replacement"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

# IAM Binding compliant
resource "google_artifact_registry_repository_iam_binding" "good_example_binding" {
repository = "example-repo"
members = ["user:[email protected]", "group:[email protected]"] # ✅ No public principals
role = "roles/artifactregistry.admin"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# IAM Member compliant
resource "google_artifact_registry_repository_iam_member" "good_example_member" {
repository = "example-repo"
member = "user:[email protected]" # ✅ Non-public principal
role = "roles/artifactregistry.reader"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# IAM Member violation
resource "google_artifact_registry_repository_iam_member" "bad_example_member" {
repository = "example-repo"
member = "allUsers" # ❌ Public principal
role = "roles/artifactregistry.reader"
}

# IAM Binding violation
resource "google_artifact_registry_repository_iam_binding" "bad_example_binding" {
repository = "example-repo"
members = ["allAuthenticatedUsers", "user:[email protected]"] # ❌ Contains public principal
role = "roles/artifactregistry.admin"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[
{
"queryName": "Artifact Registry Repo Is Public",
"severity": "MEDIUM",
"line": 4
},
{
"queryName": "Artifact Registry Repo Is Public",
"severity": "MEDIUM",
"line": 11
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"id": "a9b8c7d6-e5f4-3210-abcd-1234567890ab",
"queryName": "BigQuery Table Is Public",
"severity": "HIGH",
"category": "General Security",
"descriptionText": "BigQuery tables must not be publicly accessible. Public principals like 'allUsers' or 'allAuthenticatedUsers' should not be assigned as IAM members or bindings, as this may expose sensitive data.",
"descriptionUrl": "https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/bigquery_table_iam_member",
"platform": "Terraform",
"descriptionID": "a9b8c7d6",
"cloudProvider": "gcp",
"cwe": "284"
}
101 changes: 101 additions & 0 deletions assets/queries/terraform/gcp/bigquery_table_is_public/query.rego
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package Cx

import data.generic.terraform as tf_lib
import data.generic.common as common_lib

# For google_bigquery_table_iam_member resources
CxPolicy[result] {
resource := input.document[i].resource.google_bigquery_table_iam_member[name]
common_lib.valid_key(resource, "member")
member := resource.member
member == "allUsers"

result := {
"documentId": input.document[i].id,
"resourceType": "google_bigquery_table_iam_member",
"resourceName": tf_lib.get_resource_name(resource, name),
"searchKey": sprintf("google_bigquery_table_iam_member[{{%s}}].member", [name]),
"searchLine": common_lib.build_search_line(["resource", "google_bigquery_table_iam_member", name, "member"], []),
"issueType": "IncorrectValue",
"keyExpectedValue": "IAM member should not be a public principal",
"keyActualValue": sprintf("Found public principal: %s", [member]),
"remediation": json.marshal({
"before": "member = \"allUsers\" or \"allAuthenticatedUsers\"",
"after": "Use a non-public principal"
}),
"remediationType": "replacement"
}
}

# For google_bigquery_table_iam_member resources
CxPolicy[result] {
resource := input.document[i].resource.google_bigquery_table_iam_member[name]
common_lib.valid_key(resource, "member")
common_lib.valid_key(resource, "member")
member := resource.member
member == "allAuthenticatedUsers"

result := {
"documentId": input.document[i].id,
"resourceType": "google_bigquery_table_iam_member",
"resourceName": tf_lib.get_resource_name(resource, name),
"searchKey": sprintf("google_bigquery_table_iam_member[{{%s}}].member", [name]),
"searchLine": common_lib.build_search_line(["resource", "google_bigquery_table_iam_member", name, "member"], []),
"issueType": "IncorrectValue",
"keyExpectedValue": "IAM member should not be a public principal",
"keyActualValue": sprintf("Found public principal: %s", [member]),
"remediation": json.marshal({
"before": "member = \"allUsers\" or \"allAuthenticatedUsers\"",
"after": "Use a non-public principal"
}),
"remediationType": "replacement"
}
}

# For google_bigquery_table_iam_binding resources
CxPolicy[result] {
resource := input.document[i].resource.google_bigquery_table_iam_binding[name]
common_lib.valid_key(resource, "members")
member := resource.members[_]
member == "allUsers"

result := {
"documentId": input.document[i].id,
"resourceType": "google_bigquery_table_iam_binding",
"resourceName": tf_lib.get_resource_name(resource, name),
"searchKey": sprintf("google_bigquery_table_iam_binding[{{%s}}].members", [name]),
"searchLine": common_lib.build_search_line(["resource", "google_bigquery_table_iam_binding", name, "members"], []),
"issueType": "IncorrectValue",
"keyExpectedValue": "IAM binding should not include public principals",
"keyActualValue": "Public principal found in members",
"remediation": json.marshal({
"before": "members includes \"allUsers\" or \"allAuthenticatedUsers\"",
"after": "Remove public principals from members"
}),
"remediationType": "replacement"
}
}

# For google_bigquery_table_iam_binding resources
CxPolicy[result] {
resource := input.document[i].resource.google_bigquery_table_iam_binding[name]
common_lib.valid_key(resource, "members")
member := resource.members[_]
member == "allAuthenticatedUsers"

result := {
"documentId": input.document[i].id,
"resourceType": "google_bigquery_table_iam_binding",
"resourceName": tf_lib.get_resource_name(resource, name),
"searchKey": sprintf("google_bigquery_table_iam_binding[{{%s}}].members", [name]),
"searchLine": common_lib.build_search_line(["resource", "google_bigquery_table_iam_binding", name, "members"], []),
"issueType": "IncorrectValue",
"keyExpectedValue": "IAM binding should not include public principals",
"keyActualValue": "Public principal found in members",
"remediation": json.marshal({
"before": "members includes \"allUsers\" or \"allAuthenticatedUsers\"",
"after": "Remove public principals from members"
}),
"remediationType": "replacement"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# IAM Member compliant
resource "google_bigquery_table_iam_member" "good_example_member" {
table = "example_table"
member = "user:[email protected]" # ✅ Non-public principal
role = "roles/bigquery.dataViewer"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# IAM Binding compliant
resource "google_bigquery_table_iam_binding" "good_example_binding" {
table = "example_table"
members = ["user:[email protected]", "group:[email protected]"] # ✅ No public principals
role = "roles/bigquery.dataViewer"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# IAM Member violation
resource "google_bigquery_table_iam_member" "bad_example_member" {
table = "example_table"
member = "allUsers" # ❌ Public principal
role = "roles/bigquery.dataViewer"
}

# IAM Binding violation
resource "google_bigquery_table_iam_binding" "bad_example_binding" {
table = "example_table"
members = ["allAuthenticatedUsers", "user:[email protected]"] # ❌ Contains public principal
role = "roles/bigquery.dataViewer"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[
{
"queryName": "BigQuery Table Is Public",
"severity": "MEDIUM",
"line": 4
},
{
"queryName": "BigQuery Table Is Public",
"severity": "MEDIUM",
"line": 11
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"id": "d4e5f6g7-h8i9-0jkl-mnop-qrstuvwx1234",
"queryName": "Cloud KMS Key Ring is anonymously or publicly accessible",
"severity": "HIGH",
"category": "ENCRYPTION",
"descriptionText": "Cloud KMS Key Rings must not be publicly accessible. Public principals like 'allUsers' or 'allAuthenticatedUsers' should not be assigned in IAM member or binding configurations for key rings.",
"descriptionUrl": "https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/kms_key_ring",
"platform": "Terraform",
"descriptionID": "d4e5f6g7",
"cloudProvider": "gcp",
"cwe": "311"
}
Loading

0 comments on commit 49c8875

Please sign in to comment.