Skip to content

Commit 322a500

Browse files
prowler-botalejandrobailojfagoagas
authored
fix(ui): centralize default muted findings filter on finding groups (#10819)
Co-authored-by: Alejandro Bailo <59607668+alejandrobailo@users.noreply.github.com> Co-authored-by: Pepe Fagoaga <pepe@prowler.com>
1 parent ea09ff8 commit 322a500

8 files changed

Lines changed: 111 additions & 9 deletions

File tree

ui/CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22

33
All notable changes to the **Prowler UI** are documented in this file.
44

5+
## [1.24.2] (Prowler v5.24.2)
6+
7+
### 🐞 Fixed
8+
9+
- Default muted filter now applied consistently on the findings page and the finding-group resource drill-down, keeping muted findings hidden unless the "include muted findings" checkbox is opted in [(#10818)](https://github.com/prowler-cloud/prowler/pull/10818)
10+
11+
---
12+
513
## [1.24.1] (Prowler v5.24.1)
614

715
### 🐞 Fixed

ui/app/(prowler)/findings/page.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,8 @@ describe("findings page", () => {
3636
it("guards errors array access with a length check", () => {
3737
expect(source).toContain("errors?.length > 0");
3838
});
39+
40+
it("applies the shared default muted filter so muted findings are hidden unless the caller opts in", () => {
41+
expect(source).toContain("applyDefaultMutedFilter");
42+
});
3943
});

ui/app/(prowler)/findings/page.tsx

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
import { ContentLayout } from "@/components/ui";
1717
import { FilterTransitionWrapper } from "@/contexts";
1818
import {
19+
applyDefaultMutedFilter,
1920
createScanDetailsMapping,
2021
extractFiltersAndQuery,
2122
extractSortAndKey,
@@ -39,14 +40,16 @@ export default async function Findings({
3940
getScans({ pageSize: 50 }),
4041
]);
4142

42-
const filtersWithScanDates = await resolveFindingScanDateFilters({
43-
filters,
44-
scans: scansData?.data || [],
45-
loadScan: async (scanId: string) => {
46-
const response = await getScan(scanId);
47-
return response?.data;
48-
},
49-
});
43+
const filtersWithScanDates = applyDefaultMutedFilter(
44+
await resolveFindingScanDateFilters({
45+
filters,
46+
scans: scansData?.data || [],
47+
loadScan: async (scanId: string) => {
48+
const response = await getScan(scanId);
49+
return response?.data;
50+
},
51+
}),
52+
);
5053

5154
const hasHistoricalData = hasDateOrScanFilter(filtersWithScanDates);
5255

ui/hooks/use-finding-group-resource-state.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,9 @@ describe("useFindingGroupResourceState", () => {
1212
it("enables muted findings only for the finding-group resource drawer", () => {
1313
expect(source).toContain("includeMutedInOtherFindings: true");
1414
});
15+
16+
it("applies the shared default muted filter before fetching group resources", () => {
17+
expect(source).toContain("applyDefaultMutedFilter(filters)");
18+
expect(source).toContain("filters: effectiveFilters");
19+
});
1520
});

ui/hooks/use-finding-group-resource-state.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { useState } from "react";
66
import { canMuteFindingResource } from "@/components/findings/table/finding-resource-selection";
77
import { useResourceDetailDrawer } from "@/components/findings/table/resource-detail-drawer";
88
import { useFindingGroupResources } from "@/hooks/use-finding-group-resources";
9+
import { applyDefaultMutedFilter } from "@/lib";
910
import { FindingGroupRow, FindingResourceRow } from "@/types";
1011

1112
interface UseFindingGroupResourceStateOptions {
@@ -67,11 +68,13 @@ export function useFindingGroupResourceState({
6768
setIsLoading(loading);
6869
};
6970

71+
const effectiveFilters = applyDefaultMutedFilter(filters);
72+
7073
const { sentinelRef, refresh, loadMore, totalCount } =
7174
useFindingGroupResources({
7275
checkId: group.checkId,
7376
hasDateOrScanFilter: hasHistoricalData,
74-
filters,
77+
filters: effectiveFilters,
7578
onSetResources: handleSetResources,
7679
onAppendResources: handleAppendResources,
7780
onSetLoading: handleSetLoading,

ui/lib/findings-filters.test.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { describe, expect, it } from "vitest";
2+
3+
import { applyDefaultMutedFilter, MUTED_FILTER } from "./findings-filters";
4+
5+
describe("applyDefaultMutedFilter", () => {
6+
it("injects filter[muted]=false when the caller has not set it", () => {
7+
const input: Record<string, string> = { "filter[status__in]": "FAIL" };
8+
const result = applyDefaultMutedFilter(input);
9+
10+
expect(result["filter[muted]"]).toBe(MUTED_FILTER.EXCLUDE);
11+
expect(result["filter[status__in]"]).toBe("FAIL");
12+
});
13+
14+
it("preserves an explicit filter[muted]=include opt-in from the checkbox", () => {
15+
const result = applyDefaultMutedFilter({
16+
"filter[muted]": MUTED_FILTER.INCLUDE,
17+
});
18+
19+
expect(result["filter[muted]"]).toBe(MUTED_FILTER.INCLUDE);
20+
});
21+
22+
it("preserves an explicit filter[muted]=false (no silent overwrite)", () => {
23+
const result = applyDefaultMutedFilter({
24+
"filter[muted]": MUTED_FILTER.EXCLUDE,
25+
});
26+
27+
expect(result["filter[muted]"]).toBe(MUTED_FILTER.EXCLUDE);
28+
});
29+
30+
it("does not mutate the input object", () => {
31+
const input = { "filter[status__in]": "FAIL" };
32+
applyDefaultMutedFilter(input);
33+
34+
expect(input).not.toHaveProperty("filter[muted]");
35+
});
36+
37+
it("returns a default-filled object when called with no caller filters", () => {
38+
const result = applyDefaultMutedFilter({} as Record<string, string>);
39+
40+
expect(result["filter[muted]"]).toBe(MUTED_FILTER.EXCLUDE);
41+
});
42+
});

ui/lib/findings-filters.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/**
2+
* Shared helpers for findings filter handling.
3+
*
4+
* The `/findings` SSR page and the finding-group resource drill-down both
5+
* need to hide muted findings by default — unless the user has opted in via
6+
* the "include muted findings" checkbox. Keeping that default in one place
7+
* prevents surfaces from drifting.
8+
*/
9+
10+
export const MUTED_FILTER = {
11+
/** Wire value sent to the API to exclude muted findings. */
12+
EXCLUDE: "false",
13+
/**
14+
* Sentinel value that tells the API to return both muted and non-muted
15+
* findings. The checkbox writes this to the URL when the user opts in.
16+
*/
17+
INCLUDE: "include",
18+
} as const;
19+
20+
export type MutedFilterValue = (typeof MUTED_FILTER)[keyof typeof MUTED_FILTER];
21+
22+
/**
23+
* Returns a new filter object with the default muted behaviour applied:
24+
* hide muted findings unless the caller already set `filter[muted]`.
25+
*
26+
* The default is spread BEFORE the caller filters so any explicit value
27+
* (including `"false"` or the `"include"` opt-in) wins.
28+
*/
29+
export function applyDefaultMutedFilter<
30+
T extends Record<string, string | string[] | undefined>,
31+
>(filters: T): T {
32+
return {
33+
"filter[muted]": MUTED_FILTER.EXCLUDE,
34+
...filters,
35+
};
36+
}

ui/lib/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export * from "./error-mappings";
22
export * from "./external-urls";
3+
export * from "./findings-filters";
34
export * from "./helper";
45
export * from "./helper-filters";
56
export * from "./menu-list";

0 commit comments

Comments
 (0)