diff --git a/checkov/arm/checks/resource/SecretContentType.py b/checkov/arm/checks/resource/SecretContentType.py new file mode 100644 index 00000000000..01acf61c91c --- /dev/null +++ b/checkov/arm/checks/resource/SecretContentType.py @@ -0,0 +1,24 @@ +from __future__ import annotations + +from typing import Any +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 SecretContentType(BaseResourceValueCheck): + def __init__(self): + name = "Ensure that key vault secrets have \"content_type\" set" + id = "CKV_AZURE_114" + supported_resources = ['Microsoft.KeyVault/vaults/secrets'] + categories = [CheckCategories.GENERAL_SECURITY] + super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources) + + def get_inspected_key(self) -> str: + return "properties/contentType" + + def get_expected_value(self) -> Any: + return ANY_VALUE + + +check = SecretContentType() diff --git a/tests/arm/checks/resource/example_SecretContentType/SecretExpirationDate-FAILED.json b/tests/arm/checks/resource/example_SecretContentType/SecretExpirationDate-FAILED.json new file mode 100644 index 00000000000..1178f037182 --- /dev/null +++ b/tests/arm/checks/resource/example_SecretContentType/SecretExpirationDate-FAILED.json @@ -0,0 +1,150 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "keyVaultName": { + "type": "string", + "metadata": { + "description": "Specifies the name of the key vault." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Specifies the Azure location where the key vault should be created." + } + }, + "enabledForDeployment": { + "type": "bool", + "defaultValue": false, + "allowedValues": [ + true, + false + ], + "metadata": { + "description": "Specifies whether Azure Virtual Machines are permitted to retrieve certificates stored as secrets from the key vault." + } + }, + "enabledForDiskEncryption": { + "type": "bool", + "defaultValue": false, + "allowedValues": [ + true, + false + ], + "metadata": { + "description": "Specifies whether Azure Disk Encryption is permitted to retrieve secrets from the vault and unwrap keys." + } + }, + "enabledForTemplateDeployment": { + "type": "bool", + "defaultValue": false, + "allowedValues": [ + true, + false + ], + "metadata": { + "description": "Specifies whether Azure Resource Manager is permitted to retrieve secrets from the key vault." + } + }, + "tenantId": { + "type": "string", + "defaultValue": "[subscription().tenantId]", + "metadata": { + "description": "Specifies the Azure Active Directory tenant ID that should be used for authenticating requests to the key vault. Get it by using Get-AzSubscription cmdlet." + } + }, + "objectId": { + "type": "string", + "metadata": { + "description": "Specifies the object ID of a user, service principal or security group in the Azure Active Directory tenant for the vault. The object ID must be unique for the list of access policies. Get it by using Get-AzADUser or Get-AzADServicePrincipal cmdlets." + } + }, + "keysPermissions": { + "type": "array", + "defaultValue": [ + "list" + ], + "metadata": { + "description": "Specifies the permissions to keys in the vault. Valid values are: all, encrypt, decrypt, wrapKey, unwrapKey, sign, verify, get, list, create, update, import, delete, backup, restore, recover, and purge." + } + }, + "secretsPermissions": { + "type": "array", + "defaultValue": [ + "list" + ], + "metadata": { + "description": "Specifies the permissions to secrets in the vault. Valid values are: all, get, list, set, delete, backup, restore, recover, and purge." + } + }, + "skuName": { + "type": "string", + "defaultValue": "Standard", + "allowedValues": [ + "Standard", + "Premium" + ], + "metadata": { + "description": "Specifies whether the key vault is a standard vault or a premium vault." + } + }, + "secretName": { + "type": "string", + "metadata": { + "description": "Specifies the name of the secret that you want to create." + } + }, + "secretValue": { + "type": "securestring", + "metadata": { + "description": "Specifies the value of the secret that you want to create." + } + } + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults", + "name": "[parameters('keyVaultName')]", + "apiVersion": "2018-02-14", + "location": "[parameters('location')]", + "properties": { + "enabledForDeployment": "[parameters('enabledForDeployment')]", + "enabledForDiskEncryption": "[parameters('enabledForDiskEncryption')]", + "enabledForTemplateDeployment": "[parameters('enabledForTemplateDeployment')]", + "tenantId": "[parameters('tenantId')]", + "accessPolicies": [ + { + "objectId": "[parameters('objectId')]", + "tenantId": "[parameters('tenantId')]", + "permissions": { + "keys": "[parameters('keysPermissions')]", + "secrets": "[parameters('secretsPermissions')]" + } + } + ], + "sku": { + "name": "[parameters('skuName')]", + "family": "A" + }, + "networkAcls": { + "defaultAction": "Allow", + "bypass": "AzureServices" + } + } + }, + { + "type": "Microsoft.KeyVault/vaults/secrets", + "name": "fail", + "apiVersion": "2018-02-14", + "location": "[parameters('location')]", + "dependsOn": [ + "[resourceId('Microsoft.KeyVault/vaults', parameters('keyVaultName'))]" + ], + "properties": { + "value": "[parameters('secretValue')]" + } + } + ] +} diff --git a/tests/arm/checks/resource/example_SecretContentType/SecretExpirationDate-PASSED.json b/tests/arm/checks/resource/example_SecretContentType/SecretExpirationDate-PASSED.json new file mode 100644 index 00000000000..20b6030ce47 --- /dev/null +++ b/tests/arm/checks/resource/example_SecretContentType/SecretExpirationDate-PASSED.json @@ -0,0 +1,53 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "vaults_pike1_name": { + "defaultValue": "pike1", + "type": "String" + } + }, + "variables": {}, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2023-02-01", + "name": "[parameters('vaults_pike1_name')]", + "location": "eastus", + "properties": { + "sku": { + "family": "A", + "name": "Standard" + }, + "tenantId": "8e7f742a-4215-44a0-881b-209124f286b1", + "accessPolicies": [], + "enabledForDeployment": false, + "enabledForDiskEncryption": false, + "enabledForTemplateDeployment": false, + "enableSoftDelete": true, + "softDeleteRetentionInDays": 90, + "enableRbacAuthorization": true, + "vaultUri": "[concat('https://', parameters('vaults_pike1_name'), '.vault.azure.net/')]", + "provisioningState": "Succeeded", + "publicNetworkAccess": "Enabled" + } + }, + { + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2023-02-01", + "name": "pass", + "location": "eastus", + "dependsOn": [ + "[resourceId('Microsoft.KeyVault/vaults', parameters('vaults_pike1_name'))]" + ], + "properties": { + "contentType": "string", + "attributes": { + "enabled": true, + "nbf": 1692951264, + "exp": 1756109371 + } + } + } + ] +} \ No newline at end of file diff --git a/tests/arm/checks/resource/test_SecretContentType.py b/tests/arm/checks/resource/test_SecretContentType.py new file mode 100644 index 00000000000..fcb33309990 --- /dev/null +++ b/tests/arm/checks/resource/test_SecretContentType.py @@ -0,0 +1,40 @@ +import unittest +from pathlib import Path + +from checkov.arm.checks.resource.SecretContentType import check +from checkov.arm.runner import Runner +from checkov.runner_filter import RunnerFilter + + +class TestSecretContentType(unittest.TestCase): + def test_summary(self): + # given + test_files_dir = Path(__file__).parent / "example_SecretContentType" + + # when + report = Runner().run(root_folder=str(test_files_dir), runner_filter=RunnerFilter(checks=[check.id])) + + # then + summary = report.get_summary() + + passing_resources = { + "Microsoft.KeyVault/vaults/secrets.pass", + } + failing_resources = { + "Microsoft.KeyVault/vaults/secrets.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()