perf(api): resolve compliance-overviews attributes provider via O(1) index

This commit is contained in:
Josema Camacho committed 2026-06-10 14:11:57 +02:00
1 parent ec0bb53839
commit f41fb6a186
4 files changed
+53 -9

No files matched your search

+1
View File
@@ -19,6 +19,7 @@ All notable changes to the **Prowler API** are documented in this file.
- Workers now shut down gracefully on deploy or restart, finishing or re-queueing in-flight tasks instead of being force-killed and leaving them stuck [(#11416)](https://github.com/prowler-cloud/prowler/pull/11416)
- Resource `name` is now stored and refreshed on every scan, so resources no longer keep an empty name [(#11476)](https://github.com/prowler-cloud/prowler/pull/11476)
- `GET /api/v1/compliance-overviews/attributes` resolves the owning provider via a prebuilt `compliance_id → provider` index instead of scanning every provider on each request [(#11525)](https://github.com/prowler-cloud/prowler/pull/11525)
### 🔐 Security
+24
View File
@@ -7,6 +7,7 @@ from prowler.lib.check.compliance_models import (
from prowler.lib.check.models import CheckMetadata
AVAILABLE_COMPLIANCE_FRAMEWORKS = {}
COMPLIANCE_FRAMEWORK_PROVIDER = {}
class LazyComplianceTemplate(Mapping):
@@ -117,6 +118,29 @@ def get_compliance_frameworks(provider_type: Provider.ProviderChoices) -> list[s
return AVAILABLE_COMPLIANCE_FRAMEWORKS[provider_type]
def get_provider_for_compliance(compliance_id: str) -> str | None:
"""Resolve which provider owns `compliance_id`.
Built once, lazily, from the per-provider framework lists in
ProviderChoices order so the first provider that supports a universal
framework (e.g. ``dora``, ``csa_ccm_4.0``) wins, matching the previous
per-request linear scan.
Args:
compliance_id (str): Framework identifier (e.g., "cis_1.4_aws", "dora").
Returns:
str | None: The owning provider type, or None if no framework matches.
"""
global COMPLIANCE_FRAMEWORK_PROVIDER
if not COMPLIANCE_FRAMEWORK_PROVIDER:
for provider_type in Provider.ProviderChoices.values:
for framework_id in get_compliance_frameworks(provider_type):
COMPLIANCE_FRAMEWORK_PROVIDER.setdefault(framework_id, provider_type)
return COMPLIANCE_FRAMEWORK_PROVIDER.get(compliance_id)
def get_prowler_provider_checks(provider_type: Provider.ProviderChoices):
"""
Retrieve all check IDs for the specified provider type.
+26
View File
@@ -9570,6 +9570,32 @@ class TestComplianceOverviewViewSet:
assert "Category" in first_attr
assert "AWSService" in first_attr
def test_compliance_overview_attributes_universal_framework(
self, authenticated_client
):
# Universal frameworks (no per-provider file) must still resolve through
# the reverse index, which maps them to the first supporting provider.
response = authenticated_client.get(
reverse("complianceoverview-attributes"),
{"filter[compliance_id]": "dora"},
)
assert response.status_code == status.HTTP_200_OK
data = response.json()["data"]
assert len(data) > 0
for item in data:
assert "id" in item
assert "metadata" in item["attributes"]["attributes"]
assert "check_ids" in item["attributes"]["attributes"]
def test_compliance_overview_attributes_unknown_compliance_id(
self, authenticated_client
):
response = authenticated_client.get(
reverse("complianceoverview-attributes"),
{"filter[compliance_id]": "does_not_exist"},
)
assert response.status_code == status.HTTP_404_NOT_FOUND
def test_compliance_overview_attributes_missing_compliance_id(
self, authenticated_client
):
+2 -9
View File
@@ -116,6 +116,7 @@ from api.base_views import BaseRLSViewSet, BaseTenantViewset, BaseUserViewset
from api.compliance import (
PROWLER_COMPLIANCE_OVERVIEW_TEMPLATE,
get_compliance_frameworks,
get_provider_for_compliance,
get_prowler_provider_compliance,
)
from api.constants import SEVERITY_ORDER
@@ -5072,15 +5073,7 @@ class ComplianceOverviewViewSet(BaseRLSViewSet, TaskManagementMixin):
]
)
provider_type = None
# If we couldn't determine from database, try each provider type
if not provider_type:
for pt in Provider.ProviderChoices.values:
if compliance_id in get_compliance_frameworks(pt):
provider_type = pt
break
provider_type = get_provider_for_compliance(compliance_id)
if not provider_type:
raise NotFound(detail=f"Compliance framework '{compliance_id}' not found.")