diff --git a/checkov/arm/checks/resource/ACRAnonymousPullDisabled.py b/checkov/arm/checks/resource/ACRAnonymousPullDisabled.py new file mode 100644 index 00000000000..812a084160c --- /dev/null +++ b/checkov/arm/checks/resource/ACRAnonymousPullDisabled.py @@ -0,0 +1,38 @@ +from __future__ import annotations + +from typing import Any + +from checkov.common.models.enums import CheckResult, CheckCategories +from checkov.arm.base_resource_check import BaseResourceCheck + + +class ACRAnonymousPullDisabled(BaseResourceCheck): + ANONYMOUS_PULL_SKUS = {"Standard", "Premium"} # noqa: CCE003 # a static attribute + + def __init__(self) -> None: + name = "Ensures that ACR disables anonymous pulling of images" + id = "CKV_AZURE_138" + supported_resources = ("Microsoft.ContainerRegistry/registries",) + categories = (CheckCategories.IAM,) + super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources) + + def scan_resource_conf(self, conf: dict[str, Any]) -> CheckResult: + properties = conf.get("properties", {}) + + anonymousPullEnabled = properties.get("anonymousPullEnabled") + + sku = conf.get("sku") + + if ( + sku is not None + and isinstance(sku.get("name"), str) + and sku.get("name") in ACRAnonymousPullDisabled.ANONYMOUS_PULL_SKUS + and properties + and anonymousPullEnabled + ): + return CheckResult.FAILED + + return CheckResult.PASSED + + +check = ACRAnonymousPullDisabled() diff --git a/tests/arm/checks/resource/example_ACRAnonymousPullDisabled/fail.json b/tests/arm/checks/resource/example_ACRAnonymousPullDisabled/fail.json new file mode 100644 index 00000000000..f489195f39c --- /dev/null +++ b/tests/arm/checks/resource/example_ACRAnonymousPullDisabled/fail.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [ + { + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2021-09-01", + "name": "fail", + "location": "eastus", + "sku": { + "name": "Standard" + }, + "properties": { + "anonymousPullEnabled": true + } + } + ] +} diff --git a/tests/arm/checks/resource/example_ACRAnonymousPullDisabled/fail2.json b/tests/arm/checks/resource/example_ACRAnonymousPullDisabled/fail2.json new file mode 100644 index 00000000000..74c8af184a0 --- /dev/null +++ b/tests/arm/checks/resource/example_ACRAnonymousPullDisabled/fail2.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [ + { + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2021-09-01", + "name": "fail2", + "location": "eastus", + "sku": { + "name": "Premium" + }, + "properties": { + "anonymousPullEnabled": true + } + } + ] +} diff --git a/tests/arm/checks/resource/example_ACRAnonymousPullDisabled/pass.json b/tests/arm/checks/resource/example_ACRAnonymousPullDisabled/pass.json new file mode 100644 index 00000000000..07f29b948a3 --- /dev/null +++ b/tests/arm/checks/resource/example_ACRAnonymousPullDisabled/pass.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [ + { + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2021-09-01", + "name": "pass", + "location": "eastus", + "sku": { + "name": [] + }, + "properties": { + "anonymousPullEnabled": true + } + } + ] +} diff --git a/tests/arm/checks/resource/example_ACRAnonymousPullDisabled/pass2.json b/tests/arm/checks/resource/example_ACRAnonymousPullDisabled/pass2.json new file mode 100644 index 00000000000..f48095bb9de --- /dev/null +++ b/tests/arm/checks/resource/example_ACRAnonymousPullDisabled/pass2.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [ + { + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2021-09-01", + "name": "pass2", + "location": "eastus", + "sku": { + "name": "Premium" + }, + "properties": { + "anonymousPullEnabled": false + } + } + ] +} diff --git a/tests/arm/checks/resource/example_ACRAnonymousPullDisabled/pass3.json b/tests/arm/checks/resource/example_ACRAnonymousPullDisabled/pass3.json new file mode 100644 index 00000000000..a961d830bb4 --- /dev/null +++ b/tests/arm/checks/resource/example_ACRAnonymousPullDisabled/pass3.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [ + { + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2021-09-01", + "name": "pass3", + "location": "eastus", + "sku": { + "name": "Premium" + }, + "properties": { + "zoneRedundancy": "Disabled" + + } + } + ] +} diff --git a/tests/arm/checks/resource/example_ACRAnonymousPullDisabled/pass4.json b/tests/arm/checks/resource/example_ACRAnonymousPullDisabled/pass4.json new file mode 100644 index 00000000000..9e83eae9d55 --- /dev/null +++ b/tests/arm/checks/resource/example_ACRAnonymousPullDisabled/pass4.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [ + { + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2021-09-01", + "name": "pass4", + "location": "eastus", + "sku": { + "name": "Standard" + }, + "properties": { + "zoneRedundancy": "Disabled" + } + } + ] +} diff --git a/tests/arm/checks/resource/example_ACRAnonymousPullDisabled/pass5.json b/tests/arm/checks/resource/example_ACRAnonymousPullDisabled/pass5.json new file mode 100644 index 00000000000..61eef7b345e --- /dev/null +++ b/tests/arm/checks/resource/example_ACRAnonymousPullDisabled/pass5.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [ + { + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2021-09-01", + "name": "pass5", + "location": "eastus", + "sku": { + "name": "Basic" + }, + "properties": { + "anonymousPullEnabled": true + } + } + ] +} diff --git a/tests/arm/checks/resource/example_ACRAnonymousPullDisabled/pass6.json b/tests/arm/checks/resource/example_ACRAnonymousPullDisabled/pass6.json new file mode 100644 index 00000000000..5936f1d2642 --- /dev/null +++ b/tests/arm/checks/resource/example_ACRAnonymousPullDisabled/pass6.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [ + { + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2021-09-01", + "name": "pass6", + "location": "eastus", + "properties": { + "anonymousPullEnabled": true + } + } + ] +} diff --git a/tests/arm/checks/resource/test_ACRAnonymousPullDisabled.py b/tests/arm/checks/resource/test_ACRAnonymousPullDisabled.py new file mode 100644 index 00000000000..3f1e6ee4f03 --- /dev/null +++ b/tests/arm/checks/resource/test_ACRAnonymousPullDisabled.py @@ -0,0 +1,46 @@ +import unittest +from pathlib import Path + +from checkov.arm.checks.resource.ACRAnonymousPullDisabled import check +from checkov.arm.runner import Runner +from checkov.runner_filter import RunnerFilter + + +class TestACRAnonymousPullDisabled(unittest.TestCase): + def test_summary(self): + # given + test_files_dir = Path(__file__).parent / "example_ACRAnonymousPullDisabled" + + # when + report = Runner().run(root_folder=str(test_files_dir), runner_filter=RunnerFilter(checks=[check.id])) + + # then + summary = report.get_summary() + + passing_resources = { + "Microsoft.ContainerRegistry/registries.pass", + "Microsoft.ContainerRegistry/registries.pass2", + "Microsoft.ContainerRegistry/registries.pass3", + "Microsoft.ContainerRegistry/registries.pass4", + "Microsoft.ContainerRegistry/registries.pass5", + "Microsoft.ContainerRegistry/registries.pass6" + } + failing_resources = { + "Microsoft.ContainerRegistry/registries.fail", + "Microsoft.ContainerRegistry/registries.fail2" + } + + 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()