mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-24 13:01:56 +00:00
chore: WIP
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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 (
|
||||
<DataTable
|
||||
columns={ColumnFindings}
|
||||
data={findingsData?.data || []}
|
||||
data={enrichedResponse?.data || []}
|
||||
metadata={findingsData?.meta}
|
||||
customFilters={filterFindings}
|
||||
/>
|
||||
|
||||
@@ -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<FindingProps>[] = [
|
||||
// {
|
||||
// header: " ",
|
||||
@@ -32,6 +69,20 @@ export const ColumnFindings: ColumnDef<FindingProps>[] = [
|
||||
return <p className="max-w-96 truncate text-medium">{checktitle}</p>;
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "region",
|
||||
header: "Region",
|
||||
cell: ({ row }) => {
|
||||
const region = getResourceData(row, "region");
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>{region}</div>
|
||||
</>
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
// {
|
||||
// accessorKey: "uid",
|
||||
// header: ({ column }) => (
|
||||
@@ -45,27 +96,40 @@ export const ColumnFindings: ColumnDef<FindingProps>[] = [
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// accessorKey: "severity",
|
||||
// header: ({ column }) => (
|
||||
// <DataTableColumnHeader
|
||||
// column={column}
|
||||
// title={"Severity"}
|
||||
// param="severity"
|
||||
// />
|
||||
// ),
|
||||
// accessorKey: "provider",
|
||||
// header: "Provider Alias",
|
||||
// cell: ({ row }) => {
|
||||
// const {
|
||||
// attributes: { severity },
|
||||
// } = getFindingsData(row);
|
||||
// return <StatusBadge status={severity} />;
|
||||
// attributes: { alias },
|
||||
// } = getProviderData(row, "alias");
|
||||
// return <p className="max-w-96 truncate text-medium">{alias}</p>;
|
||||
// },
|
||||
// },
|
||||
{
|
||||
accessorKey: "severity",
|
||||
header: ({ column }) => (
|
||||
<DataTableColumnHeader
|
||||
column={column}
|
||||
title={"Severity"}
|
||||
param="severity"
|
||||
/>
|
||||
),
|
||||
cell: ({ row }) => {
|
||||
const {
|
||||
attributes: { severity },
|
||||
} = getFindingsData(row);
|
||||
return <SeverityBadge severity={severity} />;
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "status",
|
||||
header: "Scan Status",
|
||||
cell: () => {
|
||||
cell: ({ row }) => {
|
||||
const {
|
||||
attributes: { status },
|
||||
} = getFindingsData(row);
|
||||
// Temporarily overwriting the value until the API is functional.
|
||||
return <StatusBadge status={"completed"} />;
|
||||
return <StatusBadge status={status} />;
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -23,15 +23,15 @@ import { VerticalDotsIcon } from "@/components/icons";
|
||||
// import { EditForm } from "../forms";
|
||||
// import { DeleteForm } from "../forms/delete-form";
|
||||
|
||||
interface DataTableRowActionsProps<FindingsProps> {
|
||||
row: Row<FindingsProps>;
|
||||
interface DataTableRowActionsProps<FindingProps> {
|
||||
row: Row<FindingProps>;
|
||||
}
|
||||
const iconClasses =
|
||||
"text-2xl text-default-500 pointer-events-none flex-shrink-0";
|
||||
|
||||
export function DataTableRowActions<FindingsProps>({
|
||||
export function DataTableRowActions<FindingProps>({
|
||||
row,
|
||||
}: DataTableRowActionsProps<FindingsProps>) {
|
||||
}: DataTableRowActionsProps<FindingProps>) {
|
||||
// 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<FindingsProps>({
|
||||
startContent={<EditDocumentBulkIcon className={iconClasses} />}
|
||||
// onClick={() => setIsEditOpen(true)}
|
||||
>
|
||||
Edit Provider
|
||||
Edit Finding
|
||||
</DropdownItem>
|
||||
</DropdownSection>
|
||||
<DropdownSection title="Danger zone">
|
||||
@@ -97,7 +97,7 @@ export function DataTableRowActions<FindingsProps>({
|
||||
}
|
||||
// onClick={() => setIsDeleteOpen(true)}
|
||||
>
|
||||
Delete Provider
|
||||
Delete Finding
|
||||
</DropdownItem>
|
||||
</DropdownSection>
|
||||
</DropdownMenu>
|
||||
|
||||
@@ -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: <AlertIcon size={14} className="mr-1" />,
|
||||
|
||||
+54
-5
@@ -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<string, string>;
|
||||
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: {
|
||||
|
||||
Reference in New Issue
Block a user