From dcb8884bdbb26900616094896e45998c0569e56d Mon Sep 17 00:00:00 2001 From: Andre Pereira <219305055+cx-andre-pereira@users.noreply.github.com> Date: Mon, 24 Nov 2025 14:28:24 +0000 Subject: [PATCH 1/4] prototype - work in progress --- .../storage_account_without_cmk/metadata.json | 14 +++++++ .../storage_account_without_cmk/query.rego | 32 +++++++++++++++ .../test/negative1.tf | 39 +++++++++++++++++++ .../test/negative2.tf | 20 ++++++++++ .../test/positive1.tf | 29 ++++++++++++++ .../test/positive2.tf | 16 ++++++++ .../test/positive_expected_result.json | 17 ++++++++ .../terraform_azure.yaml | 4 ++ 8 files changed, 171 insertions(+) create mode 100644 assets/queries/terraform/azure/storage_account_without_cmk/metadata.json create mode 100644 assets/queries/terraform/azure/storage_account_without_cmk/query.rego create mode 100644 assets/queries/terraform/azure/storage_account_without_cmk/test/negative1.tf create mode 100644 assets/queries/terraform/azure/storage_account_without_cmk/test/negative2.tf create mode 100644 assets/queries/terraform/azure/storage_account_without_cmk/test/positive1.tf create mode 100644 assets/queries/terraform/azure/storage_account_without_cmk/test/positive2.tf create mode 100644 assets/queries/terraform/azure/storage_account_without_cmk/test/positive_expected_result.json diff --git a/assets/queries/terraform/azure/storage_account_without_cmk/metadata.json b/assets/queries/terraform/azure/storage_account_without_cmk/metadata.json new file mode 100644 index 00000000000..308d73c3950 --- /dev/null +++ b/assets/queries/terraform/azure/storage_account_without_cmk/metadata.json @@ -0,0 +1,14 @@ +{ + "id": "9bf1568d-4cd2-4581-81ef-d2efabee1178", + "queryName": "Beta - Storage Account Without CMK", + "severity": "MEDIUM", + "category": "Encryption", + "descriptionText": "The 'azurerm_storage_account' resource should enable CMK encryption", + "descriptionUrl": "https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_account", + "platform": "Terraform", + "descriptionID": "9bf1568d", + "cloudProvider": "azure", + "cwe": "311", + "riskScore": "3.0", + "experimental": "true" +} diff --git a/assets/queries/terraform/azure/storage_account_without_cmk/query.rego b/assets/queries/terraform/azure/storage_account_without_cmk/query.rego new file mode 100644 index 00000000000..02c5a00d4b2 --- /dev/null +++ b/assets/queries/terraform/azure/storage_account_without_cmk/query.rego @@ -0,0 +1,32 @@ +package Cx + +import data.generic.common as common_lib +import data.generic.terraform as tf_lib + +CxPolicy[result] { + diagnostic_settings := {x | x := input.document[_].resource["azurerm_monitor_diagnostic_setting"][_]} + custom_managed_keys := {x | x := input.document[_].resource["azurerm_storage_account_customer_managed_key"][_]} + + resource := input.document[i].resource["azurerm_storage_account"][name] + id_reference := sprintf("azurerm_storage_account.%s.id",[name]) + + not has_cmk_resource(custom_managed_keys, id_reference) + diagnostic_settings[_].storage_account_id == id_reference + + not common_lib.valid_key(resource, "customer_managed_key") # assumes valid configuration if cmk is defined + + result := { + "documentId": input.document[i].id, + "resourceType": "azurerm_storage_account", + "resourceName": tf_lib.get_resource_name(resource, name), + "searchKey" : sprintf("azurerm_storage_account[%s]", [name]), + "issueType": "MissingAttribute", + "keyExpectedValue" : sprintf("'azurerm_storage_account[%s].customer_managed_key' should be set", [name]), + "keyActualValue" : sprintf("'azurerm_storage_account[%s].customer_managed_key' is undefined or null", [name]), + "searchLine" : common_lib.build_search_line(["resource", "azurerm_storage_account", name, "customer_managed_key"], []) + } +} + +has_cmk_resource(custom_managed_keys, id_reference) { + custom_managed_keys[_].storage_account_id == id_reference +} diff --git a/assets/queries/terraform/azure/storage_account_without_cmk/test/negative1.tf b/assets/queries/terraform/azure/storage_account_without_cmk/test/negative1.tf new file mode 100644 index 00000000000..b8bceaca821 --- /dev/null +++ b/assets/queries/terraform/azure/storage_account_without_cmk/test/negative1.tf @@ -0,0 +1,39 @@ +resource "azurerm_storage_account" "negative2_1" { + name = "storageaccountname" + resource_group_name = azurerm_resource_group.negative2_1.name + location = azurerm_resource_group.negative2_1.location + account_tier = "Standard" + account_replication_type = "GRS" + + customer_managed_key { + key_vault_key_id = azurerm_key_vault_key.example.id + user_assigned_identity_id = azurerm_user_assigned_identity.example.id + } +} + +resource "azurerm_monitor_diagnostic_setting" "negative2_1" { + name = "negative2_1" + target_resource_id = azurerm_subscription.negative2_1.id + + storage_account_id = azurerm_storage_account.negative2_1.id +} + +resource "azurerm_storage_account" "negative2_2" { + name = "storageaccountname" + resource_group_name = azurerm_resource_group.negative2_2.name + location = azurerm_resource_group.negative2_2.location + account_tier = "Standard" + account_replication_type = "GRS" + + customer_managed_key { + managed_hsm_key_id = azurerm_managed_hsm_key.example.id + user_assigned_identity_id = azurerm_user_assigned_identity.example.id + } +} + +resource "azurerm_monitor_diagnostic_setting" "negative2_2" { + name = "negative2_2" + target_resource_id = azurerm_subscription.negative2_2.id + + storage_account_id = azurerm_storage_account.negative2_2.id +} diff --git a/assets/queries/terraform/azure/storage_account_without_cmk/test/negative2.tf b/assets/queries/terraform/azure/storage_account_without_cmk/test/negative2.tf new file mode 100644 index 00000000000..933347e9012 --- /dev/null +++ b/assets/queries/terraform/azure/storage_account_without_cmk/test/negative2.tf @@ -0,0 +1,20 @@ +resource "azurerm_storage_account" "negative1" { + name = "storageaccountname" + resource_group_name = azurerm_resource_group.negative1.name + location = azurerm_resource_group.negative1.location + account_tier = "Standard" + account_replication_type = "GRS" +} + +resource "azurerm_monitor_diagnostic_setting" "negative1" { + name = "negative1" + target_resource_id = azurerm_subscription.negative1.id + + storage_account_id = azurerm_storage_account.negative1.id +} + +resource "azurerm_storage_account_customer_managed_key" "negative1" { + storage_account_id = azurerm_storage_account.negative1.id + key_vault_id = azurerm_key_vault.negative1.id + key_name = azurerm_key_vault_key.negative1.name +} diff --git a/assets/queries/terraform/azure/storage_account_without_cmk/test/positive1.tf b/assets/queries/terraform/azure/storage_account_without_cmk/test/positive1.tf new file mode 100644 index 00000000000..68f364dd333 --- /dev/null +++ b/assets/queries/terraform/azure/storage_account_without_cmk/test/positive1.tf @@ -0,0 +1,29 @@ +resource "azurerm_storage_account" "positive1_1" { # missing associated "azurerm_monitor_diagnostic_setting" + name = "storageaccountname" + resource_group_name = azurerm_resource_group.positive1_1.name + location = azurerm_resource_group.positive1_1.location + account_tier = "Standard" + account_replication_type = "GRS" + + customer_managed_key { + key_vault_key_id = azurerm_key_vault_key.example.id + user_assigned_identity_id = azurerm_user_assigned_identity.example.id + } +} + +resource "azurerm_storage_account" "positive1_2" { + name = "storageaccountname" + resource_group_name = azurerm_resource_group.positive1_2.name + location = azurerm_resource_group.positive1_2.location + account_tier = "Standard" + account_replication_type = "GRS" + + # missing "customer_managed_key" block +} + +resource "azurerm_monitor_diagnostic_setting" "positive1_2" { + name = "positive1_2" + target_resource_id = azurerm_subscription.positive1_2.id + + storage_account_id = azurerm_storage_account.positive1_2.id +} diff --git a/assets/queries/terraform/azure/storage_account_without_cmk/test/positive2.tf b/assets/queries/terraform/azure/storage_account_without_cmk/test/positive2.tf new file mode 100644 index 00000000000..2880b744eca --- /dev/null +++ b/assets/queries/terraform/azure/storage_account_without_cmk/test/positive2.tf @@ -0,0 +1,16 @@ +resource "azurerm_storage_account" "positive2_1" { + name = "storageaccountname" + resource_group_name = azurerm_resource_group.positive2_1.name + location = azurerm_resource_group.positive2_1.location + account_tier = "Standard" + account_replication_type = "GRS" +} + +resource "azurerm_monitor_diagnostic_setting" "positive2_1" { + name = "positive2_1" + target_resource_id = azurerm_subscription.positive2_1.id + + storage_account_id = azurerm_storage_account.positive2_1.id +} + +# missing "azurerm_storage_account_customer_managed_key" association diff --git a/assets/queries/terraform/azure/storage_account_without_cmk/test/positive_expected_result.json b/assets/queries/terraform/azure/storage_account_without_cmk/test/positive_expected_result.json new file mode 100644 index 00000000000..929b0c5731e --- /dev/null +++ b/assets/queries/terraform/azure/storage_account_without_cmk/test/positive_expected_result.json @@ -0,0 +1,17 @@ +[ + { + "queryName": "", + "severity": "", + "line": + }, + { + "queryName": "", + "severity": "", + "line": + }, + { + "queryName": "", + "severity": "", + "line": + } +] diff --git a/assets/similarityID_transition/terraform_azure.yaml b/assets/similarityID_transition/terraform_azure.yaml index 407c810f4d1..63d56598ca9 100644 --- a/assets/similarityID_transition/terraform_azure.yaml +++ b/assets/similarityID_transition/terraform_azure.yaml @@ -3,3 +3,7 @@ similarityIDChangeList: queryName: Sensitive Port Is Exposed To Wide Private Network observations: "" change: 5 + - queryId: 9bf1568d-4cd2-4581-81ef-d2efabee1178 + queryName: Beta - Storage Account Without CMK + observations: "" + change: 2 From 950bf3b52943e7abe547d64d1bc41f810e15c1e6 Mon Sep 17 00:00:00 2001 From: Andre Pereira <219305055+cx-andre-pereira@users.noreply.github.com> Date: Mon, 24 Nov 2025 15:07:36 +0000 Subject: [PATCH 2/4] finished initial implementation --- .../storage_account_without_cmk/query.rego | 16 ++++---- .../test/negative1.tf | 41 +++++-------------- .../test/negative2.tf | 41 ++++++++++++++----- .../test/negative3.tf | 7 ++++ .../test/positive1.tf | 23 +++-------- .../test/positive_expected_result.json | 19 ++++----- 6 files changed, 69 insertions(+), 78 deletions(-) create mode 100644 assets/queries/terraform/azure/storage_account_without_cmk/test/negative3.tf diff --git a/assets/queries/terraform/azure/storage_account_without_cmk/query.rego b/assets/queries/terraform/azure/storage_account_without_cmk/query.rego index 02c5a00d4b2..0f78c8d826e 100644 --- a/assets/queries/terraform/azure/storage_account_without_cmk/query.rego +++ b/assets/queries/terraform/azure/storage_account_without_cmk/query.rego @@ -4,16 +4,16 @@ import data.generic.common as common_lib import data.generic.terraform as tf_lib CxPolicy[result] { - diagnostic_settings := {x | x := input.document[_].resource["azurerm_monitor_diagnostic_setting"][_]} - custom_managed_keys := {x | x := input.document[_].resource["azurerm_storage_account_customer_managed_key"][_]} - resource := input.document[i].resource["azurerm_storage_account"][name] - id_reference := sprintf("azurerm_storage_account.%s.id",[name]) + id_reference := sprintf("${azurerm_storage_account.%s.id}", [name]) + + not common_lib.valid_key(resource, "customer_managed_key") # assumes valid configuration if cmk is defined - not has_cmk_resource(custom_managed_keys, id_reference) + diagnostic_settings := {x | x := input.document[_].resource["azurerm_monitor_diagnostic_setting"][_]} # must be associated with diagnostic_setting diagnostic_settings[_].storage_account_id == id_reference - not common_lib.valid_key(resource, "customer_managed_key") # assumes valid configuration if cmk is defined + custom_managed_keys := {x | x := input.document[_].resource["azurerm_storage_account_customer_managed_key"][_]} + not is_associated_with_cmk_resource(custom_managed_keys, id_reference) result := { "documentId": input.document[i].id, @@ -23,10 +23,10 @@ CxPolicy[result] { "issueType": "MissingAttribute", "keyExpectedValue" : sprintf("'azurerm_storage_account[%s].customer_managed_key' should be set", [name]), "keyActualValue" : sprintf("'azurerm_storage_account[%s].customer_managed_key' is undefined or null", [name]), - "searchLine" : common_lib.build_search_line(["resource", "azurerm_storage_account", name, "customer_managed_key"], []) + "searchLine" : common_lib.build_search_line(["resource", "azurerm_storage_account", name], []) } } -has_cmk_resource(custom_managed_keys, id_reference) { +is_associated_with_cmk_resource(custom_managed_keys, id_reference) { custom_managed_keys[_].storage_account_id == id_reference } diff --git a/assets/queries/terraform/azure/storage_account_without_cmk/test/negative1.tf b/assets/queries/terraform/azure/storage_account_without_cmk/test/negative1.tf index b8bceaca821..b0658c8621c 100644 --- a/assets/queries/terraform/azure/storage_account_without_cmk/test/negative1.tf +++ b/assets/queries/terraform/azure/storage_account_without_cmk/test/negative1.tf @@ -1,39 +1,20 @@ -resource "azurerm_storage_account" "negative2_1" { +resource "azurerm_storage_account" "negative1" { # associated with "azurerm_storage_account_customer_managed_key" resource name = "storageaccountname" - resource_group_name = azurerm_resource_group.negative2_1.name - location = azurerm_resource_group.negative2_1.location + resource_group_name = azurerm_resource_group.negative1.name + location = azurerm_resource_group.negative1.location account_tier = "Standard" account_replication_type = "GRS" - - customer_managed_key { - key_vault_key_id = azurerm_key_vault_key.example.id - user_assigned_identity_id = azurerm_user_assigned_identity.example.id - } -} - -resource "azurerm_monitor_diagnostic_setting" "negative2_1" { - name = "negative2_1" - target_resource_id = azurerm_subscription.negative2_1.id - - storage_account_id = azurerm_storage_account.negative2_1.id } -resource "azurerm_storage_account" "negative2_2" { - name = "storageaccountname" - resource_group_name = azurerm_resource_group.negative2_2.name - location = azurerm_resource_group.negative2_2.location - account_tier = "Standard" - account_replication_type = "GRS" +resource "azurerm_monitor_diagnostic_setting" "negative1" { + name = "negative1" + target_resource_id = azurerm_subscription.negative1.id - customer_managed_key { - managed_hsm_key_id = azurerm_managed_hsm_key.example.id - user_assigned_identity_id = azurerm_user_assigned_identity.example.id - } + storage_account_id = azurerm_storage_account.negative1.id } -resource "azurerm_monitor_diagnostic_setting" "negative2_2" { - name = "negative2_2" - target_resource_id = azurerm_subscription.negative2_2.id - - storage_account_id = azurerm_storage_account.negative2_2.id +resource "azurerm_storage_account_customer_managed_key" "negative1" { + storage_account_id = azurerm_storage_account.negative1.id + key_vault_id = azurerm_key_vault.negative1.id + key_name = azurerm_key_vault_key.negative1.name } diff --git a/assets/queries/terraform/azure/storage_account_without_cmk/test/negative2.tf b/assets/queries/terraform/azure/storage_account_without_cmk/test/negative2.tf index 933347e9012..d8f8baee7bd 100644 --- a/assets/queries/terraform/azure/storage_account_without_cmk/test/negative2.tf +++ b/assets/queries/terraform/azure/storage_account_without_cmk/test/negative2.tf @@ -1,20 +1,39 @@ -resource "azurerm_storage_account" "negative1" { +resource "azurerm_storage_account" "negative2_1" { # sets "customer_managed_key" field name = "storageaccountname" - resource_group_name = azurerm_resource_group.negative1.name - location = azurerm_resource_group.negative1.location + resource_group_name = azurerm_resource_group.negative2_1.name + location = azurerm_resource_group.negative2_1.location account_tier = "Standard" account_replication_type = "GRS" + + customer_managed_key { + key_vault_key_id = azurerm_key_vault_key.example.id + user_assigned_identity_id = azurerm_user_assigned_identity.example.id + } +} + +resource "azurerm_monitor_diagnostic_setting" "negative2_1" { + name = "negative2_1" + target_resource_id = azurerm_subscription.negative2_1.id + + storage_account_id = azurerm_storage_account.negative2_1.id } -resource "azurerm_monitor_diagnostic_setting" "negative1" { - name = "negative1" - target_resource_id = azurerm_subscription.negative1.id +resource "azurerm_storage_account" "negative2_2" { # sets "customer_managed_key" field + name = "storageaccountname" + resource_group_name = azurerm_resource_group.negative2_2.name + location = azurerm_resource_group.negative2_2.location + account_tier = "Standard" + account_replication_type = "GRS" - storage_account_id = azurerm_storage_account.negative1.id + customer_managed_key { + managed_hsm_key_id = azurerm_managed_hsm_key.example.id + user_assigned_identity_id = azurerm_user_assigned_identity.example.id + } } -resource "azurerm_storage_account_customer_managed_key" "negative1" { - storage_account_id = azurerm_storage_account.negative1.id - key_vault_id = azurerm_key_vault.negative1.id - key_name = azurerm_key_vault_key.negative1.name +resource "azurerm_monitor_diagnostic_setting" "negative2_2" { + name = "negative2_2" + target_resource_id = azurerm_subscription.negative2_2.id + + storage_account_id = azurerm_storage_account.negative2_2.id } diff --git a/assets/queries/terraform/azure/storage_account_without_cmk/test/negative3.tf b/assets/queries/terraform/azure/storage_account_without_cmk/test/negative3.tf new file mode 100644 index 00000000000..7a49a4afec1 --- /dev/null +++ b/assets/queries/terraform/azure/storage_account_without_cmk/test/negative3.tf @@ -0,0 +1,7 @@ +resource "azurerm_storage_account" "negative3" { # missing associated "azurerm_monitor_diagnostic_setting" + name = "storageaccountname" + resource_group_name = azurerm_resource_group.negative3.name + location = azurerm_resource_group.negative3.location + account_tier = "Standard" + account_replication_type = "GRS" +} diff --git a/assets/queries/terraform/azure/storage_account_without_cmk/test/positive1.tf b/assets/queries/terraform/azure/storage_account_without_cmk/test/positive1.tf index 68f364dd333..05ddb233607 100644 --- a/assets/queries/terraform/azure/storage_account_without_cmk/test/positive1.tf +++ b/assets/queries/terraform/azure/storage_account_without_cmk/test/positive1.tf @@ -1,29 +1,16 @@ -resource "azurerm_storage_account" "positive1_1" { # missing associated "azurerm_monitor_diagnostic_setting" +resource "azurerm_storage_account" "positive1_1" { name = "storageaccountname" resource_group_name = azurerm_resource_group.positive1_1.name location = azurerm_resource_group.positive1_1.location account_tier = "Standard" account_replication_type = "GRS" - customer_managed_key { - key_vault_key_id = azurerm_key_vault_key.example.id - user_assigned_identity_id = azurerm_user_assigned_identity.example.id - } -} - -resource "azurerm_storage_account" "positive1_2" { - name = "storageaccountname" - resource_group_name = azurerm_resource_group.positive1_2.name - location = azurerm_resource_group.positive1_2.location - account_tier = "Standard" - account_replication_type = "GRS" - # missing "customer_managed_key" block } -resource "azurerm_monitor_diagnostic_setting" "positive1_2" { - name = "positive1_2" - target_resource_id = azurerm_subscription.positive1_2.id +resource "azurerm_monitor_diagnostic_setting" "positive1_1" { + name = "positive1_1" + target_resource_id = azurerm_subscription.positive1_1.id - storage_account_id = azurerm_storage_account.positive1_2.id + storage_account_id = azurerm_storage_account.positive1_1.id } diff --git a/assets/queries/terraform/azure/storage_account_without_cmk/test/positive_expected_result.json b/assets/queries/terraform/azure/storage_account_without_cmk/test/positive_expected_result.json index 929b0c5731e..a98b4556f9d 100644 --- a/assets/queries/terraform/azure/storage_account_without_cmk/test/positive_expected_result.json +++ b/assets/queries/terraform/azure/storage_account_without_cmk/test/positive_expected_result.json @@ -1,17 +1,14 @@ [ { - "queryName": "", - "severity": "", - "line": + "queryName": "Beta - Storage Account Without CMK", + "severity": "MEDIUM", + "line": 1, + "fileName": "positive1.tf" }, { - "queryName": "", - "severity": "", - "line": - }, - { - "queryName": "", - "severity": "", - "line": + "queryName": "Beta - Storage Account Without CMK", + "severity": "MEDIUM", + "line": 1, + "fileName": "positive2.tf" } ] From 6f4bc630f5031e107d3cdd7c66215277d3131a97 Mon Sep 17 00:00:00 2001 From: Andre Pereira <219305055+cx-andre-pereira@users.noreply.github.com> Date: Wed, 26 Nov 2025 11:04:10 +0000 Subject: [PATCH 3/4] minor change --- .../terraform/azure/storage_account_without_cmk/query.rego | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/assets/queries/terraform/azure/storage_account_without_cmk/query.rego b/assets/queries/terraform/azure/storage_account_without_cmk/query.rego index 0f78c8d826e..55ded9847c6 100644 --- a/assets/queries/terraform/azure/storage_account_without_cmk/query.rego +++ b/assets/queries/terraform/azure/storage_account_without_cmk/query.rego @@ -4,15 +4,15 @@ import data.generic.common as common_lib import data.generic.terraform as tf_lib CxPolicy[result] { - resource := input.document[i].resource["azurerm_storage_account"][name] + resource := input.document[i].resource.azurerm_storage_account[name] id_reference := sprintf("${azurerm_storage_account.%s.id}", [name]) not common_lib.valid_key(resource, "customer_managed_key") # assumes valid configuration if cmk is defined - diagnostic_settings := {x | x := input.document[_].resource["azurerm_monitor_diagnostic_setting"][_]} # must be associated with diagnostic_setting + diagnostic_settings := {x | x := input.document[_].resource.azurerm_monitor_diagnostic_setting[_]} # must be associated with diagnostic_setting diagnostic_settings[_].storage_account_id == id_reference - custom_managed_keys := {x | x := input.document[_].resource["azurerm_storage_account_customer_managed_key"][_]} + custom_managed_keys := {x | x := input.document[_].resource.azurerm_storage_account_customer_managed_key[_]} not is_associated_with_cmk_resource(custom_managed_keys, id_reference) result := { From 291904851df226a83e426fa189a4354747ec5bbc Mon Sep 17 00:00:00 2001 From: Andre Pereira <219305055+cx-andre-pereira@users.noreply.github.com> Date: Wed, 3 Dec 2025 16:07:30 +0000 Subject: [PATCH 4/4] cwe updated --- .../terraform/azure/storage_account_without_cmk/metadata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/queries/terraform/azure/storage_account_without_cmk/metadata.json b/assets/queries/terraform/azure/storage_account_without_cmk/metadata.json index 308d73c3950..c6c8f4f2013 100644 --- a/assets/queries/terraform/azure/storage_account_without_cmk/metadata.json +++ b/assets/queries/terraform/azure/storage_account_without_cmk/metadata.json @@ -8,7 +8,7 @@ "platform": "Terraform", "descriptionID": "9bf1568d", "cloudProvider": "azure", - "cwe": "311", + "cwe": "522", "riskScore": "3.0", "experimental": "true" }