From 9d7adb1c19f533514ac9f5d5a4c358991fdba47b Mon Sep 17 00:00:00 2001 From: Andre Pereira <219305055+cx-andre-pereira@users.noreply.github.com> Date: Wed, 19 Nov 2025 14:45:31 +0000 Subject: [PATCH 1/4] initial changes --- .../query.rego | 25 ++++---- .../test/negative.tf | 59 +++++++++++++++++-- .../test/positive.tf | 37 ++++++++---- .../test/positive_expected_result.json | 7 ++- 4 files changed, 98 insertions(+), 30 deletions(-) diff --git a/assets/queries/terraform/azure/encryption_on_managed_disk_disabled/query.rego b/assets/queries/terraform/azure/encryption_on_managed_disk_disabled/query.rego index bd7260c3a01..a684ad94906 100644 --- a/assets/queries/terraform/azure/encryption_on_managed_disk_disabled/query.rego +++ b/assets/queries/terraform/azure/encryption_on_managed_disk_disabled/query.rego @@ -6,25 +6,30 @@ import data.generic.terraform as tf_lib CxPolicy[result] { resource := input.document[i].resource encryption := resource.azurerm_managed_disk[name] - not common_lib.valid_key(encryption, "encryption_settings") + results := undefined_or_empty(encryption) result := { "documentId": input.document[i].id, "resourceType": "azurerm_managed_disk", "resourceName": tf_lib.get_resource_name(resource, name), - "searchKey": sprintf("azurerm_managed_disk[%s]", [name]), - "issueType": "MissingAttribute", + "searchKey": results.searchKey, #sprintf("azurerm_managed_disk[%s]", [name]), + "issueType": results.issueType, #"MissingAttribute", "keyExpectedValue": sprintf("azurerm_managed_disk[%s].encryption_settings should be defined and not null", [name]), "keyActualValue": sprintf("azurerm_managed_disk[%s].encryption_settings is undefined or null", [name]), - "searchLine": common_lib.build_search_line(["resource","azurerm_managed_disk" ,name], []), - "remediation": "encryption_settings = {\n\t\t enabled= true\n\t}\n", - "remediationType": "addition", + "searchLine": results.searchLine, #common_lib.build_search_line(["resource","azurerm_managed_disk" ,name], []) } } +undefined_or_empty(encryption) { + not common_lib.valid_key(encryption, "encryption_settings") +} else { + encryption.encryption_settings == [[],{}][_] # [] for tfplan support +} + CxPolicy[result] { resource := input.document[i].resource encryption := resource.azurerm_managed_disk[name] + common_lib.valid_key(encryption.encryption_settings, "enabled") #legacy encryption.encryption_settings.enabled == false result := { @@ -34,12 +39,6 @@ CxPolicy[result] { "searchKey": sprintf("azurerm_managed_disk[%s].encryption_settings.enabled", [name]), "issueType": "IncorrectValue", "keyExpectedValue": sprintf("azurerm_managed_disk[%s].encryption_settings.enabled should be true ", [name]), - "keyActualValue": sprintf("azurerm_managed_disk[%s].encryption_settings.enabled is false", [name]), - "searchLine": common_lib.build_search_line(["resource","azurerm_managed_disk" ,name ,"encryption_settings", "enabled"], []), - "remediation": json.marshal({ - "before": "false", - "after": "true" - }), - "remediationType": "replacement", + "keyActualValue": sprintf("azurerm_managed_disk[%s].encryption_settings.enabled is false", [name]) } } diff --git a/assets/queries/terraform/azure/encryption_on_managed_disk_disabled/test/negative.tf b/assets/queries/terraform/azure/encryption_on_managed_disk_disabled/test/negative.tf index 119c4cc2df0..beedcd1fc06 100644 --- a/assets/queries/terraform/azure/encryption_on_managed_disk_disabled/test/negative.tf +++ b/assets/queries/terraform/azure/encryption_on_managed_disk_disabled/test/negative.tf @@ -6,11 +6,60 @@ resource "azurerm_managed_disk" "negative1" { storage_account_type = "Standard_LRS" create_option = "Empty" disk_size_gb = "1" - + + encryption_settings = { + enabled = true # legacy + } +} + +resource "azurerm_managed_disk" "negative2" { + name = "acctestmd" + location = "West US 2" + resource_group_name = azurerm_resource_group.example.name + storage_account_type = "Standard_LRS" + create_option = "Empty" + disk_size_gb = "1" + + encryption_settings = { + disk_encryption_key { + secret_url = "sample_url" + source_vault_id = "sample_id" + }, + key_encryption_key { + secret_url = "sample_url" + source_vault_id = "sample_id" + } + } +} + +resource "azurerm_managed_disk" "negative3" { + name = "acctestmd" + location = "West US 2" + resource_group_name = azurerm_resource_group.example.name + storage_account_type = "Standard_LRS" + create_option = "Empty" + disk_size_gb = "1" + encryption_settings = { - enabled = true + disk_encryption_key { + secret_url = "sample_url" + source_vault_id = "sample_id" + } } - tags = { - environment = "staging" +} + +resource "azurerm_managed_disk" "negative4" { + name = "acctestmd" + location = "West US 2" + resource_group_name = azurerm_resource_group.example.name + storage_account_type = "Standard_LRS" + create_option = "Empty" + disk_size_gb = "1" + + encryption_settings = { + key_encryption_key { + secret_url = "sample_url" + source_vault_id = "sample_id" + } } -} \ No newline at end of file +} diff --git a/assets/queries/terraform/azure/encryption_on_managed_disk_disabled/test/positive.tf b/assets/queries/terraform/azure/encryption_on_managed_disk_disabled/test/positive.tf index 78d461a632b..a8b2a249a5e 100644 --- a/assets/queries/terraform/azure/encryption_on_managed_disk_disabled/test/positive.tf +++ b/assets/queries/terraform/azure/encryption_on_managed_disk_disabled/test/positive.tf @@ -6,12 +6,8 @@ resource "azurerm_managed_disk" "positive1" { create_option = "Empty" disk_size_gb = "1" - encryption_settings = { - enabled = false - } - - tags = { - environment = "staging" + encryption_settings { + enabled = false # legacy } } @@ -22,9 +18,28 @@ resource "azurerm_managed_disk" "positive2" { storage_account_type = "Standard_LRS" create_option = "Empty" disk_size_gb = "1" - - tags = { - environment = "staging" - } -} \ No newline at end of file + #missing "encryption_settings" +} + +resource "azurerm_managed_disk" "positive3" { + name = "acctestmd" + location = "West US 2" + resource_group_name = azurerm_resource_group.example.name + storage_account_type = "Standard_LRS" + create_option = "Empty" + disk_size_gb = "1" + + encryption_settings {} +} + +resource "azurerm_managed_disk" "positive4" { + name = "acctestmd" + location = "West US 2" + resource_group_name = azurerm_resource_group.example.name + storage_account_type = "Standard_LRS" + create_option = "Empty" + disk_size_gb = "1" + + encryption_settings = [] # simulates "tfplan" +} diff --git a/assets/queries/terraform/azure/encryption_on_managed_disk_disabled/test/positive_expected_result.json b/assets/queries/terraform/azure/encryption_on_managed_disk_disabled/test/positive_expected_result.json index 9cef2f7f43c..d953f8733c7 100644 --- a/assets/queries/terraform/azure/encryption_on_managed_disk_disabled/test/positive_expected_result.json +++ b/assets/queries/terraform/azure/encryption_on_managed_disk_disabled/test/positive_expected_result.json @@ -7,6 +7,11 @@ { "queryName": "Encryption On Managed Disk Disabled", "severity": "MEDIUM", - "line": 18 + "line": 14 + }, + { + "queryName": "Encryption On Managed Disk Disabled", + "severity": "MEDIUM", + "line": 25 } ] From bc27997db8f0733e4fe9e3e84d6be2f287d55a18 Mon Sep 17 00:00:00 2001 From: Andre Pereira <219305055+cx-andre-pereira@users.noreply.github.com> Date: Wed, 19 Nov 2025 15:01:34 +0000 Subject: [PATCH 2/4] improvements --- .../query.rego | 52 +++++++++++++------ .../test/negative.tf | 13 +++-- .../test/positive.tf | 2 +- .../test/positive_expected_result.json | 7 ++- 4 files changed, 50 insertions(+), 24 deletions(-) diff --git a/assets/queries/terraform/azure/encryption_on_managed_disk_disabled/query.rego b/assets/queries/terraform/azure/encryption_on_managed_disk_disabled/query.rego index a684ad94906..1ef4bc62db9 100644 --- a/assets/queries/terraform/azure/encryption_on_managed_disk_disabled/query.rego +++ b/assets/queries/terraform/azure/encryption_on_managed_disk_disabled/query.rego @@ -4,33 +4,45 @@ import data.generic.common as common_lib import data.generic.terraform as tf_lib CxPolicy[result] { - resource := input.document[i].resource - encryption := resource.azurerm_managed_disk[name] - results := undefined_or_empty(encryption) + resource := input.document[i].resource.azurerm_managed_disk[name] + + results := undefined_or_empty(resource, name) result := { "documentId": input.document[i].id, "resourceType": "azurerm_managed_disk", "resourceName": tf_lib.get_resource_name(resource, name), - "searchKey": results.searchKey, #sprintf("azurerm_managed_disk[%s]", [name]), - "issueType": results.issueType, #"MissingAttribute", - "keyExpectedValue": sprintf("azurerm_managed_disk[%s].encryption_settings should be defined and not null", [name]), - "keyActualValue": sprintf("azurerm_managed_disk[%s].encryption_settings is undefined or null", [name]), - "searchLine": results.searchLine, #common_lib.build_search_line(["resource","azurerm_managed_disk" ,name], []) + "searchKey": results.searchKey, + "issueType": results.issueType, + "keyExpectedValue": sprintf("'azurerm_managed_disk[%s].encryption_settings' should be defined and not null", [name]), + "keyActualValue": sprintf("'azurerm_managed_disk[%s].encryption_settings' is %s", [name, results.actual_value_string]), + "searchLine": results.searchLine, } } -undefined_or_empty(encryption) { +undefined_or_empty(encryption, name) = results { not common_lib.valid_key(encryption, "encryption_settings") -} else { + results := { + "searchKey": sprintf("azurerm_managed_disk[%s]", [name]), + "issueType": "MissingAttribute", + "searchLine": common_lib.build_search_line(["resource", "azurerm_managed_disk", name], []), + "actual_value_string": "undefined or null" + } +} else = results { encryption.encryption_settings == [[],{}][_] # [] for tfplan support + results := { + "searchKey": sprintf("azurerm_managed_disk[%s].encryption_settings", [name]), + "issueType": "IncorrectValue", + "searchLine": common_lib.build_search_line(["resource", "azurerm_managed_disk", name, "encryption_settings"], []), + "actual_value_string": sprintf("set to '%v'",[encryption.encryption_settings]) + } } -CxPolicy[result] { - resource := input.document[i].resource - encryption := resource.azurerm_managed_disk[name] - common_lib.valid_key(encryption.encryption_settings, "enabled") #legacy - encryption.encryption_settings.enabled == false +CxPolicy[result] { #legacy + resource := input.document[i].resource.azurerm_managed_disk[name] + + common_lib.valid_key(resource.encryption_settings, "enabled") + resource.encryption_settings.enabled == false result := { "documentId": input.document[i].id, @@ -38,7 +50,13 @@ CxPolicy[result] { "resourceName": tf_lib.get_resource_name(resource, name), "searchKey": sprintf("azurerm_managed_disk[%s].encryption_settings.enabled", [name]), "issueType": "IncorrectValue", - "keyExpectedValue": sprintf("azurerm_managed_disk[%s].encryption_settings.enabled should be true ", [name]), - "keyActualValue": sprintf("azurerm_managed_disk[%s].encryption_settings.enabled is false", [name]) + "keyExpectedValue": sprintf("'azurerm_managed_disk[%s].encryption_settings.enabled' should be set to true", [name]), + "keyActualValue": sprintf("'azurerm_managed_disk[%s].encryption_settings.enabled' is set to false", [name]), + "searchLine": common_lib.build_search_line(["resource", "azurerm_managed_disk", name, "encryption_settings", "enabled"], []), + "remediation": json.marshal({ + "before": "false", + "after": "true" + }), + "remediationType": "replacement" } } diff --git a/assets/queries/terraform/azure/encryption_on_managed_disk_disabled/test/negative.tf b/assets/queries/terraform/azure/encryption_on_managed_disk_disabled/test/negative.tf index beedcd1fc06..a41be3c9c32 100644 --- a/assets/queries/terraform/azure/encryption_on_managed_disk_disabled/test/negative.tf +++ b/assets/queries/terraform/azure/encryption_on_managed_disk_disabled/test/negative.tf @@ -7,7 +7,7 @@ resource "azurerm_managed_disk" "negative1" { create_option = "Empty" disk_size_gb = "1" - encryption_settings = { + encryption_settings { enabled = true # legacy } } @@ -20,15 +20,18 @@ resource "azurerm_managed_disk" "negative2" { create_option = "Empty" disk_size_gb = "1" - encryption_settings = { + encryption_settings { + disk_encryption_key { secret_url = "sample_url" source_vault_id = "sample_id" - }, + } + key_encryption_key { secret_url = "sample_url" source_vault_id = "sample_id" } + } } @@ -40,7 +43,7 @@ resource "azurerm_managed_disk" "negative3" { create_option = "Empty" disk_size_gb = "1" - encryption_settings = { + encryption_settings { disk_encryption_key { secret_url = "sample_url" source_vault_id = "sample_id" @@ -56,7 +59,7 @@ resource "azurerm_managed_disk" "negative4" { create_option = "Empty" disk_size_gb = "1" - encryption_settings = { + encryption_settings { key_encryption_key { secret_url = "sample_url" source_vault_id = "sample_id" diff --git a/assets/queries/terraform/azure/encryption_on_managed_disk_disabled/test/positive.tf b/assets/queries/terraform/azure/encryption_on_managed_disk_disabled/test/positive.tf index a8b2a249a5e..85b116ae07b 100644 --- a/assets/queries/terraform/azure/encryption_on_managed_disk_disabled/test/positive.tf +++ b/assets/queries/terraform/azure/encryption_on_managed_disk_disabled/test/positive.tf @@ -19,7 +19,7 @@ resource "azurerm_managed_disk" "positive2" { create_option = "Empty" disk_size_gb = "1" - #missing "encryption_settings" + # missing "encryption_settings" } resource "azurerm_managed_disk" "positive3" { diff --git a/assets/queries/terraform/azure/encryption_on_managed_disk_disabled/test/positive_expected_result.json b/assets/queries/terraform/azure/encryption_on_managed_disk_disabled/test/positive_expected_result.json index d953f8733c7..22ca038752f 100644 --- a/assets/queries/terraform/azure/encryption_on_managed_disk_disabled/test/positive_expected_result.json +++ b/assets/queries/terraform/azure/encryption_on_managed_disk_disabled/test/positive_expected_result.json @@ -12,6 +12,11 @@ { "queryName": "Encryption On Managed Disk Disabled", "severity": "MEDIUM", - "line": 25 + "line": 33 + }, + { + "queryName": "Encryption On Managed Disk Disabled", + "severity": "MEDIUM", + "line": 44 } ] From 15e067a923a0cab13a0597b4e82123d001153686 Mon Sep 17 00:00:00 2001 From: Andre Pereira <219305055+cx-andre-pereira@users.noreply.github.com> Date: Wed, 19 Nov 2025 16:56:07 +0000 Subject: [PATCH 3/4] engine update for proper tfplan parsing --- .../metadata.json | 2 +- pkg/parser/json/tfplan.go | 12 ++--- pkg/parser/json/tfplan_test.go | 50 ++++++++++++++++++- 3 files changed, 55 insertions(+), 9 deletions(-) diff --git a/assets/queries/terraform/azure/encryption_on_managed_disk_disabled/metadata.json b/assets/queries/terraform/azure/encryption_on_managed_disk_disabled/metadata.json index 770e9fd3be0..22b4eeaaeea 100644 --- a/assets/queries/terraform/azure/encryption_on_managed_disk_disabled/metadata.json +++ b/assets/queries/terraform/azure/encryption_on_managed_disk_disabled/metadata.json @@ -10,4 +10,4 @@ "cloudProvider": "azure", "cwe": "311", "riskScore": "5.5" -} \ No newline at end of file +} diff --git a/pkg/parser/json/tfplan.go b/pkg/parser/json/tfplan.go index 23f2a8aa5fa..4dc110995b1 100644 --- a/pkg/parser/json/tfplan.go +++ b/pkg/parser/json/tfplan.go @@ -62,14 +62,12 @@ func readPlan(plan *hcl_plan.Plan) model.Document { // readModule will iterate over all planned_value getting the information required func (kp *KicsPlan) readModule(module *hcl_plan.StateModule) { - // initialize all the types interfaces + // initialize all the types interfaces and fill in all the types interfaces for _, resource := range module.Resources { - convNamedRes := make(map[string]KicsPlanNamedResource) - kp.Resource[resource.Type] = convNamedRes - } - // fill in all the types interfaces - for _, resource := range module.Resources { - kp.Resource[resource.Type][resource.Name] = resource.AttributeValues + if _, type_map := kp.Resource[resource.Type]; !type_map { + kp.Resource[resource.Type] = make(map[string]KicsPlanNamedResource) + } + kp.Resource[resource.Type][resource.Address] = resource.AttributeValues } for _, childModule := range module.ChildModules { diff --git a/pkg/parser/json/tfplan_test.go b/pkg/parser/json/tfplan_test.go index 46bff92cc41..6b17a679e6e 100644 --- a/pkg/parser/json/tfplan_test.go +++ b/pkg/parser/json/tfplan_test.go @@ -51,7 +51,7 @@ func TestJson_parseTFPlan(t *testing.T) { want: model.Document{ "resource": map[string]interface{}{ "fakewebservices_database": map[string]interface{}{ - "prod_db": map[string]interface{}{ + "fakewebservices_database.prod_db": map[string]interface{}{ "name": "Production DB", "size": (float64)(256), }, @@ -72,6 +72,54 @@ func TestJson_parseTFPlan(t *testing.T) { want: model.Document{}, wantErr: true, }, + { + name: "test - parse tfplan with duplicate resource names (different addresses)", + args: args{ + doc: model.Document{ + "format_version": "0.2", + "terraform_version": "1.0.5", + "planned_values": map[string]interface{}{ + "root_module": map[string]interface{}{ + "resources": []map[string]interface{}{ + { + "address": "fakewebservices_database.prod_db[0]", + "type": "fakewebservices_database", + "name": "prod_db", + "values": map[string]interface{}{ + "name": "Production DB A", + "size": 256, + }, + }, + { + "address": "fakewebservices_database.prod_db[1]", + "type": "fakewebservices_database", + "name": "prod_db", + "values": map[string]interface{}{ + "name": "Production DB B", + "size": 512, + }, + }, + }, + }, + }, + }, + }, + want: model.Document{ + "resource": map[string]interface{}{ + "fakewebservices_database": map[string]interface{}{ + "fakewebservices_database.prod_db[0]": map[string]interface{}{ + "name": "Production DB A", + "size": (float64)(256), + }, + "fakewebservices_database.prod_db[1]": map[string]interface{}{ + "name": "Production DB B", + "size": (float64)(512), + }, + }, + }, + }, + wantErr: false, + }, } for _, tt := range tests { From be314788033b137a85963a84ce52e4a084039661 Mon Sep 17 00:00:00 2001 From: Andre Pereira <219305055+cx-andre-pereira@users.noreply.github.com> Date: Wed, 19 Nov 2025 22:52:11 +0000 Subject: [PATCH 4/4] simplified logic --- .../query.rego | 40 +++++++++---------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/assets/queries/terraform/azure/encryption_on_managed_disk_disabled/query.rego b/assets/queries/terraform/azure/encryption_on_managed_disk_disabled/query.rego index 1ef4bc62db9..deb4a586507 100644 --- a/assets/queries/terraform/azure/encryption_on_managed_disk_disabled/query.rego +++ b/assets/queries/terraform/azure/encryption_on_managed_disk_disabled/query.rego @@ -14,49 +14,45 @@ CxPolicy[result] { "resourceName": tf_lib.get_resource_name(resource, name), "searchKey": results.searchKey, "issueType": results.issueType, - "keyExpectedValue": sprintf("'azurerm_managed_disk[%s].encryption_settings' should be defined and not null", [name]), - "keyActualValue": sprintf("'azurerm_managed_disk[%s].encryption_settings' is %s", [name, results.actual_value_string]), + "keyExpectedValue": results.keyExpectedValue, + "keyActualValue": results.keyActualValue, "searchLine": results.searchLine, + "remediation": results.remediation, + "remediationType": results.remediationType } } -undefined_or_empty(encryption, name) = results { - not common_lib.valid_key(encryption, "encryption_settings") +undefined_or_empty(resource, name) = results { + not common_lib.valid_key(resource, "encryption_settings") results := { "searchKey": sprintf("azurerm_managed_disk[%s]", [name]), "issueType": "MissingAttribute", + "keyExpectedValue": sprintf("'azurerm_managed_disk[%s].encryption_settings' should be defined and not null", [name]), + "keyActualValue": sprintf("'azurerm_managed_disk[%s].encryption_settings' is undefined or null", [name]), "searchLine": common_lib.build_search_line(["resource", "azurerm_managed_disk", name], []), - "actual_value_string": "undefined or null" + "remediation": null, + "remediationType": null } } else = results { - encryption.encryption_settings == [[],{}][_] # [] for tfplan support + resource.encryption_settings == [[],{}][_] # [] for tfplan support results := { "searchKey": sprintf("azurerm_managed_disk[%s].encryption_settings", [name]), "issueType": "IncorrectValue", + "keyExpectedValue": sprintf("'azurerm_managed_disk[%s].encryption_settings' should be defined and not null", [name]), + "keyActualValue": sprintf("'azurerm_managed_disk[%s].encryption_settings' is set to '%v", [name, resource.encryption_settings]), "searchLine": common_lib.build_search_line(["resource", "azurerm_managed_disk", name, "encryption_settings"], []), - "actual_value_string": sprintf("set to '%v'",[encryption.encryption_settings]) + "remediation": null, + "remediationType": null } -} - -CxPolicy[result] { #legacy - resource := input.document[i].resource.azurerm_managed_disk[name] - - common_lib.valid_key(resource.encryption_settings, "enabled") +} else = results { resource.encryption_settings.enabled == false - - result := { - "documentId": input.document[i].id, - "resourceType": "azurerm_managed_disk", - "resourceName": tf_lib.get_resource_name(resource, name), + results := { "searchKey": sprintf("azurerm_managed_disk[%s].encryption_settings.enabled", [name]), "issueType": "IncorrectValue", "keyExpectedValue": sprintf("'azurerm_managed_disk[%s].encryption_settings.enabled' should be set to true", [name]), "keyActualValue": sprintf("'azurerm_managed_disk[%s].encryption_settings.enabled' is set to false", [name]), "searchLine": common_lib.build_search_line(["resource", "azurerm_managed_disk", name, "encryption_settings", "enabled"], []), - "remediation": json.marshal({ - "before": "false", - "after": "true" - }), + "remediation": json.marshal({"before": "false", "after": "true"}), "remediationType": "replacement" } }