From dfca97633e1b0cae13e5877460178b1c11624695 Mon Sep 17 00:00:00 2001 From: Andoni Alonso <14891798+andoniaf@users.noreply.github.com> Date: Tue, 3 Mar 2026 13:35:58 +0100 Subject: [PATCH] feat(sdk): add provider_uid to OCSF unmapped output (#10231) Co-authored-by: Pepe Fagoaga --- prowler/CHANGELOG.md | 1 + prowler/lib/outputs/finding.py | 2 ++ prowler/lib/outputs/ocsf/ocsf.py | 1 + tests/lib/outputs/finding_test.py | 1 + tests/lib/outputs/fixtures/fixtures.py | 2 ++ tests/lib/outputs/ocsf/ocsf_test.py | 5 +++++ 6 files changed, 12 insertions(+) diff --git a/prowler/CHANGELOG.md b/prowler/CHANGELOG.md index 739b9f09b2..1afef8275a 100644 --- a/prowler/CHANGELOG.md +++ b/prowler/CHANGELOG.md @@ -40,6 +40,7 @@ All notable changes to the **Prowler SDK** are documented in this file. - OpenStack compute service with 7 security checks [(#9944)](https://github.com/prowler-cloud/prowler/pull/9944) - OpenStack image service with 6 security checks [(#10096)](https://github.com/prowler-cloud/prowler/pull/10096) - IaC `--provider-uid` flag to specify the provider UID for the IaC provider [(#10233)](https://github.com/prowler-cloud/prowler/pull/10233) +- `provider_uid` field in OCSF `unmapped` output for provider identification [(#10231)](https://github.com/prowler-cloud/prowler/pull/10231) ### 🔄 Changed diff --git a/prowler/lib/outputs/finding.py b/prowler/lib/outputs/finding.py index 79bc47d502..70188742d8 100644 --- a/prowler/lib/outputs/finding.py +++ b/prowler/lib/outputs/finding.py @@ -34,6 +34,7 @@ class Finding(BaseModel): auth_method: str timestamp: Union[int, datetime] account_uid: str + provider_uid: Optional[str] = None account_name: Optional[str] = None account_email: Optional[str] = None account_organization_uid: Optional[str] = None @@ -244,6 +245,7 @@ class Finding(BaseModel): output_data["account_uid"] = get_nested_attribute( provider, "identity.cluster" ) + output_data["provider_uid"] = provider.identity.context output_data["region"] = f"namespace: {check_output.namespace}" elif provider.type == "github": diff --git a/prowler/lib/outputs/ocsf/ocsf.py b/prowler/lib/outputs/ocsf/ocsf.py index 48b44c6b00..f5681f7e89 100644 --- a/prowler/lib/outputs/ocsf/ocsf.py +++ b/prowler/lib/outputs/ocsf/ocsf.py @@ -178,6 +178,7 @@ class OCSF(Output): "notes": finding.metadata.Notes, "compliance": finding.compliance, "scan_id": str(scan_id), + "provider_uid": finding.provider_uid or finding.account_uid, }, ) if finding.provider != "kubernetes": diff --git a/tests/lib/outputs/finding_test.py b/tests/lib/outputs/finding_test.py index 566824c82f..0da8be6a9a 100644 --- a/tests/lib/outputs/finding_test.py +++ b/tests/lib/outputs/finding_test.py @@ -514,6 +514,7 @@ class TestFinding: assert finding_output.resource_tags == {} assert finding_output.partition is None assert finding_output.account_uid == "test_cluster" + assert finding_output.provider_uid == "In-Cluster" assert finding_output.account_name == "context: In-Cluster" assert finding_output.account_email is None assert finding_output.account_organization_uid is None diff --git a/tests/lib/outputs/fixtures/fixtures.py b/tests/lib/outputs/fixtures/fixtures.py index 3f6dfc2815..1ad20f33db 100644 --- a/tests/lib/outputs/fixtures/fixtures.py +++ b/tests/lib/outputs/fixtures/fixtures.py @@ -44,11 +44,13 @@ def generate_finding_output( check_id: str = "service_test_check_id", check_title: str = "service_test_check_id", check_type: list[str] = ["test-type"], + provider_uid: str = None, ) -> Finding: return Finding( auth_method="profile: default", timestamp=timestamp if timestamp else datetime.now(), account_uid=account_uid, + provider_uid=provider_uid, account_name=account_name, account_email="", account_organization_uid="test-organization-id", diff --git a/tests/lib/outputs/ocsf/ocsf_test.py b/tests/lib/outputs/ocsf/ocsf_test.py index 3199443b13..7942fca361 100644 --- a/tests/lib/outputs/ocsf/ocsf_test.py +++ b/tests/lib/outputs/ocsf/ocsf_test.py @@ -113,6 +113,7 @@ class TestOCSF: "additional_urls": findings[0].metadata.AdditionalURLs, "notes": findings[0].metadata.Notes, "compliance": findings[0].compliance, + "provider_uid": findings[0].account_uid, } # Test with int timestamp (UNIX timestamp) @@ -219,6 +220,7 @@ class TestOCSF: ], "notes": "test-notes", "compliance": {"test-compliance": "test-compliance"}, + "provider_uid": "123456789012", }, "activity_name": "Create", "activity_id": 1, @@ -354,6 +356,7 @@ class TestOCSF: "additional_urls": finding_output.metadata.AdditionalURLs, "notes": finding_output.metadata.Notes, "compliance": finding_output.compliance, + "provider_uid": finding_output.account_uid, } # ResourceDetails @@ -424,6 +427,7 @@ class TestOCSF: muted=True, region=AWS_REGION_EU_WEST_1, provider="kubernetes", + provider_uid="test-k8s-context", ) finding_ocsf = OCSF([finding_output]) @@ -433,6 +437,7 @@ class TestOCSF: assert finding_ocsf.resources[0].namespace == finding_output.region.replace( "namespace: ", "" ) + assert finding_ocsf.unmapped["provider_uid"] == "test-k8s-context" def test_finding_output_cloud_fail_low_not_muted(self): finding_output = generate_finding_output(