From d70f1dc53b74b7666ce350d3172364a389a1a607 Mon Sep 17 00:00:00 2001 From: James Woolfenden Date: Wed, 30 Aug 2023 13:30:40 +0100 Subject: [PATCH] feat(arm): implement CKV_AZURE_100 for arm (#5490) * feat(arm): implement CKV_AZURE_100 for arm * feat(arm): implement CKV_AZURE_100 for arm --- .../arm/checks/resource/CosmosDBHaveCMK.py | 21 +++ .../example_CosmosDBHaveCMK/fail.json | 65 ++++++++++ .../example_CosmosDBHaveCMK/pass.json | 66 ++++++++++ .../checks/resource/test_CosmosDBHaveCMK.py | 40 ++++++ .../azure/example_CosmosDBHaveCMK/main.tf | 78 +++++++++++ .../resource/azure/test_CosmosDBHaveCMK.py | 122 +++++------------- 6 files changed, 300 insertions(+), 92 deletions(-) create mode 100644 checkov/arm/checks/resource/CosmosDBHaveCMK.py create mode 100644 tests/arm/checks/resource/example_CosmosDBHaveCMK/fail.json create mode 100644 tests/arm/checks/resource/example_CosmosDBHaveCMK/pass.json create mode 100644 tests/arm/checks/resource/test_CosmosDBHaveCMK.py create mode 100644 tests/terraform/checks/resource/azure/example_CosmosDBHaveCMK/main.tf diff --git a/checkov/arm/checks/resource/CosmosDBHaveCMK.py b/checkov/arm/checks/resource/CosmosDBHaveCMK.py new file mode 100644 index 00000000000..d05125b7361 --- /dev/null +++ b/checkov/arm/checks/resource/CosmosDBHaveCMK.py @@ -0,0 +1,21 @@ +from checkov.common.models.consts import ANY_VALUE +from checkov.common.models.enums import CheckCategories +from checkov.arm.base_resource_value_check import BaseResourceValueCheck + + +class CosmosDBHaveCMK(BaseResourceValueCheck): + def __init__(self): + name = "Ensure that Cosmos DB accounts have customer-managed keys to encrypt data at rest" + id = "CKV_AZURE_100" + supported_resources = ['Microsoft.DocumentDb/databaseAccounts'] + categories = [CheckCategories.NETWORKING] + super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources) + + def get_inspected_key(self): + return 'properties/keyVaultKeyUri' + + def get_expected_value(self): + return ANY_VALUE + + +check = CosmosDBHaveCMK() diff --git a/tests/arm/checks/resource/example_CosmosDBHaveCMK/fail.json b/tests/arm/checks/resource/example_CosmosDBHaveCMK/fail.json new file mode 100644 index 00000000000..46570ed5c82 --- /dev/null +++ b/tests/arm/checks/resource/example_CosmosDBHaveCMK/fail.json @@ -0,0 +1,65 @@ +{ + "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string" + }, + "locationName": { + "type": "string" + }, + "defaultExperience": { + "type": "string" + }, + "isZoneRedundant": { + "type": "string" + } + }, + "variables": {}, + "resources": [ + { + "apiVersion": "2023-03-15-preview", + "kind": "GlobalDocumentDB", + "type": "Microsoft.DocumentDb/databaseAccounts", + "name": "fail", + "location": "[parameters('location')]", + "properties": { + "databaseAccountOfferType": "Standard", + "locations": [ + { + "id": "[concat(parameters('name'), '-', parameters('location'))]", + "failoverPriority": 0, + "locationName": "[parameters('locationName')]" + } + ], + "backupPolicy": { + "type": "Periodic", + "periodicModeProperties": { + "backupIntervalInMinutes": 240, + "backupRetentionIntervalInHours": 8, + "backupStorageRedundancy": "Geo" + } + }, + "isVirtualNetworkFilterEnabled": false, + "virtualNetworkRules": [], + "ipRules": [], + "dependsOn": [], + "minimalTlsVersion": "Tls12", + "enableMultipleWriteLocations": false, + "capabilities": [], + "enableFreeTier": true, + "capacity": { + "totalThroughputLimit": 1000 + } + }, + "tags": { + "defaultExperience": "[parameters('defaultExperience')]", + "hidden-cosmos-mmspecial": "" + } + } + ], + "outputs": {} +} \ No newline at end of file diff --git a/tests/arm/checks/resource/example_CosmosDBHaveCMK/pass.json b/tests/arm/checks/resource/example_CosmosDBHaveCMK/pass.json new file mode 100644 index 00000000000..4e5e8a87614 --- /dev/null +++ b/tests/arm/checks/resource/example_CosmosDBHaveCMK/pass.json @@ -0,0 +1,66 @@ +{ + "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "type": "string" + }, + "location": { + "type": "string" + }, + "locationName": { + "type": "string" + }, + "defaultExperience": { + "type": "string" + }, + "isZoneRedundant": { + "type": "string" + } + }, + "variables": {}, + "resources": [ + { + "apiVersion": "2023-03-15-preview", + "kind": "GlobalDocumentDB", + "type": "Microsoft.DocumentDb/databaseAccounts", + "name": "pass", + "location": "[parameters('location')]", + "properties": { + "databaseAccountOfferType": "Standard", + "locations": [ + { + "id": "[concat(parameters('name'), '-', parameters('location'))]", + "failoverPriority": 0, + "locationName": "[parameters('locationName')]" + } + ], + "backupPolicy": { + "type": "Periodic", + "periodicModeProperties": { + "backupIntervalInMinutes": 240, + "backupRetentionIntervalInHours": 8, + "backupStorageRedundancy": "Geo" + } + }, + "isVirtualNetworkFilterEnabled": false, + "virtualNetworkRules": [], + "ipRules": [], + "dependsOn": [], + "minimalTlsVersion": "Tls12", + "enableMultipleWriteLocations": false, + "capabilities": [], + "enableFreeTier": true, + "capacity": { + "totalThroughputLimit": 1000 + }, + "keyVaultKeyUri": "https://examplekeyvaultjgw.vault.azure.net/keys/temp" + }, + "tags": { + "defaultExperience": "[parameters('defaultExperience')]", + "hidden-cosmos-mmspecial": "" + } + } + ], + "outputs": {} +} \ No newline at end of file diff --git a/tests/arm/checks/resource/test_CosmosDBHaveCMK.py b/tests/arm/checks/resource/test_CosmosDBHaveCMK.py new file mode 100644 index 00000000000..f474cd1e0ba --- /dev/null +++ b/tests/arm/checks/resource/test_CosmosDBHaveCMK.py @@ -0,0 +1,40 @@ +import unittest +from pathlib import Path + +from checkov.arm.checks.resource.CosmosDBHaveCMK import check +from checkov.arm.runner import Runner +from checkov.runner_filter import RunnerFilter + + +class TestCosmosDBHaveCMK(unittest.TestCase): + def test_summary(self): + # given + test_files_dir = Path(__file__).parent / "example_CosmosDBHaveCMK" + + # when + report = Runner().run(root_folder=str(test_files_dir), runner_filter=RunnerFilter(checks=[check.id])) + + # then + summary = report.get_summary() + + passing_resources = { + "Microsoft.DocumentDb/databaseAccounts.pass", + } + failing_resources = { + "Microsoft.DocumentDb/databaseAccounts.fail", + } + + passed_check_resources = {c.resource for c in report.passed_checks} + failed_check_resources = {c.resource for c in report.failed_checks} + + self.assertEqual(summary["passed"], len(passing_resources)) + self.assertEqual(summary["failed"], len(failing_resources)) + self.assertEqual(summary["skipped"], 0) + self.assertEqual(summary["parsing_errors"], 0) + + self.assertEqual(passing_resources, passed_check_resources) + self.assertEqual(failing_resources, failed_check_resources) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/terraform/checks/resource/azure/example_CosmosDBHaveCMK/main.tf b/tests/terraform/checks/resource/azure/example_CosmosDBHaveCMK/main.tf new file mode 100644 index 00000000000..601871f1873 --- /dev/null +++ b/tests/terraform/checks/resource/azure/example_CosmosDBHaveCMK/main.tf @@ -0,0 +1,78 @@ + +resource "azurerm_cosmosdb_account" "fail" { + name = "tfex-cosmos-db-${random_integer.ri.result}" + location = azurerm_resource_group.rg.location + resource_group_name = azurerm_resource_group.rg.name + offer_type = "Standard" + kind = "GlobalDocumentDB" + + enable_automatic_failover = true + + capabilities { + name = "EnableAggregationPipeline" + } + + capabilities { + name = "mongoEnableDocLevelTTL" + } + + capabilities { + name = "MongoDBv3.4" + } + + consistency_policy { + consistency_level = "BoundedStaleness" + max_interval_in_seconds = 10 + max_staleness_prefix = 200 + } + + geo_location { + location = var.failover_location + failover_priority = 1 + } + + geo_location { + location = azurerm_resource_group.rg.location + failover_priority = 0 + } +} + +resource "azurerm_cosmosdb_account" "pass" { + name = "tfex-cosmos-db-${random_integer.ri.result}" + location = azurerm_resource_group.rg.location + resource_group_name = azurerm_resource_group.rg.name + offer_type = "Standard" + kind = "GlobalDocumentDB" + + enable_automatic_failover = true + + capabilities { + name = "EnableAggregationPipeline" + } + + capabilities { + name = "mongoEnableDocLevelTTL" + } + + capabilities { + name = "MongoDBv3.4" + } + + consistency_policy { + consistency_level = "BoundedStaleness" + max_interval_in_seconds = 10 + max_staleness_prefix = 200 + } + + geo_location { + location = var.failover_location + failover_priority = 1 + } + + geo_location { + location = azurerm_resource_group.rg.location + failover_priority = 0 + } + + key_vault_key_id = "A versionless Key Vault Key ID for CMK encryption" +} diff --git a/tests/terraform/checks/resource/azure/test_CosmosDBHaveCMK.py b/tests/terraform/checks/resource/azure/test_CosmosDBHaveCMK.py index d719dbb832d..7fb1f45bf6d 100644 --- a/tests/terraform/checks/resource/azure/test_CosmosDBHaveCMK.py +++ b/tests/terraform/checks/resource/azure/test_CosmosDBHaveCMK.py @@ -1,103 +1,41 @@ +import os import unittest -import hcl2 - +from checkov.runner_filter import RunnerFilter +from checkov.terraform.runner import Runner from checkov.terraform.checks.resource.azure.CosmosDBHaveCMK import check -from checkov.common.models.enums import CheckResult class TestCosmosDBHaveCMK(unittest.TestCase): - def test_failure(self): - hcl_res = hcl2.loads(""" - resource "azurerm_cosmosdb_account" "db" { - name = "tfex-cosmos-db-${random_integer.ri.result}" - location = azurerm_resource_group.rg.location - resource_group_name = azurerm_resource_group.rg.name - offer_type = "Standard" - kind = "GlobalDocumentDB" - - enable_automatic_failover = true - - capabilities { - name = "EnableAggregationPipeline" - } - - capabilities { - name = "mongoEnableDocLevelTTL" - } - - capabilities { - name = "MongoDBv3.4" - } - - consistency_policy { - consistency_level = "BoundedStaleness" - max_interval_in_seconds = 10 - max_staleness_prefix = 200 - } - - geo_location { - location = var.failover_location - failover_priority = 1 - } - - geo_location { - location = azurerm_resource_group.rg.location - failover_priority = 0 - } - } - """) - resource_conf = hcl_res['resource'][0]['azurerm_cosmosdb_account']['db'] - scan_result = check.scan_resource_conf(conf=resource_conf) - self.assertEqual(CheckResult.FAILED, scan_result) + def test(self): + runner = Runner() + current_dir = os.path.dirname(os.path.realpath(__file__)) + + test_files_dir = os.path.join(current_dir, "example_CosmosDBHaveCMK") + report = runner.run(root_folder=test_files_dir, + runner_filter=RunnerFilter(checks=[check.id])) + summary = report.get_summary() + + passing_resources = { + 'azurerm_cosmosdb_account.pass' + } + failing_resources = { + 'azurerm_cosmosdb_account.fail', + } + skipped_resources = {} + + passed_check_resources = set([c.resource for c in report.passed_checks]) + failed_check_resources = set([c.resource for c in report.failed_checks]) + + self.assertEqual(summary['passed'], len(passing_resources)) + self.assertEqual(summary['failed'], len(failing_resources)) + self.assertEqual(summary['skipped'], len(skipped_resources)) + self.assertEqual(summary['parsing_errors'], 0) - def test_success(self): - hcl_res = hcl2.loads(""" - resource "azurerm_cosmosdb_account" "db" { - name = "tfex-cosmos-db-${random_integer.ri.result}" - location = azurerm_resource_group.rg.location - resource_group_name = azurerm_resource_group.rg.name - offer_type = "Standard" - kind = "GlobalDocumentDB" - - enable_automatic_failover = true - - capabilities { - name = "EnableAggregationPipeline" - } - - capabilities { - name = "mongoEnableDocLevelTTL" - } - - capabilities { - name = "MongoDBv3.4" - } - - consistency_policy { - consistency_level = "BoundedStaleness" - max_interval_in_seconds = 10 - max_staleness_prefix = 200 - } - - geo_location { - location = var.failover_location - failover_priority = 1 - } - - geo_location { - location = azurerm_resource_group.rg.location - failover_priority = 0 - } - - key_vault_key_id = "A versionless Key Vault Key ID for CMK encryption" - } - """) - resource_conf = hcl_res['resource'][0]['azurerm_cosmosdb_account']['db'] - scan_result = check.scan_resource_conf(conf=resource_conf) - self.assertEqual(CheckResult.PASSED, scan_result) + self.assertEqual(passing_resources, passed_check_resources) + self.assertEqual(failing_resources, failed_check_resources) if __name__ == '__main__': - unittest.main() + unittest.main() \ No newline at end of file