mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-10-19 01:44:16 +00:00
perf(api): resolve compliance-overviews attributes provider via O(1) index
This commit is contained in:
4 files changed
+53
-9
No files matched your search
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
):
|
||||
|
||||
@@ -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.")
|
||||
|
||||
|
||||
Reference in new issue
Block a user