From e5b9fee942259c462f0bd654dea41b73db03ea80 Mon Sep 17 00:00:00 2001 From: StylusFrost Date: Thu, 7 May 2026 09:32:48 +0200 Subject: [PATCH] fix(sdk): dedupe entry-point compliance frameworks against built-ins The entry-point loop in get_available_compliance_frameworks appended framework names without checking whether the same name was already provided by a built-in. The loader (Compliance.get_bulk) already respects "built-in wins" via an explicit guard, but the listing function did not, causing --list-compliance and --list-compliance-requirements to print the same name twice on collision with a built-in. Adds the same dedup guard already used by the universal frameworks loop a few lines above, restoring symmetry across the three compliance discovery layers. Includes a regression test that registers a fake entry point with a name colliding with a known built-in (cis_2.0_aws) and asserts the framework appears exactly once in the resulting list. --- prowler/config/config.py | 6 +++--- tests/config/config_test.py | 26 ++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/prowler/config/config.py b/prowler/config/config.py index af1f52e1ae..d651211801 100644 --- a/prowler/config/config.py +++ b/prowler/config/config.py @@ -150,9 +150,9 @@ def get_available_compliance_frameworks(provider=None): if os.path.isdir(path): for file in os.scandir(path): if file.is_file() and file.name.endswith(".json"): - available_compliance_frameworks.append( - file.name.removesuffix(".json") - ) + name = file.name.removesuffix(".json") + if name not in available_compliance_frameworks: + available_compliance_frameworks.append(name) return available_compliance_frameworks diff --git a/tests/config/config_test.py b/tests/config/config_test.py index c038c9930b..0fe00d792f 100644 --- a/tests/config/config_test.py +++ b/tests/config/config_test.py @@ -463,6 +463,32 @@ class Test_Config: all_frameworks = get_available_compliance_frameworks() assert "csa_ccm_4.0" in all_frameworks + @mock.patch("prowler.config.config._get_ep_compliance_dirs") + def test_get_available_compliance_frameworks_dedupes_ep_collisions_with_builtins( + self, mock_dirs + ): + """Entry-point compliance frameworks that collide with a built-in + name must appear only once in the available frameworks list. + Built-in wins silently — same policy as the universal frameworks + loop and as Compliance.get_bulk.""" + import json + import tempfile + + with tempfile.TemporaryDirectory() as tmpdir: + # cis_2.0_aws ships as a built-in under prowler/compliance/aws/ + json_path = os.path.join(tmpdir, "cis_2.0_aws.json") + with open(json_path, "w") as f: + json.dump({"Framework": "CIS", "Provider": "aws"}, f) + + mock_dirs.return_value = {"aws": tmpdir} + + frameworks = get_available_compliance_frameworks("aws") + + assert frameworks.count("cis_2.0_aws") == 1, ( + f"Expected cis_2.0_aws to appear exactly once, got " + f"{frameworks.count('cis_2.0_aws')} occurrences in: {frameworks}" + ) + def test_load_and_validate_config_file_aws(self): path = pathlib.Path(os.path.dirname(os.path.realpath(__file__))) config_test_file = f"{path}/fixtures/config.yaml"