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: {