mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-05-06 08:47:18 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b82d7318ce | |||
| fdff780624 |
@@ -11,6 +11,7 @@ All notable changes to the **Prowler API** are documented in this file.
|
||||
- `VALKEY_SCHEME`, `VALKEY_USERNAME`, and `VALKEY_PASSWORD` environment variables to configure Celery broker TLS/auth connection details for Valkey/ElastiCache [(#10420)](https://github.com/prowler-cloud/prowler/pull/10420)
|
||||
- `Vercel` provider support [(#10190)](https://github.com/prowler-cloud/prowler/pull/10190)
|
||||
- Finding groups list and latest endpoints support `sort=delta`, ordering by `new_count` then `changed_count` so groups with the most new findings rank highest [(#10606)](https://github.com/prowler-cloud/prowler/pull/10606)
|
||||
- Finding groups list and latest endpoints support `sort=status`, ordering by aggregated status with the FAIL > PASS > MUTED priority [(#10628)](https://github.com/prowler-cloud/prowler/pull/10628)
|
||||
|
||||
### 🔄 Changed
|
||||
|
||||
|
||||
@@ -16872,6 +16872,68 @@ class TestFindingGroupViewSet:
|
||||
asc_keys = [delta_key(item) for item in response.json()["data"]]
|
||||
assert asc_keys == sorted(asc_keys)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"endpoint_name", ["finding-group-list", "finding-group-latest"]
|
||||
)
|
||||
def test_finding_groups_sort_by_status(
|
||||
self,
|
||||
authenticated_client,
|
||||
finding_groups_fixture,
|
||||
endpoint_name,
|
||||
):
|
||||
"""Sort by status orders by aggregated status (FAIL > PASS > MUTED)."""
|
||||
status_order = {"FAIL": 3, "PASS": 2, "MUTED": 1}
|
||||
|
||||
# Descending: FAIL groups first, then PASS
|
||||
params = {"sort": "-status"}
|
||||
if endpoint_name == "finding-group-list":
|
||||
params["filter[inserted_at]"] = TODAY
|
||||
|
||||
response = authenticated_client.get(reverse(endpoint_name), params)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
data = response.json()["data"]
|
||||
assert len(data) > 0
|
||||
|
||||
desc_statuses = [item["attributes"]["status"] for item in data]
|
||||
desc_keys = [status_order[s] for s in desc_statuses]
|
||||
assert desc_keys == sorted(desc_keys, reverse=True)
|
||||
|
||||
# Ascending: PASS groups first, then FAIL
|
||||
params["sort"] = "status"
|
||||
response = authenticated_client.get(reverse(endpoint_name), params)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
asc_statuses = [
|
||||
item["attributes"]["status"] for item in response.json()["data"]
|
||||
]
|
||||
asc_keys = [status_order[s] for s in asc_statuses]
|
||||
assert asc_keys == sorted(asc_keys)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"endpoint_name", ["finding-group-list", "finding-group-latest"]
|
||||
)
|
||||
def test_finding_groups_sort_by_status_includes_muted(
|
||||
self,
|
||||
authenticated_client,
|
||||
finding_groups_fixture,
|
||||
endpoint_name,
|
||||
):
|
||||
"""When include_muted is set, MUTED groups participate in status sort."""
|
||||
status_order = {"FAIL": 3, "PASS": 2, "MUTED": 1}
|
||||
|
||||
params = {"sort": "status", "filter[include_muted]": "true"}
|
||||
if endpoint_name == "finding-group-list":
|
||||
params["filter[inserted_at]"] = TODAY
|
||||
|
||||
response = authenticated_client.get(reverse(endpoint_name), params)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
data = response.json()["data"]
|
||||
|
||||
statuses = [item["attributes"]["status"] for item in data]
|
||||
assert "MUTED" in statuses
|
||||
assert statuses[0] == "MUTED"
|
||||
keys = [status_order[s] for s in statuses]
|
||||
assert keys == sorted(keys)
|
||||
|
||||
def test_finding_groups_latest_ignores_date_filters(
|
||||
self, authenticated_client, finding_groups_fixture
|
||||
):
|
||||
|
||||
@@ -7219,6 +7219,7 @@ class FindingGroupViewSet(BaseRLSViewSet):
|
||||
"check_id": "check_id",
|
||||
"check_title": "check_title",
|
||||
"severity": "severity_order",
|
||||
"status": "status_order",
|
||||
"delta": "delta_order",
|
||||
"fail_count": "fail_count",
|
||||
"pass_count": "pass_count",
|
||||
@@ -7570,6 +7571,19 @@ class FindingGroupViewSet(BaseRLSViewSet):
|
||||
sort_param, self._FINDING_GROUP_SORT_MAP
|
||||
)
|
||||
if ordering:
|
||||
# status_order is annotated on demand so groups can be sorted
|
||||
# by their aggregated status (FAIL > PASS > MUTED), mirroring
|
||||
# the priority used in _post_process_aggregation.
|
||||
if any(field.lstrip("-") == "status_order" for field in ordering):
|
||||
aggregated_queryset = aggregated_queryset.annotate(
|
||||
status_order=Case(
|
||||
When(fail_count__gt=0, then=Value(3)),
|
||||
When(pass_count__gt=0, then=Value(2)),
|
||||
default=Value(1),
|
||||
output_field=IntegerField(),
|
||||
)
|
||||
)
|
||||
|
||||
# delta_order is a virtual sort field: expand it to a
|
||||
# lexicographic ordering by (new_count, changed_count) so groups
|
||||
# with more new findings rank higher, with changed_count as the
|
||||
|
||||
Reference in New Issue
Block a user