mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-23 04:21:52 +00:00
Finding detail card
Finding detail card
This commit is contained in:
@@ -142,10 +142,10 @@ export const ColumnFindings: ColumnDef<FindingProps>[] = [
|
||||
return (
|
||||
<TriggerSheet
|
||||
triggerComponent={<PlusIcon />}
|
||||
title="Scan Details"
|
||||
description="View the scan details"
|
||||
title="Finding Details"
|
||||
description="View the finding details"
|
||||
>
|
||||
<DataTableRowDetails entityId={row.original.id} />
|
||||
<DataTableRowDetails finding={getFindingsData(row)} />
|
||||
</TriggerSheet>
|
||||
);
|
||||
},
|
||||
|
||||
@@ -2,36 +2,10 @@
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
import { getScan } from "@/actions/scans";
|
||||
import { ScanDetail, SkeletonTableScans } from "@/components/scans/table";
|
||||
import { ScanProps } from "@/types";
|
||||
import { FindingDetail } from "@/components/findings/table";
|
||||
import { FindingProps } from "@/types";
|
||||
|
||||
export const DataTableRowDetails = ({ entityId }: { entityId: string }) => {
|
||||
const [scanDetails, setScanDetails] = useState<ScanProps | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchScanDetails = async () => {
|
||||
try {
|
||||
const result = await getScan(entityId);
|
||||
setScanDetails(result?.data);
|
||||
} catch (error) {
|
||||
console.error("Error fetching scan details:", error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchScanDetails();
|
||||
}, [entityId]);
|
||||
|
||||
if (isLoading) {
|
||||
return <SkeletonTableScans />;
|
||||
}
|
||||
|
||||
if (!scanDetails) {
|
||||
return <div>No scan details available</div>;
|
||||
}
|
||||
|
||||
return <ScanDetail scanDetails={scanDetails} />;
|
||||
export const DataTableRowDetails = ({ finding }: { finding: FindingProps }) => {
|
||||
console.log(finding);
|
||||
return <FindingDetail findingDetails={finding} />;
|
||||
};
|
||||
|
||||
@@ -4,139 +4,94 @@ import { Card, CardBody, CardHeader, Divider } from "@nextui-org/react";
|
||||
|
||||
import { DateWithTime, SnippetId } from "@/components/ui/entities";
|
||||
import { StatusBadge } from "@/components/ui/table/status-badge";
|
||||
import { ScanProps } from "@/types";
|
||||
import { FindingProps } from "@/types";
|
||||
import {Table, TableHeader, TableColumn, TableBody, TableRow, TableCell} from "@nextui-org/react";
|
||||
|
||||
export const FindingDetail = ({ findingDetails }: { findingDetails: ScanProps }) => {
|
||||
const scanOnDemand = findingDetails.attributes;
|
||||
export const FindingDetail = ({ findingDetails }: { findingDetails: FindingProps }) => {
|
||||
const finding = findingDetails;
|
||||
console.log(finding)
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex flex-col items-baseline md:flex-row md:gap-x-4">
|
||||
<h2 className="text-lg font-black uppercase">Scan Details - </h2>
|
||||
<p>{scanOnDemand.name}</p>
|
||||
</div>
|
||||
|
||||
<StatusBadge size="lg" status={scanOnDemand.state} />
|
||||
</div>
|
||||
<Divider />
|
||||
<div className="relative z-0 flex w-full flex-col justify-between gap-4 overflow-auto rounded-large bg-content1 p-4 shadow-small">
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<div className="space-y-4">
|
||||
<DetailItem
|
||||
label="ID"
|
||||
value={<SnippetId label="Type" entityId={findingDetails.id} />}
|
||||
/>
|
||||
<DetailItem label="Trigger" value={scanOnDemand.trigger} />
|
||||
<DetailItem
|
||||
label="Resource Count"
|
||||
value={scanOnDemand.unique_resource_count.toString()}
|
||||
/>
|
||||
<DetailItem label="Progress" value={`${scanOnDemand.progress}%`} />
|
||||
<DetailItem
|
||||
label="Duration"
|
||||
value={`${scanOnDemand.duration} seconds`}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
<DateItem
|
||||
label="Started At"
|
||||
value={
|
||||
scanOnDemand.started_at ? (
|
||||
<DateWithTime dateTime={scanOnDemand.started_at.toString()} />
|
||||
) : (
|
||||
"Not Started"
|
||||
)
|
||||
}
|
||||
/>
|
||||
<DateItem
|
||||
label="Completed At"
|
||||
value={
|
||||
scanOnDemand.completed_at ? (
|
||||
<DateWithTime
|
||||
dateTime={scanOnDemand.completed_at.toString()}
|
||||
/>
|
||||
) : (
|
||||
"Not Started"
|
||||
)
|
||||
}
|
||||
/>
|
||||
<DateItem
|
||||
label="Scheduled At"
|
||||
value={
|
||||
scanOnDemand.scheduled_at ? (
|
||||
<DateWithTime
|
||||
dateTime={scanOnDemand.scheduled_at.toString()}
|
||||
/>
|
||||
) : (
|
||||
"Not Scheduled"
|
||||
)
|
||||
}
|
||||
/>
|
||||
<DetailItem
|
||||
label="Provider ID"
|
||||
value={
|
||||
<SnippetId
|
||||
label="Provider ID"
|
||||
entityId={findingDetails.relationships.provider.data.id}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<DetailItem
|
||||
label="Task ID"
|
||||
value={
|
||||
<SnippetId
|
||||
label="Task ID"
|
||||
entityId={findingDetails.relationships.task.data.id}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Card className="relative w-full border-small border-default-100 p-3 shadow-lg">
|
||||
<CardHeader className="py-2">
|
||||
<h2 className="text-2xl font-bold">Scan Arguments</h2>
|
||||
</CardHeader>
|
||||
|
||||
<Divider />
|
||||
|
||||
<CardBody className="p-4">
|
||||
<DetailItem
|
||||
label="Checks"
|
||||
value={
|
||||
(scanOnDemand.scanner_args as any)?.checks_to_execute?.join(
|
||||
", ",
|
||||
) || "N/A"
|
||||
}
|
||||
/>
|
||||
</CardBody>
|
||||
</Card>
|
||||
<Table aria-label="Example static collection table">
|
||||
<TableHeader>
|
||||
<TableColumn>Name </TableColumn>
|
||||
<TableColumn>Value</TableColumn>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
<TableRow key="1">
|
||||
<TableCell>Resource ID</TableCell>
|
||||
<TableCell>{finding.relationships.resource.id}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow key="2">
|
||||
<TableCell>Resource ARN</TableCell>
|
||||
<TableCell>{finding.relationships.resource.attributes.uid}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow key="3">
|
||||
<TableCell>Check ID</TableCell>
|
||||
<TableCell>{finding.attributes.check_id}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow key="4">
|
||||
<TableCell>Types</TableCell>
|
||||
<TableCell>{finding.attributes.check_metadata.checktype}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow key="5">
|
||||
<TableCell>Scan time</TableCell>
|
||||
<TableCell>{finding.attributes.inserted_at}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow key="6">
|
||||
<TableCell>Prowler Finding ID</TableCell>
|
||||
<TableCell>{finding.relationships.resource.attributes.uid}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow key="7">
|
||||
<TableCell>Severity</TableCell>
|
||||
<TableCell>{finding.id}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow key="8">
|
||||
<TableCell>Status</TableCell>
|
||||
<TableCell>{finding.attributes.status}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow key="9">
|
||||
<TableCell>Region</TableCell>
|
||||
<TableCell>{finding.relationships.resource.attributes.region}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow key="10">
|
||||
<TableCell>Service</TableCell>
|
||||
<TableCell>{finding.relationships.resource.attributes.service}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow key="11">
|
||||
<TableCell>Account</TableCell>
|
||||
<TableCell>{finding.relationships.provider.attributes.uid}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow key="12">
|
||||
<TableCell>Details</TableCell>
|
||||
<TableCell>{finding.attributes.status_extended}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow key="13">
|
||||
<TableCell>Risk</TableCell>
|
||||
<TableCell>{finding.attributes.check_metadata.risk}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow key="14">
|
||||
<TableCell>Recommendation</TableCell>
|
||||
<TableCell>{finding.attributes.check_metadata.remediation.recommendation.text}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow key="15">
|
||||
<TableCell>CLI</TableCell>
|
||||
<TableCell>{finding.attributes.check_metadata.remediation.code.cli}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow key="16">
|
||||
<TableCell>Other</TableCell>
|
||||
<TableCell>{finding.attributes.check_metadata.remediation.code.other}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow key="17">
|
||||
<TableCell>Terraform</TableCell>
|
||||
<TableCell>{finding.attributes.check_metadata.remediation.code.terraform}</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const DateItem = ({
|
||||
label,
|
||||
value,
|
||||
}: {
|
||||
label: string;
|
||||
value: React.ReactNode;
|
||||
}) => (
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="font-semibold text-default-500">{label}:</span>
|
||||
<span className="text-default-700">{value}</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
const DetailItem = ({
|
||||
label,
|
||||
value,
|
||||
}: {
|
||||
label: string;
|
||||
value: React.ReactNode;
|
||||
}) => (
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="font-semibold text-default-500">{label}:</span>
|
||||
<span className="text-default-700">{value}</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -2,3 +2,4 @@ export * from "./column-findings";
|
||||
export * from "./data-table-row-actions";
|
||||
export * from "./data-table-row-details";
|
||||
export * from "./skeleton-table-findings";
|
||||
export * from "./finding-detail";
|
||||
|
||||
@@ -247,6 +247,7 @@ export interface FindingProps {
|
||||
type: "Resource";
|
||||
id: string;
|
||||
}[];
|
||||
id: string;
|
||||
attributes: {
|
||||
uid: string;
|
||||
name: string;
|
||||
|
||||
Reference in New Issue
Block a user