diff --git a/checkov/common/checks_infra/checks_parser.py b/checkov/common/checks_infra/checks_parser.py index 978804dfb9..d70ba19602 100644 --- a/checkov/common/checks_infra/checks_parser.py +++ b/checkov/common/checks_infra/checks_parser.py @@ -202,6 +202,9 @@ def validate_check_config(self, file_path: str, raw_check: dict[str, dict[str, A def parse_raw_check(self, raw_check: Dict[str, Dict[str, Any]], **kwargs: Any) -> BaseGraphCheck: providers = self._get_check_providers(raw_check) policy_definition = raw_check.get("definition", {}) + if isinstance(policy_definition, list): + # a list of conditions is treated as an implicit AND + policy_definition = {"and": policy_definition} check = self._parse_raw_check(policy_definition, kwargs.get("resources_types"), providers) check.id = raw_check.get("metadata", {}).get("id", "") check.name = raw_check.get("metadata", {}).get("name", "") diff --git a/tests/common/checks_infra/examples/valid_check_list_definition.yaml b/tests/common/checks_infra/examples/valid_check_list_definition.yaml new file mode 100644 index 0000000000..9c6b44da22 --- /dev/null +++ b/tests/common/checks_infra/examples/valid_check_list_definition.yaml @@ -0,0 +1,16 @@ +metadata: + name: "Ensure S3 bucket has versioning and encryption" + id: CUSTOM_LIST_DEF + category: "GENERAL_SECURITY" +definition: + - cond_type: attribute + resource_types: + - aws_s3_bucket + attribute: versioning.enabled + operator: equals + value: "true" + - cond_type: attribute + resource_types: + - aws_s3_bucket + attribute: server_side_encryption_configuration + operator: exists diff --git a/tests/common/checks_infra/test_checks_parser.py b/tests/common/checks_infra/test_checks_parser.py index 44a8215be7..98e043f88d 100644 --- a/tests/common/checks_infra/test_checks_parser.py +++ b/tests/common/checks_infra/test_checks_parser.py @@ -5,6 +5,7 @@ from checkov.common.checks_infra.checks_parser import GraphCheckParser from checkov.common.checks_infra.resources_types import resources_types as raw_resources_types +from checkov.common.graph.checks_infra.enums import SolverType EXAMPLES_DIR = Path(__file__).parent / "examples" @@ -91,3 +92,49 @@ def test_parse_taggable_resource_list(): providers = ["azure"] check = parser._parse_raw_check(raw_check, [], providers) assert check.resource_types == raw_resources_types.get("azure_taggable") + + +def test_validate_check_config_list_definition(caplog: LogCaptureFixture): + """A definition that is a list should pass validation.""" + # given + file_path = EXAMPLES_DIR / "valid_check_list_definition.yaml" + check_yaml = yaml.safe_load(file_path.read_text()) + + # when + valid = GraphCheckParser().validate_check_config(file_path=str(file_path), raw_check=check_yaml) + + # then + assert valid + assert len(caplog.messages) == 0 + + +def test_parse_raw_check_list_definition(): + """A list-type definition should be treated as an implicit AND of its elements.""" + parser = GraphCheckParser() + raw_check = { + "metadata": {"id": "TEST_LIST", "name": "Test List Def", "category": "GENERAL_SECURITY"}, + "definition": [ + { + "cond_type": "attribute", + "resource_types": ["aws_s3_bucket"], + "attribute": "versioning.enabled", + "operator": "equals", + "value": "true", + }, + { + "cond_type": "attribute", + "resource_types": ["aws_s3_bucket"], + "attribute": "server_side_encryption_configuration", + "operator": "exists", + }, + ], + } + + check = parser.parse_raw_check(raw_check) + + assert check.id == "TEST_LIST" + assert check.type == SolverType.COMPLEX + assert check.operator == "and" + assert len(check.sub_checks) == 2 + assert check.sub_checks[0].attribute == "versioning.enabled" + assert check.sub_checks[1].attribute == "server_side_encryption_configuration"