diff --git a/actions/findings/findings.ts b/actions/findings/findings.ts
index 86f0bf5605..9fc89b0907 100644
--- a/actions/findings/findings.ts
+++ b/actions/findings/findings.ts
@@ -14,10 +14,11 @@ export const getFindings = async ({
}) => {
const session = await auth();
- if (isNaN(Number(page)) || page < 1) redirect("/findings");
+ if (isNaN(Number(page)) || page < 1)
+ redirect("findings?include=resources.provider,scan");
const keyServer = process.env.API_BASE_URL;
- const url = new URL(`${keyServer}/findings`);
+ const url = new URL(`${keyServer}/findings?include=resources.provider,scan`);
if (page) url.searchParams.append("page[number]", page.toString());
if (query) url.searchParams.append("filter[search]", query);
@@ -39,7 +40,6 @@ export const getFindings = async ({
});
const data = await findings.json();
const parsedData = parseStringify(data);
- console.log(parsedData.data);
revalidatePath("/findings");
return parsedData;
} catch (error) {
diff --git a/app/(prowler)/findings/page.tsx b/app/(prowler)/findings/page.tsx
index 3558db664b..0e7dd6c9b6 100644
--- a/app/(prowler)/findings/page.tsx
+++ b/app/(prowler)/findings/page.tsx
@@ -10,7 +10,7 @@ import {
} from "@/components/findings/table";
import { Header } from "@/components/ui";
import { DataTable } from "@/components/ui/table";
-import { SearchParamsProps } from "@/types/components";
+import { FindingProps, SearchParamsProps } from "@/types/components";
export default async function Findings({
searchParams,
@@ -49,10 +49,60 @@ const SSRDataTable = async ({
const findingsData = await getFindings({ query, page, sort, filters });
+ // Create dictionaries from the included data
+ const resourceDict = Object.fromEntries(
+ findingsData.included
+ .filter((item: { type: string }) => item.type === "Resource")
+ .map((resource: { id: string }) => [resource.id, resource]),
+ );
+
+ const scanDict = Object.fromEntries(
+ findingsData.included
+ .filter((item: { type: string }) => item.type === "Scan")
+ .map((scan: { id: string }) => [scan.id, scan]),
+ );
+
+ const providerDict = Object.fromEntries(
+ findingsData.included
+ .filter((item: { type: string }) => item.type === "Provider")
+ .map((provider: { id: string }) => [provider.id, provider]),
+ );
+
+ // Enrich each finding with its corresponding resource, scan, and provider
+ const enrichedFindings = findingsData.data.map((finding: FindingProps) => {
+ const scanId = finding.relationships?.scan?.data?.id;
+ // eslint-disable-next-line security/detect-object-injection
+ const scan = scanId ? scanDict[scanId] : undefined;
+
+ const resourceId = finding.relationships?.resources?.data?.[0]?.id;
+ console.log(resourceId, "resourceId");
+ const resource =
+ resourceId && resourceDict ? resourceDict[resourceId] : undefined;
+
+ const providerId = resource?.relationships?.provider?.data?.id;
+ // eslint-disable-next-line security/detect-object-injection
+ const provider = providerId ? providerDict[providerId] : undefined;
+
+ return {
+ ...finding,
+ relationships: {
+ scan: scan,
+ resource: resource,
+ provider: provider,
+ },
+ };
+ });
+
+ // Create the new object while maintaining the original structure
+ const enrichedResponse = {
+ ...findingsData,
+ data: enrichedFindings,
+ };
+
return (
diff --git a/components/findings/table/column-findings.tsx b/components/findings/table/column-findings.tsx
index d8d02ae9c2..98803d2a7c 100644
--- a/components/findings/table/column-findings.tsx
+++ b/components/findings/table/column-findings.tsx
@@ -3,13 +3,17 @@
import { ColumnDef } from "@tanstack/react-table";
import { DateWithTime } from "@/components/ui/entities";
-import { DataTableColumnHeader, StatusBadge } from "@/components/ui/table";
+import {
+ DataTableColumnHeader,
+ SeverityBadge,
+ StatusBadge,
+} from "@/components/ui/table";
import { FindingProps } from "@/types";
import { DataTableRowActions } from "./data-table-row-actions";
const getFindingsData = (row: { original: FindingProps }) => {
- console.log(row.original);
+ // console.log(row.original);
return row.original;
};
@@ -17,6 +21,39 @@ const getFindingsMetadata = (row: { original: FindingProps }) => {
return row.original.attributes.check_metadata;
};
+const getResourceData = (
+ row: { original: FindingProps },
+ field: keyof FindingProps["relationships"]["resource"]["attributes"],
+) => {
+ return (
+ // eslint-disable-next-line security/detect-object-injection
+ row.original.relationships?.resource?.attributes?.[field] ||
+ `No ${field} found in resource`
+ );
+};
+
+const getProviderData = (
+ row: { original: FindingProps },
+ field: keyof FindingProps["relationships"]["provider"]["attributes"],
+) => {
+ return (
+ // eslint-disable-next-line security/detect-object-injection
+ row.original.relationships?.provider?.attributes?.[field] ||
+ `No ${field} found in provider`
+ );
+};
+
+const getScanData = (
+ row: { original: FindingProps },
+ field: keyof FindingProps["relationships"]["scan"]["attributes"],
+) => {
+ return (
+ // eslint-disable-next-line security/detect-object-injection
+ row.original.relationships?.scan?.attributes?.[field] ||
+ `No ${field} found in scan`
+ );
+};
+
export const ColumnFindings: ColumnDef[] = [
// {
// header: " ",
@@ -32,6 +69,20 @@ export const ColumnFindings: ColumnDef[] = [
return {checktitle}
;
},
},
+ {
+ accessorKey: "region",
+ header: "Region",
+ cell: ({ row }) => {
+ const region = getResourceData(row, "region");
+
+ return (
+ <>
+ {region}
+ >
+ );
+ },
+ },
+
// {
// accessorKey: "uid",
// header: ({ column }) => (
@@ -45,27 +96,40 @@ export const ColumnFindings: ColumnDef[] = [
// },
// },
// {
- // accessorKey: "severity",
- // header: ({ column }) => (
- //
- // ),
+ // accessorKey: "provider",
+ // header: "Provider Alias",
// cell: ({ row }) => {
// const {
- // attributes: { severity },
- // } = getFindingsData(row);
- // return ;
+ // attributes: { alias },
+ // } = getProviderData(row, "alias");
+ // return {alias}
;
// },
// },
+ {
+ accessorKey: "severity",
+ header: ({ column }) => (
+
+ ),
+ cell: ({ row }) => {
+ const {
+ attributes: { severity },
+ } = getFindingsData(row);
+ return ;
+ },
+ },
{
accessorKey: "status",
header: "Scan Status",
- cell: () => {
+ cell: ({ row }) => {
+ const {
+ attributes: { status },
+ } = getFindingsData(row);
// Temporarily overwriting the value until the API is functional.
- return ;
+ return ;
},
},
{
diff --git a/components/findings/table/data-table-row-actions.tsx b/components/findings/table/data-table-row-actions.tsx
index dd59e24126..dea023d359 100644
--- a/components/findings/table/data-table-row-actions.tsx
+++ b/components/findings/table/data-table-row-actions.tsx
@@ -23,15 +23,15 @@ import { VerticalDotsIcon } from "@/components/icons";
// import { EditForm } from "../forms";
// import { DeleteForm } from "../forms/delete-form";
-interface DataTableRowActionsProps {
- row: Row;
+interface DataTableRowActionsProps {
+ row: Row;
}
const iconClasses =
"text-2xl text-default-500 pointer-events-none flex-shrink-0";
-export function DataTableRowActions({
+export function DataTableRowActions({
row,
-}: DataTableRowActionsProps) {
+}: DataTableRowActionsProps) {
// const [isEditOpen, setIsEditOpen] = useState(false);
// const [isDeleteOpen, setIsDeleteOpen] = useState(false);
// const providerId = (row.original as { id: string }).id;
@@ -80,7 +80,7 @@ export function DataTableRowActions({
startContent={}
// onClick={() => setIsEditOpen(true)}
>
- Edit Provider
+ Edit Finding
@@ -97,7 +97,7 @@ export function DataTableRowActions({
}
// onClick={() => setIsDeleteOpen(true)}
>
- Delete Provider
+ Delete Finding
diff --git a/components/ui/table/severity-badge.tsx b/components/ui/table/severity-badge.tsx
index 9539e56bb3..8b852eea36 100644
--- a/components/ui/table/severity-badge.tsx
+++ b/components/ui/table/severity-badge.tsx
@@ -4,7 +4,7 @@ import React from "react";
import { AlertIcon } from "@/components/icons";
-type Severity = "critical" | "high" | "medium" | "low";
+type Severity = "informational" | "low" | "medium" | "high" | "critical";
const severityIconMap = {
critical: ,
diff --git a/types/components.ts b/types/components.ts
index d2fbfe5014..89dee77381 100644
--- a/types/components.ts
+++ b/types/components.ts
@@ -108,7 +108,7 @@ export interface ScanProps {
}
export interface FindingProps {
- type: "Findings";
+ type: "Finding";
id: string;
attributes: {
uid: string;
@@ -160,13 +160,40 @@ export interface FindingProps {
id: string;
};
};
- resources: {
+ resource: {
data: {
type: "Resource";
id: string;
- }[];
- meta: {
- count: number;
+ };
+ attributes: {
+ uid: string;
+ name: string;
+ region: string;
+ service: string;
+ tags: Record;
+ type: string;
+ inserted_at: string;
+ updated_at: string;
+ };
+ relationships: {
+ provider: {
+ data: {
+ type: "Provider";
+ id: string;
+ };
+ };
+ findings: {
+ meta: {
+ count: number;
+ };
+ data: {
+ type: "Finding";
+ id: string;
+ }[];
+ };
+ };
+ links: {
+ self: string;
};
};
provider: {
@@ -174,6 +201,28 @@ export interface FindingProps {
type: "Provider";
id: string;
};
+ attributes: {
+ provider: string;
+ uid: string;
+ alias: string;
+ connection: {
+ connected: boolean;
+ last_checked_at: string;
+ };
+ inserted_at: string;
+ updated_at: string;
+ };
+ relationships: {
+ secret: {
+ data: {
+ type: "ProviderSecret";
+ id: string;
+ };
+ };
+ };
+ links: {
+ self: string;
+ };
};
};
links: {