From f47310bcebbfee6dc3cafffe4c752fb44abdb7a4 Mon Sep 17 00:00:00 2001
From: Alejandro Bailo <59607668+alejandrobailo@users.noreply.github.com>
Date: Fri, 16 Jan 2026 13:58:36 +0100
Subject: [PATCH] feat(ui): add resource groups filter to findings view (#9812)
---
ui/actions/findings/findings.ts | 14 +-
ui/app/(prowler)/findings/page.tsx | 4 +-
ui/components/findings/findings-filters.tsx | 11 +-
ui/components/icons/Icons.tsx | 184 +++++++++++++++++---
ui/lib/categories.ts | 13 ++
ui/types/filters.ts | 1 +
6 files changed, 198 insertions(+), 29 deletions(-)
diff --git a/ui/actions/findings/findings.ts b/ui/actions/findings/findings.ts
index 249742ce2e..1fade543aa 100644
--- a/ui/actions/findings/findings.ts
+++ b/ui/actions/findings/findings.ts
@@ -92,7 +92,12 @@ export const getMetadataInfo = async ({
Object.entries(filters).forEach(([key, value]) => {
// Define filters to exclude
- const excludedFilters = ["region__in", "service__in", "resource_type__in"];
+ const excludedFilters = [
+ "region__in",
+ "service__in",
+ "resource_type__in",
+ "resource_groups__in",
+ ];
if (
key !== "filter[search]" &&
!excludedFilters.some((filter) => key.includes(filter))
@@ -127,7 +132,12 @@ export const getLatestMetadataInfo = async ({
Object.entries(filters).forEach(([key, value]) => {
// Define filters to exclude
- const excludedFilters = ["region__in", "service__in", "resource_type__in"];
+ const excludedFilters = [
+ "region__in",
+ "service__in",
+ "resource_type__in",
+ "resource_groups__in",
+ ];
if (
key !== "filter[search]" &&
!excludedFilters.some((filter) => key.includes(filter))
diff --git a/ui/app/(prowler)/findings/page.tsx b/ui/app/(prowler)/findings/page.tsx
index 5ba469b745..c5b38019f3 100644
--- a/ui/app/(prowler)/findings/page.tsx
+++ b/ui/app/(prowler)/findings/page.tsx
@@ -117,12 +117,13 @@ export default async function Findings({
})()
: null;
- // Extract unique regions, services, categories from the new endpoint
+ // Extract unique regions, services, categories, groups from the new endpoint
const uniqueRegions = metadataInfoData?.data?.attributes?.regions || [];
const uniqueServices = metadataInfoData?.data?.attributes?.services || [];
const uniqueResourceTypes =
metadataInfoData?.data?.attributes?.resource_types || [];
const uniqueCategories = metadataInfoData?.data?.attributes?.categories || [];
+ const uniqueGroups = metadataInfoData?.data?.attributes?.groups || [];
// Extract provider IDs and details using helper functions
const providerIds = providersData ? extractProviderIds(providersData) : [];
@@ -159,6 +160,7 @@ export default async function Findings({
uniqueServices={uniqueServices}
uniqueResourceTypes={uniqueResourceTypes}
uniqueCategories={uniqueCategories}
+ uniqueGroups={uniqueGroups}
/>
}>
diff --git a/ui/components/findings/findings-filters.tsx b/ui/components/findings/findings-filters.tsx
index 571a976086..857a49f958 100644
--- a/ui/components/findings/findings-filters.tsx
+++ b/ui/components/findings/findings-filters.tsx
@@ -13,7 +13,7 @@ import { Button } from "@/components/shadcn";
import { ExpandableSection } from "@/components/ui/expandable-section";
import { DataTableFilterCustom } from "@/components/ui/table";
import { useRelatedFilters } from "@/hooks";
-import { getCategoryLabel } from "@/lib/categories";
+import { getCategoryLabel, getGroupLabel } from "@/lib/categories";
import { FilterEntity, FilterType, ScanEntity, ScanProps } from "@/types";
import { ProviderProps } from "@/types/providers";
@@ -29,6 +29,7 @@ interface FindingsFiltersProps {
uniqueServices: string[];
uniqueResourceTypes: string[];
uniqueCategories: string[];
+ uniqueGroups: string[];
}
export const FindingsFilters = ({
@@ -41,6 +42,7 @@ export const FindingsFilters = ({
uniqueServices,
uniqueResourceTypes,
uniqueCategories,
+ uniqueGroups,
}: FindingsFiltersProps) => {
const [isExpanded, setIsExpanded] = useState(false);
@@ -80,6 +82,13 @@ export const FindingsFilters = ({
labelFormatter: getCategoryLabel,
index: 5,
},
+ {
+ key: FilterType.RESOURCE_GROUPS,
+ labelCheckboxGroup: "Resource Group",
+ values: uniqueGroups,
+ labelFormatter: getGroupLabel,
+ index: 6,
+ },
{
key: FilterType.SCAN,
labelCheckboxGroup: "Scan ID",
diff --git a/ui/components/icons/Icons.tsx b/ui/components/icons/Icons.tsx
index ba353879ac..eb746e8e83 100644
--- a/ui/components/icons/Icons.tsx
+++ b/ui/components/icons/Icons.tsx
@@ -1119,7 +1119,7 @@ export const KubernetesIcon: React.FC = ({
};
export const LighthouseIcon: React.FC = ({
- size = 24,
+ size = 19,
width,
height,
...props
@@ -1127,39 +1127,173 @@ export const LighthouseIcon: React.FC = ({
return (
);
};
diff --git a/ui/lib/categories.ts b/ui/lib/categories.ts
index 8063b3b0ee..5de56059e7 100644
--- a/ui/lib/categories.ts
+++ b/ui/lib/categories.ts
@@ -51,6 +51,19 @@ export function getCategoryLabel(id: string): string {
return formatLabel(id, "-");
}
+/**
+ * Converts a resource group ID to a human-readable label.
+ * Convenience wrapper for formatLabel with "_" delimiter.
+ *
+ * Examples:
+ * - "ai_ml" -> "AI ML"
+ * - "api_gateway" -> "API Gateway"
+ * - "iam" -> "IAM"
+ */
+export function getGroupLabel(id: string): string {
+ return formatLabel(id, "_");
+}
+
export function formatWord(word: string): string {
const lowerWord = word.toLowerCase();
diff --git a/ui/types/filters.ts b/ui/types/filters.ts
index 126ef1f82e..ee58e6edcf 100644
--- a/ui/types/filters.ts
+++ b/ui/types/filters.ts
@@ -35,4 +35,5 @@ export enum FilterType {
STATUS = "status__in",
DELTA = "delta__in",
CATEGORY = "category__in",
+ RESOURCE_GROUPS = "resource_groups__in",
}