fix(iac): include resource line range in finding UID to prevent duplicates (#10241)

Co-authored-by: Pepe Fagoaga <pepe@prowler.com>
This commit is contained in:
Andoni Alonso
2026-03-03 17:40:36 +01:00
committed by GitHub
parent b61b6cba53
commit e8d2b4a189
3 changed files with 42 additions and 1 deletions

View File

@@ -453,6 +453,9 @@ class Finding(BaseModel):
f"{output_data['region']}-{output_data['resource_name']}"
)
if provider.type == "iac" and output_data.get("resource_line_range"):
output_data["uid"] += f"-{output_data['resource_line_range']}"
if not output_data["resource_uid"]:
logger.error(
f"Check {check_output.check_metadata.CheckID} has no resource_uid."

View File

@@ -302,7 +302,12 @@ class Scan:
for report in iac_reports:
# Generate unique UID for the finding
finding_uid = f"{report.check_metadata.CheckID}-{report.resource_name}-{report.resource_line_range}"
finding_uid = (
f"prowler-iac-{report.check_metadata.CheckID}-iac-"
f"{report.region}-{report.resource_name}"
)
if report.resource_line_range:
finding_uid += f"-{report.resource_line_range}"
# Convert status string to Status enum
status_enum = (

View File

@@ -723,6 +723,10 @@ class TestFinding:
assert finding_output.resource_name == "aws_s3_bucket.example"
assert finding_output.resource_uid == "aws_s3_bucket.example"
assert finding_output.region == "main" # Branch name, not line range
assert (
finding_output.uid
== "prowler-iac-service_check_id-iac-main-aws_s3_bucket.example-1:5"
)
assert finding_output.status == Status.PASS
assert finding_output.status_extended == "mock_status_extended"
assert finding_output.muted is False
@@ -737,6 +741,35 @@ class TestFinding:
assert finding_output.metadata.SubServiceName == ""
assert finding_output.metadata.ResourceIdTemplate == ""
def test_generate_output_iac_empty_line_range(self):
provider = MagicMock()
provider.type = "iac"
provider.provider_uid = None
provider.scan_repository_url = "https://github.com/user/repo"
provider.auth_method = "No auth"
check_output = MagicMock()
check_output.file_path = "/path/to/iac/main.tf"
check_output.resource_name = "main.tf"
check_output.resource_path = "/path/to/iac/main.tf"
check_output.resource_line_range = ""
check_output.region = "main"
check_output.resource = {"resource": "main.tf", "value": {}}
check_output.resource_details = ""
check_output.status = Status.PASS
check_output.status_extended = "No issues found"
check_output.muted = False
check_output.check_metadata = mock_check_metadata(provider="iac")
check_output.compliance = {}
output_options = MagicMock()
output_options.unix_timestamp = False
finding_output = Finding.generate_output(provider, check_output, output_options)
assert isinstance(finding_output, Finding)
assert finding_output.uid == "prowler-iac-service_check_id-iac-main-main.tf"
def assert_keys_lowercase(self, d):
for k, v in d.items():
assert k.islower()