From a3c3a283b0af77a58cf648df4c501d1600bef336 Mon Sep 17 00:00:00 2001 From: Taylor <28880387+tsmithv11@users.noreply.github.com> Date: Wed, 27 Nov 2024 05:52:45 -0800 Subject: [PATCH] feat(terraform): Add new checks to match run checks (#6868) * Add check for 424a5e77-8997-47d9-b0e0-daaca8b81b01 * Add check for d480c1d2-06b3-4e53-81c9-a21ed83cb5fc * Add egress check * Fix test * Adjust tests * Add new NSG check * Revert "Add new NSG check" This reverts commit 1be3da1047c4018d35b4a86ba47902ca3cfff3a5. * New check * Fix ID --- .../azure/AzureSpringCloudTLSDisabled.yaml | 47 +++++++ .../aws/AbsSecurityGroupUnrestrictedEgress.py | 118 ++++++++++++++++++ .../aws/SecurityGroupUnrestrictedEgressAny.py | 10 ++ ...ureContainerInstancePublicIPAddressType.py | 22 ++++ ...KubernetesClusterHTTPApplicationRouting.py | 22 ++++ .../main.tf | 110 ++++++++++++++++ ...test_SecurityGroupUnrestrictedEgressAny.py | 47 +++++++ .../main.tf | 16 +++ .../main.tf | 11 ++ ...ureContainerInstancePublicIPAddressType.py | 42 +++++++ ...KubernetesClusterHTTPApplicationRouting.py | 42 +++++++ .../AzureSpringCloudTLSDisabled/expected.yaml | 7 ++ .../AzureSpringCloudTLSDisabled/main.tf | 95 ++++++++++++++ .../graph/checks/test_yaml_policies.py | 3 + .../test_runner_azure_resources.py | 2 +- tests/terraform/runner/test_plan_runner.py | 2 +- tests/terraform/runner/test_runner.py | 2 + 17 files changed, 596 insertions(+), 2 deletions(-) create mode 100644 checkov/terraform/checks/graph_checks/azure/AzureSpringCloudTLSDisabled.yaml create mode 100644 checkov/terraform/checks/resource/aws/AbsSecurityGroupUnrestrictedEgress.py create mode 100644 checkov/terraform/checks/resource/aws/SecurityGroupUnrestrictedEgressAny.py create mode 100644 checkov/terraform/checks/resource/azure/AzureContainerInstancePublicIPAddressType.py create mode 100644 checkov/terraform/checks/resource/azure/KubernetesClusterHTTPApplicationRouting.py create mode 100644 tests/terraform/checks/resource/aws/example_SecurityGroupUnrestrictedEgressAny/main.tf create mode 100644 tests/terraform/checks/resource/aws/test_SecurityGroupUnrestrictedEgressAny.py create mode 100644 tests/terraform/checks/resource/azure/example_AzureContainerInstancePublicIPAddressType/main.tf create mode 100644 tests/terraform/checks/resource/azure/example_KubernetesClusterHTTPApplicationRouting/main.tf create mode 100644 tests/terraform/checks/resource/azure/test_AzureContainerInstancePublicIPAddressType.py create mode 100644 tests/terraform/checks/resource/azure/test_KubernetesClusterHTTPApplicationRouting.py create mode 100644 tests/terraform/graph/checks/resources/AzureSpringCloudTLSDisabled/expected.yaml create mode 100644 tests/terraform/graph/checks/resources/AzureSpringCloudTLSDisabled/main.tf diff --git a/checkov/terraform/checks/graph_checks/azure/AzureSpringCloudTLSDisabled.yaml b/checkov/terraform/checks/graph_checks/azure/AzureSpringCloudTLSDisabled.yaml new file mode 100644 index 00000000000..292d024e2d5 --- /dev/null +++ b/checkov/terraform/checks/graph_checks/azure/AzureSpringCloudTLSDisabled.yaml @@ -0,0 +1,47 @@ +metadata: + id: "CKV2_AZURE_55" + name: "Ensure Azure Spring Cloud app end-to-end TLS is enabled" + category: "NETWORKING" +definition: + or: + - cond_type: attribute + resource_types: + - azurerm_spring_cloud_service + attribute: sku_tier + operator: not_exists + - cond_type: attribute + resource_types: + - azurerm_spring_cloud_service + attribute: sku_tier + operator: equals + value: "Basic" + - and: + - cond_type: filter + attribute: resource_type + value: + - azurerm_spring_cloud_service + operator: within + - or: + - resource_types: + - azurerm_spring_cloud_service + connected_resource_types: + - azurerm_spring_cloud_app + operator: not_exists + cond_type: connection + - and: + - resource_types: + - azurerm_spring_cloud_service + connected_resource_types: + - azurerm_spring_cloud_app + operator: exists + cond_type: connection + - cond_type: attribute + resource_types: + - azurerm_spring_cloud_app + attribute: tls_enabled + operator: exists + - cond_type: attribute + resource_types: + - azurerm_spring_cloud_app + attribute: tls_enabled + operator: is_true diff --git a/checkov/terraform/checks/resource/aws/AbsSecurityGroupUnrestrictedEgress.py b/checkov/terraform/checks/resource/aws/AbsSecurityGroupUnrestrictedEgress.py new file mode 100644 index 00000000000..f4bf86cf1df --- /dev/null +++ b/checkov/terraform/checks/resource/aws/AbsSecurityGroupUnrestrictedEgress.py @@ -0,0 +1,118 @@ +from __future__ import annotations + +from typing import Any + +from checkov.common.models.enums import CheckResult, CheckCategories +from checkov.terraform.checks.resource.base_resource_check import BaseResourceCheck +from checkov.common.util.type_forcers import force_list +from checkov.common.util.type_forcers import force_int + + +class AbsSecurityGroupUnrestrictedEgress(BaseResourceCheck): + def __init__(self, check_id: str, port: int) -> None: + name = f"Ensure no security groups allow egress from 0.0.0.0:0 to port {port}" + supported_resources = ('aws_security_group', 'aws_security_group_rule', 'aws_vpc_security_group_egress_rule') + categories = (CheckCategories.NETWORKING,) + super().__init__(name=name, id=check_id, categories=categories, supported_resources=supported_resources) + self.port = port + + def scan_resource_conf(self, conf: dict[str, list[Any]]) -> CheckResult: + """ + Looks for configuration at security group egress rules : + https://www.terraform.io/docs/providers/aws/r/security_group.html + https://www.terraform.io/docs/providers/aws/r/security_group_rule.html + + Return PASS if: + - The resource is an aws_security_group that contains no violating egress rules (including if there are no + egress rules at all), OR + - The resource is an aws_security_group_rule of type 'egress' that does not violate the check. + + Return FAIL if: + - The resource is an aws_security_group that contains a violating egress rule, OR + - The resource is an aws_security_group_rule of type 'egress' that violates the check. + + Return UNKNOWN if: + - the resource is an aws_security_group_rule of type 'egress', OR + + :param conf: aws_security_group configuration + :return: + """ + + if 'egress' in conf: # This means it's an SG resource with egress block(s) + egress_conf = conf['egress'] + for egress_rule in egress_conf: + for rule in force_list(egress_rule): + if isinstance(rule, dict): + if self.check_self(rule): + return CheckResult.PASSED + if self.contains_violation(rule): + self.evaluated_keys = [ + f'egress/[{egress_conf.index(egress_rule)}]/from_port', + f'egress/[{egress_conf.index(egress_rule)}]/to_port', + f'egress/[{egress_conf.index(egress_rule)}]/cidr_blocks', + f'egress/[{egress_conf.index(egress_rule)}]/ipv6_cidr_blocks', + ] + return CheckResult.FAILED + + return CheckResult.PASSED + + if 'type' in conf: # This means it's an SG_rule resource. + type = force_list(conf['type'])[0] + if type == 'egress': + if self.check_self(conf): + return CheckResult.PASSED + self.evaluated_keys = ['from_port', 'to_port', 'cidr_blocks', 'ipv6_cidr_blocks'] + if self.contains_violation(conf): + return CheckResult.FAILED + return CheckResult.PASSED + return CheckResult.UNKNOWN + else: + self.evaluated_keys = ['from_port', 'to_port', 'cidr_ipv4', 'cidr_ipv6'] + if 'from_port' in conf or 'to_port' in conf: + if self.contains_violation(conf): + return CheckResult.FAILED + return CheckResult.PASSED + + return CheckResult.PASSED + + def contains_violation(self, conf: dict[str, list[Any]]) -> bool: + from_port = force_int(force_list(conf.get('from_port', [{-1}]))[0]) + to_port = force_int(force_list(conf.get('to_port', [{-1}]))[0]) + protocol = force_list(conf.get('protocol', [None]))[0] + if from_port == 0 and to_port == 0: + to_port = 65535 + + prefix_list_ids = conf.get('prefix_list_ids') + if prefix_list_ids and prefix_list_ids != [[]]: + return False + + if from_port is not None and to_port is not None and (from_port <= self.port <= to_port) or ( + protocol == '-1' and from_port == 0 and to_port == 65535): + if conf.get('cidr_blocks'): + conf_cidr_blocks = conf.get('cidr_blocks', [[]]) + else: + conf_cidr_blocks = conf.get('cidr_ipv4', [[]]) + if conf_cidr_blocks and len(conf_cidr_blocks) > 0: + conf_cidr_blocks = conf_cidr_blocks[0] + cidr_blocks = force_list(conf_cidr_blocks) + if "0.0.0.0/0" in cidr_blocks: + return True + if conf.get('ipv6_cidr_blocks'): + ipv6_cidr_blocks = conf.get('ipv6_cidr_blocks', []) + else: + ipv6_cidr_blocks = conf.get('cidr_ipv6', []) + if ipv6_cidr_blocks and ipv6_cidr_blocks[0] is not None and \ + any(ip in ['::/0', '0000:0000:0000:0000:0000:0000:0000:0000/0'] for ip in ipv6_cidr_blocks[0]): + return True + if not ipv6_cidr_blocks and not cidr_blocks \ + and conf.get('security_groups') is None \ + and conf.get('source_security_group_id') is None: + return True + return False + + def check_self(self, conf: dict[str, list[Any]]) -> bool: + if conf.get('self'): + limit = force_list(conf['self'])[0] + if limit: + return True + return False diff --git a/checkov/terraform/checks/resource/aws/SecurityGroupUnrestrictedEgressAny.py b/checkov/terraform/checks/resource/aws/SecurityGroupUnrestrictedEgressAny.py new file mode 100644 index 00000000000..8462f4e8167 --- /dev/null +++ b/checkov/terraform/checks/resource/aws/SecurityGroupUnrestrictedEgressAny.py @@ -0,0 +1,10 @@ +from checkov.terraform.checks.resource.aws.AbsSecurityGroupUnrestrictedEgress import\ + AbsSecurityGroupUnrestrictedEgress + + +class SecurityGroupUnrestrictedEgressAll(AbsSecurityGroupUnrestrictedEgress): + def __init__(self): + super().__init__(check_id="CKV_AWS_382", port=-1) + + +check = SecurityGroupUnrestrictedEgressAll() diff --git a/checkov/terraform/checks/resource/azure/AzureContainerInstancePublicIPAddressType.py b/checkov/terraform/checks/resource/azure/AzureContainerInstancePublicIPAddressType.py new file mode 100644 index 00000000000..ea89f69981a --- /dev/null +++ b/checkov/terraform/checks/resource/azure/AzureContainerInstancePublicIPAddressType.py @@ -0,0 +1,22 @@ +from typing import List + +from checkov.common.models.enums import CheckCategories +from checkov.terraform.checks.resource.base_resource_value_check import BaseResourceValueCheck + + +class AzureContainerInstancePublicIPAddressType(BaseResourceValueCheck): + def __init__(self) -> None: + name = "Ensure that Azure Container group is deployed into virtual network" + id = "CKV_AZURE_245" + supported_resources = ('azurerm_container_group',) + categories = (CheckCategories.NETWORKING,) + super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources) + + def get_inspected_key(self) -> str: + return 'ip_address_type' + + def get_expected_values(self) -> List[str]: + return ['Private', 'None'] + + +check = AzureContainerInstancePublicIPAddressType() diff --git a/checkov/terraform/checks/resource/azure/KubernetesClusterHTTPApplicationRouting.py b/checkov/terraform/checks/resource/azure/KubernetesClusterHTTPApplicationRouting.py new file mode 100644 index 00000000000..f9a397f5b69 --- /dev/null +++ b/checkov/terraform/checks/resource/azure/KubernetesClusterHTTPApplicationRouting.py @@ -0,0 +1,22 @@ +from typing import List, Any + +from checkov.terraform.checks.resource.base_resource_negative_value_check import BaseResourceNegativeValueCheck +from checkov.common.models.enums import CheckCategories + + +class KubernetesClusterHTTPApplicationRouting(BaseResourceNegativeValueCheck): + def __init__(self) -> None: + name = "Ensure Azure AKS cluster HTTP application routing is disabled" + id = "CKV_AZURE_246" + supported_resources = ('azurerm_kubernetes_cluster',) + categories = (CheckCategories.NETWORKING,) + super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources) + + def get_inspected_key(self) -> str: + return "http_application_routing_enabled" + + def get_forbidden_values(self) -> List[Any]: + return [True] + + +check = KubernetesClusterHTTPApplicationRouting() diff --git a/tests/terraform/checks/resource/aws/example_SecurityGroupUnrestrictedEgressAny/main.tf b/tests/terraform/checks/resource/aws/example_SecurityGroupUnrestrictedEgressAny/main.tf new file mode 100644 index 00000000000..80d7f3c10e8 --- /dev/null +++ b/tests/terraform/checks/resource/aws/example_SecurityGroupUnrestrictedEgressAny/main.tf @@ -0,0 +1,110 @@ +resource "aws_security_group" "pass" { + name = "example" + vpc_id = "aws_vpc.example.id" + + ingress { + cidr_blocks = ["0.0.0.0/0"] + from_port = 80 + to_port = 80 + protocol = "tcp" + } + ingress { + cidr_blocks = ["0.0.0.0/0"] + from_port = 443 + to_port = 443 + protocol = "tcp" + } + egress { + cidr_blocks = ["0.0.0.0/0"] + from_port = 20 + to_port = 200 + protocol = "-1" + } +} + +resource "aws_security_group" "fail2" { + name = "example" + vpc_id = "aws_vpc.example.id" + + ingress { + cidr_blocks = ["0.0.0.0/0"] + from_port = 80 + to_port = 80 + protocol = "tcp" + } + ingress { + cidr_blocks = ["0.0.0.0/0"] + from_port = 443 + to_port = 443 + protocol = "tcp" + } + egress { + cidr_blocks = ["0.0.0.0/0"] + from_port = 0 + to_port = 0 + protocol = "-1" + } +} + +resource "aws_security_group_rule" "pass" { + cidr_blocks = ["0.0.0.0/0"] + from_port = 80 + to_port = 80 + protocol = "tcp" + security_group_id = "sg-12345" + type = "egress" +} + +resource "aws_vpc_security_group_egress_rule" "pass" { + security_group_id = aws_security_group.example.id + + cidr_ipv4 = "0.0.0.0/0" + from_port = 80 + ip_protocol = "tcp" + to_port = 80 +} + +# fail +resource "aws_security_group" "fail" { + name = "allow-all-ingress" + description = "unfettered access" + vpc_id = "test_vpc" + + egress { + from_port = -1 + to_port = -1 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + description = "Test unfettered access" + } +} + + +resource "aws_security_group_rule" "fail" { + cidr_blocks = ["0.0.0.0/0"] + from_port = -1 + to_port = -1 + protocol = "tcp" + security_group_id = "sg-12345" + description = "Test unfettered access" + type = "egress" +} + +resource "aws_security_group_rule" "fail2" { + cidr_blocks = ["0.0.0.0/0"] + from_port = 0 + to_port = 0 + protocol = "-1" + security_group_id = "sg-123456" + description = "Test unfettered access" + type = "egress" +} + +resource "aws_vpc_security_group_egress_rule" "fail" { + security_group_id = aws_security_group.example.id + + cidr_ipv4 = "0.0.0.0/0" + from_port = -1 + ip_protocol = "tcp" + to_port = -1 +} \ No newline at end of file diff --git a/tests/terraform/checks/resource/aws/test_SecurityGroupUnrestrictedEgressAny.py b/tests/terraform/checks/resource/aws/test_SecurityGroupUnrestrictedEgressAny.py new file mode 100644 index 00000000000..5ca18560a7d --- /dev/null +++ b/tests/terraform/checks/resource/aws/test_SecurityGroupUnrestrictedEgressAny.py @@ -0,0 +1,47 @@ +import unittest +from pathlib import Path + +from checkov.runner_filter import RunnerFilter +from checkov.terraform.checks.resource.aws.SecurityGroupUnrestrictedEgressAny import check +from checkov.terraform.runner import Runner + + +class TestSecurityGroupUnrestrictedEgressAny(unittest.TestCase): + def test(self): + # given + test_files_dir = Path(__file__).parent / "example_SecurityGroupUnrestrictedEgressAny" + + # when + report = Runner().run(root_folder=str(test_files_dir), runner_filter=RunnerFilter(checks=[check.id])) + + # then + summary = report.get_summary() + + passing_resources = { + "aws_security_group.pass", + "aws_security_group_rule.pass", + "aws_vpc_security_group_egress_rule.pass" + } + + failing_resources = { + "aws_security_group.fail2", + "aws_security_group.fail", + "aws_security_group_rule.fail", + "aws_vpc_security_group_egress_rule.fail", + "aws_security_group_rule.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() diff --git a/tests/terraform/checks/resource/azure/example_AzureContainerInstancePublicIPAddressType/main.tf b/tests/terraform/checks/resource/azure/example_AzureContainerInstancePublicIPAddressType/main.tf new file mode 100644 index 00000000000..b65c54956ec --- /dev/null +++ b/tests/terraform/checks/resource/azure/example_AzureContainerInstancePublicIPAddressType/main.tf @@ -0,0 +1,16 @@ +# Fail: public IP address type +resource "azurerm_container_group" "fail_public" { + name = "example-continst" + ip_address_type = "Public" +} + +# Fail: IP address type not set +resource "azurerm_container_group" "fail_notset" { + name = "example-continst" +} + +# Pass: IP address type not public +resource "azurerm_container_group" "pass" { + name = "example-continst" + ip_address_type = "Private" +} \ No newline at end of file diff --git a/tests/terraform/checks/resource/azure/example_KubernetesClusterHTTPApplicationRouting/main.tf b/tests/terraform/checks/resource/azure/example_KubernetesClusterHTTPApplicationRouting/main.tf new file mode 100644 index 00000000000..dfcc04cf0fa --- /dev/null +++ b/tests/terraform/checks/resource/azure/example_KubernetesClusterHTTPApplicationRouting/main.tf @@ -0,0 +1,11 @@ +resource "azurerm_kubernetes_cluster" "fail" { + http_application_routing_enabled = true +} + +resource "azurerm_kubernetes_cluster" "pass_false" { + http_application_routing_enabled = false +} + +resource "azurerm_kubernetes_cluster" "pass_missing" { + name = "example-aks1" +} diff --git a/tests/terraform/checks/resource/azure/test_AzureContainerInstancePublicIPAddressType.py b/tests/terraform/checks/resource/azure/test_AzureContainerInstancePublicIPAddressType.py new file mode 100644 index 00000000000..e888621993b --- /dev/null +++ b/tests/terraform/checks/resource/azure/test_AzureContainerInstancePublicIPAddressType.py @@ -0,0 +1,42 @@ +import os +import unittest + +from checkov.runner_filter import RunnerFilter +from checkov.terraform.runner import Runner +from checkov.terraform.checks.resource.azure.AzureContainerInstancePublicIPAddressType import check + + +class TestAzureContainerInstancePublicIPAddressType(unittest.TestCase): + + def test(self): + runner = Runner() + current_dir = os.path.dirname(os.path.realpath(__file__)) + + test_files_dir = os.path.join(current_dir, "example_AzureContainerInstancePublicIPAddressType") + report = runner.run(root_folder=test_files_dir, + runner_filter=RunnerFilter(checks=[check.id])) + summary = report.get_summary() + + passing_resources = { + 'azurerm_container_group.pass', + } + failing_resources = { + 'azurerm_container_group.fail_notset', + 'azurerm_container_group.fail_public', + } + 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) + + self.assertEqual(passing_resources, passed_check_resources) + self.assertEqual(failing_resources, failed_check_resources) + + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/tests/terraform/checks/resource/azure/test_KubernetesClusterHTTPApplicationRouting.py b/tests/terraform/checks/resource/azure/test_KubernetesClusterHTTPApplicationRouting.py new file mode 100644 index 00000000000..5e5fbfe92e0 --- /dev/null +++ b/tests/terraform/checks/resource/azure/test_KubernetesClusterHTTPApplicationRouting.py @@ -0,0 +1,42 @@ +import unittest +from pathlib import Path + +from checkov.runner_filter import RunnerFilter +from checkov.terraform.checks.resource.azure.KubernetesClusterHTTPApplicationRouting import check +from checkov.terraform.runner import Runner + + +class TestKubernetesClusterHTTPApplicationRouting(unittest.TestCase): + def test(self): + # given + test_files_dir = Path(__file__).parent / "example_KubernetesClusterHTTPApplicationRouting" + + # when + report = Runner().run(root_folder=str(test_files_dir), runner_filter=RunnerFilter(checks=[check.id])) + + # then + summary = report.get_summary() + + passing_resources = { + "azurerm_kubernetes_cluster.pass_false", + "azurerm_kubernetes_cluster.pass_missing", + } + + failing_resources = { + "azurerm_kubernetes_cluster.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"], 2) + self.assertEqual(summary["failed"], 1) + 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/graph/checks/resources/AzureSpringCloudTLSDisabled/expected.yaml b/tests/terraform/graph/checks/resources/AzureSpringCloudTLSDisabled/expected.yaml new file mode 100644 index 00000000000..96ddc546357 --- /dev/null +++ b/tests/terraform/graph/checks/resources/AzureSpringCloudTLSDisabled/expected.yaml @@ -0,0 +1,7 @@ +pass: + - "azurerm_spring_cloud_service.pass" + - "azurerm_spring_cloud_service.pass_basic" + - "azurerm_spring_cloud_service.pass_notset" +fail: + - "azurerm_spring_cloud_service.fail_notset" + - "azurerm_spring_cloud_service.fail" \ No newline at end of file diff --git a/tests/terraform/graph/checks/resources/AzureSpringCloudTLSDisabled/main.tf b/tests/terraform/graph/checks/resources/AzureSpringCloudTLSDisabled/main.tf new file mode 100644 index 00000000000..325172799c6 --- /dev/null +++ b/tests/terraform/graph/checks/resources/AzureSpringCloudTLSDisabled/main.tf @@ -0,0 +1,95 @@ +# Fail: SKU is not Basic and tls is disabled +resource "azurerm_spring_cloud_service" "fail" { + name = "example-springcloud" + resource_group_name = azurerm_resource_group.fail.name + location = azurerm_resource_group.fail.location + sku_tier = "Standard" # Computed, so unknown if not set +} + +resource "azurerm_spring_cloud_app" "fail" { + name = "example-springcloudapp" + resource_group_name = azurerm_resource_group.fail.name + service_name = azurerm_spring_cloud_service.fail.name + tls_enabled = false # defaults to false + + identity { + type = "SystemAssigned" + } +} + +# Pass: SKU is Basic and tls is disabled +resource "azurerm_spring_cloud_service" "pass_basic" { + name = "example-springcloud" + resource_group_name = azurerm_resource_group.pass_basic.name + location = azurerm_resource_group.pass_basic.location + sku_tier = "Basic" # Computed, so unknown if not set +} + +resource "azurerm_spring_cloud_app" "pass_basic" { + name = "example-springcloudapp" + resource_group_name = azurerm_resource_group.pass_basic.name + service_name = azurerm_spring_cloud_service.pass_basic.name + tls_enabled = false # defaults to false + + identity { + type = "SystemAssigned" + } +} + +# Pass: SKU is not set and tls is disabled +resource "azurerm_spring_cloud_service" "pass_notset" { + name = "example-springcloud" + resource_group_name = azurerm_resource_group.pass_notset.name + location = azurerm_resource_group.pass_notset.location + sku_tier = "Basic" # Computed, so unknown if not set +} + +resource "azurerm_spring_cloud_app" "unknown_notset" { + name = "example-springcloudapp" + resource_group_name = azurerm_resource_group.pass_notset.name + service_name = azurerm_spring_cloud_service.pass_notset.name + tls_enabled = false # defaults to false + + identity { + type = "SystemAssigned" + } +} + +# Fail: SKU is not Basic and tls is not set +resource "azurerm_spring_cloud_service" "fail_notset" { + name = "example-springcloud" + resource_group_name = azurerm_resource_group.fail_notset.name + location = azurerm_resource_group.fail_notset.location + sku_tier = "Standard" # Computed, so unknown if not set +} + +resource "azurerm_spring_cloud_app" "fail_notset" { + name = "example-springcloudapp" + resource_group_name = azurerm_resource_group.fail_notset.name + service_name = azurerm_spring_cloud_service.fail_notset.name + # not setting tls_enabled defaults to false + + identity { + type = "SystemAssigned" + } +} + +# Pass: SKU is not Basic and tls is true +resource "azurerm_spring_cloud_service" "pass" { + name = "example-springcloud" + resource_group_name = azurerm_resource_group.pass.name + location = azurerm_resource_group.pass.location + sku_tier = "Standard" # Computed, so unknown if not set +} + +resource "azurerm_spring_cloud_app" "pass" { + name = "example-springcloudapp" + resource_group_name = azurerm_resource_group.pass.name + service_name = azurerm_spring_cloud_service.pass.name + tls_enabled = true # defaults to false + + identity { + type = "SystemAssigned" + } +} + diff --git a/tests/terraform/graph/checks/test_yaml_policies.py b/tests/terraform/graph/checks/test_yaml_policies.py index f9aee72e0fb..172dce2baac 100644 --- a/tests/terraform/graph/checks/test_yaml_policies.py +++ b/tests/terraform/graph/checks/test_yaml_policies.py @@ -544,6 +544,9 @@ def test_CloudfrontOriginNotHTTPSOnly(self): def test_SQSEncryptionCMK(self): self.go("SQSEncryptionCMK") + def test_AzureSpringCloudTLSDisabled(self): + self.go("AzureSpringCloudTLSDisabled") + def test_registry_load(self): registry = Registry(parser=GraphCheckParser(), checks_dir=str( Path(__file__).parent.parent.parent.parent.parent / "checkov" / "terraform" / "checks" / "graph_checks")) diff --git a/tests/terraform/image_referencer/test_runner_azure_resources.py b/tests/terraform/image_referencer/test_runner_azure_resources.py index 853096a2145..fcbadca73ed 100644 --- a/tests/terraform/image_referencer/test_runner_azure_resources.py +++ b/tests/terraform/image_referencer/test_runner_azure_resources.py @@ -87,7 +87,7 @@ def test_containers_resources(mocker: MockerFixture, graph_framework): sca_image_report = next(report for report in reports if report.check_type == CheckType.SCA_IMAGE) assert len(tf_report.resources) == 1 - assert len(tf_report.passed_checks) == 1 + assert len(tf_report.passed_checks) == 2 assert len(tf_report.failed_checks) == 2 assert len(tf_report.skipped_checks) == 0 assert len(tf_report.parsing_errors) == 0 diff --git a/tests/terraform/runner/test_plan_runner.py b/tests/terraform/runner/test_plan_runner.py index 292c8c94f2d..dd9b2899be9 100644 --- a/tests/terraform/runner/test_plan_runner.py +++ b/tests/terraform/runner/test_plan_runner.py @@ -276,7 +276,7 @@ def test_plan_runner_with_empty_vpc_connection(self): self.assertEqual(report.get_exit_code({'soft_fail': False, 'soft_fail_checks': [], 'soft_fail_threshold': None, 'hard_fail_checks': [], 'hard_fail_threshold': None}), 1) self.assertEqual(report.get_exit_code({'soft_fail': True, 'soft_fail_checks': [], 'soft_fail_threshold': None, 'hard_fail_checks': [], 'hard_fail_threshold': None}), 0) - self.assertEqual(report.get_summary()["failed"], 107) + self.assertEqual(report.get_summary()["failed"], 108) def test_runner_child_modules(self): current_dir = os.path.dirname(os.path.realpath(__file__)) diff --git a/tests/terraform/runner/test_runner.py b/tests/terraform/runner/test_runner.py index 7e2f79da42f..4ac82202749 100644 --- a/tests/terraform/runner/test_runner.py +++ b/tests/terraform/runner/test_runner.py @@ -434,6 +434,8 @@ def test_no_missing_ids(self): continue # duplicate of CKV_AZURE_3 if f"CKV_AZURE_{i}" == "CKV_AZURE_90": continue # duplicate of CKV_AZURE_53 + if f"CKV_AZURE_{i}" == "CKV_AZURE_243": + continue # ARM only check, not a Terraform check self.assertIn(f'CKV_AZURE_{i}', azure_checks, msg=f'The new Azure violation should have the ID "CKV_AZURE_{i}"')