Files
prowler/ui/lib/findings-filters.ts
T

128 lines
4.1 KiB
TypeScript

/**
* Shared filter constants and helpers for findings-shaped endpoints.
*
* Pairs with `lib/findings-sort.ts` (sort tokens). This module covers the
* filter side of the same query language.
*/
// ---------------------------------------------------------------------------
// Filter values
// ---------------------------------------------------------------------------
/**
* The "FAIL" status value as it crosses the wire to the API. Used in both
* `filter[status]` (single) and `filter[status__in]` (CSV) form.
*
* NOTE: this is a bare value, not a full enum. The broader Status/Delta
* enum migration is intentionally out of scope here — see PR follow-up.
*/
export const FAIL_FILTER_VALUE = "FAIL";
/**
* The "new" delta value. Used in `filter[delta]` and `filter[delta__in]`.
*/
export const NEW_DELTA_FILTER_VALUE = "new";
/**
* Values accepted by `filter[muted]`.
*
* - `EXCLUDE` ("false"): the API hides muted findings (default UI behaviour).
* - `INCLUDE` ("include"): a sentinel that the API treats as "show all
* regardless of muted state". This is NOT the literal string "true" — the
* server route ignores invalid values which conveniently bypasses the
* filter.
*/
export const MUTED_FILTER = {
EXCLUDE: "false",
INCLUDE: "include",
} as const;
export type MutedFilterValue = (typeof MUTED_FILTER)[keyof typeof MUTED_FILTER];
// ---------------------------------------------------------------------------
// URL helpers
// ---------------------------------------------------------------------------
/**
* Drill-down preset: "FAIL findings, hide muted". Mutates `params` in place.
*
* Repeated 6+ times across overview widgets that link to /findings
* (attack-surface card, sankey, severity-over-time, risk-radar, risk-plot,
* etc). Centralising avoids drift if product later adds, say, `delta=new`
* to all drill-downs.
*/
export function applyFailNonMutedFilters(params: URLSearchParams): void {
params.set("filter[status__in]", FAIL_FILTER_VALUE);
params.set("filter[muted]", MUTED_FILTER.EXCLUDE);
}
/**
* Returns a new filter object with the default findings behaviour applied:
* hide muted findings unless the caller already set `filter[muted]`.
*
* Used by both the grouped findings SSR path and the resource drill-down so
* they stay aligned with the checkbox default on `/findings`.
*/
export function applyDefaultMutedFilter<
T extends Record<string, string | string[] | undefined>,
>(filters: T): T {
return {
"filter[muted]": MUTED_FILTER.EXCLUDE,
...filters,
};
}
// ---------------------------------------------------------------------------
// Filter parsing
// ---------------------------------------------------------------------------
/**
* Splits a JSON:API CSV filter value into clean string tokens.
*
* Accepts both string and string[] inputs because Next.js `searchParams`
* surface either form depending on whether the key appears once or multiple
* times in the URL. Returns trimmed, non-empty tokens in input order.
*
* Previously duplicated in three call sites
* (actions/finding-groups, components/findings/table/inline-resource-container,
* implicitly inside lib/findings-groups). Single source now.
*/
export function splitCsvFilterValues(
value: string | string[] | undefined,
): string[] {
if (Array.isArray(value)) {
return value
.flatMap((item) => item.split(","))
.map((item) => item.trim())
.filter(Boolean);
}
if (typeof value === "string") {
return value
.split(",")
.map((item) => item.trim())
.filter(Boolean);
}
return [];
}
/**
* True when the caller has opted into seeing muted findings via either the
* `filter[muted]=include` shorthand or a multi-value variant.
*
* Previously duplicated in actions/finding-groups and
* components/findings/table/inline-resource-container.
*/
export function includesMutedFindings(
filters: Record<string, string | string[] | undefined>,
): boolean {
const mutedFilter = filters["filter[muted]"];
if (Array.isArray(mutedFilter)) {
return mutedFilter.includes(MUTED_FILTER.INCLUDE);
}
return mutedFilter === MUTED_FILTER.INCLUDE;
}