diff --git a/actions/compliance.ts b/actions/compliance.ts new file mode 100644 index 0000000000..76064ebb33 --- /dev/null +++ b/actions/compliance.ts @@ -0,0 +1,39 @@ +"use server"; + +import { revalidatePath } from "next/cache"; +import { redirect } from "next/navigation"; + +import { parseStringify } from "@/lib"; + +export const getCompliance = async ({ page = 1 }) => { + if (isNaN(Number(page)) || page < 1) redirect("/compliance"); + const keyServer = process.env.LOCAL_SITE_URL; + + try { + const compliance = await fetch( + `${keyServer}/api/compliance?page%5Bnumber%5D=${page}`, + ); + const data = await compliance.json(); + const parsedData = parseStringify(data); + revalidatePath("/compliance"); + return parsedData; + } catch (error) { + console.error("Error fetching Compliance:", error); + return undefined; + } +}; + +export const getErrorMessage = (error: unknown): string => { + let message: string; + + if (error instanceof Error) { + message = error.message; + } else if (error && typeof error === "object" && "message" in error) { + message = String(error.message); + } else if (typeof error === "string") { + message = error; + } else { + message = "Wops! Something when wrong."; + } + return message; +}; diff --git a/app/(prowler)/compliance/page.tsx b/app/(prowler)/compliance/page.tsx index 59b39ea737..e610187e06 100644 --- a/app/(prowler)/compliance/page.tsx +++ b/app/(prowler)/compliance/page.tsx @@ -1,13 +1,53 @@ -import React from "react"; +import { Spacer } from "@nextui-org/react"; +import { redirect } from "next/navigation"; +import { Suspense } from "react"; +import { getCompliance } from "@/actions/compliance"; +import { + ComplianceCard, + ComplianceSkeletonGrid, +} from "@/components/compliance"; +import { FilterControls } from "@/components/filters"; import { Header } from "@/components/ui"; +import { searchParamsProps } from "@/types"; -export default function Compliance() { +export default async function Compliance({ searchParams }: searchParamsProps) { return ( <>
- -

Hi hi from Compliance page

+ + + + }> + + ); } + +const SSRComplianceGrid = async ({ searchParams }: searchParamsProps) => { + const page = searchParams.page ? parseInt(searchParams.page) : 1; + const compliancesData = await getCompliance({ page }); + const [compliances] = await Promise.all([compliancesData]); + + if (compliances?.errors) redirect("/compliance"); + + return ( +
+ {compliances.compliance?.data.map((compliance: any) => ( + + ))} +
+ ); +}; diff --git a/app/api/compliance/route.ts b/app/api/compliance/route.ts new file mode 100644 index 0000000000..b2e4a01d9f --- /dev/null +++ b/app/api/compliance/route.ts @@ -0,0 +1,10 @@ +import { NextResponse } from "next/server"; + +import data from "../../../dataCompliance.json"; + +export async function GET() { + // Simulate fetching data with a delay + await new Promise((resolve) => setTimeout(resolve, 2000)); + + return NextResponse.json({ compliance: data }); +} diff --git a/components/compliance/ComplianceCard.tsx b/components/compliance/ComplianceCard.tsx new file mode 100644 index 0000000000..32e8004377 --- /dev/null +++ b/components/compliance/ComplianceCard.tsx @@ -0,0 +1,85 @@ +import { Card, CardBody, Progress } from "@nextui-org/react"; +import Image from "next/image"; +import React from "react"; + +import { getComplianceIcon } from "../icons"; + +interface ComplianceCardProps { + title: string; + passingRequirements: number; + totalRequirements: number; + prevPassingRequirements: number; + prevTotalRequirements: number; +} + +export const ComplianceCard: React.FC = ({ + title, + passingRequirements, + totalRequirements, + prevPassingRequirements, + prevTotalRequirements, +}) => { + const ratingPercentage = Math.floor( + (passingRequirements / totalRequirements) * 100, + ); + + const prevRatingPercentage = Math.floor( + (prevPassingRequirements / prevTotalRequirements) * 100, + ); + + const getScanChange = () => { + const scanDifference = ratingPercentage - prevRatingPercentage; + if (scanDifference < 0 && scanDifference <= -1) { + return `${scanDifference}% from last scan`; + } + if (scanDifference > 0 && scanDifference >= 1) { + return `+${scanDifference}% from last scan`; + } + return "No change from last scan"; + }; + + const getRatingColor = (ratingPercentage: number) => { + if (ratingPercentage <= 10) { + return "danger"; + } + if (ratingPercentage <= 40) { + return "warning"; + } + return "success"; + }; + + return ( + + +
+ {`${title} +
+

{title}

+ +
+ + + {passingRequirements} / {totalRequirements} + + Passing Requirements + + {getScanChange()} +
+
+
+
+
+ ); +}; diff --git a/components/compliance/ComplianceSkeletonGrid.tsx b/components/compliance/ComplianceSkeletonGrid.tsx new file mode 100644 index 0000000000..c16d3edca9 --- /dev/null +++ b/components/compliance/ComplianceSkeletonGrid.tsx @@ -0,0 +1,18 @@ +import { Card, Skeleton } from "@nextui-org/react"; +import React from "react"; + +export const ComplianceSkeletonGrid = () => { + return ( + +
+ {[...Array(28)].map((_, index) => ( +
+ +
+
+
+ ))} +
+
+ ); +}; diff --git a/components/compliance/index.ts b/components/compliance/index.ts new file mode 100644 index 0000000000..6f025f2ca5 --- /dev/null +++ b/components/compliance/index.ts @@ -0,0 +1,2 @@ +export * from "./ComplianceCard"; +export * from "./ComplianceSkeletonGrid"; diff --git a/components/filters/CustomCheckboxMutedFindings.tsx b/components/filters/CustomCheckboxMutedFindings.tsx index 0ebfcb7755..62715403fe 100644 --- a/components/filters/CustomCheckboxMutedFindings.tsx +++ b/components/filters/CustomCheckboxMutedFindings.tsx @@ -1,10 +1,20 @@ import { Checkbox } from "@nextui-org/react"; import React from "react"; -export const CustomCheckboxMutedFindings = () => { +interface CustomCheckboxMutedFindingsProps { + mutedFindings?: boolean; +} + +export const CustomCheckboxMutedFindings: React.FC< + CustomCheckboxMutedFindingsProps +> = ({ mutedFindings }) => { return ( - - Include Muted Findings - + <> + {mutedFindings && ( + + Include Muted Findings + + )} + ); }; diff --git a/components/filters/FilterControls.tsx b/components/filters/FilterControls.tsx index 5ec7fe418a..e087d3c108 100644 --- a/components/filters/FilterControls.tsx +++ b/components/filters/FilterControls.tsx @@ -1,15 +1,23 @@ +import React from "react"; + import { CustomAccountSelection } from "./CustomAccountSelection"; import { CustomCheckboxMutedFindings } from "./CustomCheckboxMutedFindings"; import { CustomDatePicker } from "./CustomDatePicker"; import { CustomSelectProvider } from "./CustomSelectProvider"; -export const FilterControls = () => { +interface FilterControlsProps { + mutedFindings?: boolean; +} + +export const FilterControls: React.FC = ({ + mutedFindings = true, +}) => { return (
- +
); }; diff --git a/components/icons/compliance/IconCompliance.tsx b/components/icons/compliance/IconCompliance.tsx new file mode 100644 index 0000000000..543f6c68c8 --- /dev/null +++ b/components/icons/compliance/IconCompliance.tsx @@ -0,0 +1,63 @@ +import AWSLogo from "./aws.svg"; +import CISLogo from "./cis.svg"; +import CISALogo from "./cisa.svg"; +import ENSLogo from "./ens.png"; +import FedRAMPLogo from "./fedramp.svg"; +import FFIECLogo from "./ffiec.svg"; +import GDPRLogo from "./gdpr.svg"; +import GxPLogo from "./gxp-aws.svg"; +import HIPAALogo from "./hipaa.svg"; +import ISOLogo from "./iso-27001.svg"; +import MITRELogo from "./mitre-attack.svg"; +import NISTLogo from "./nist.svg"; +import PCILogo from "./pci-dss.svg"; +import RBILogo from "./rbi.svg"; +import SOC2Logo from "./soc2.svg"; + +export const getComplianceIcon = (complianceTitle: string) => { + if (complianceTitle.toLowerCase().includes("aws")) { + return AWSLogo; + } + if (complianceTitle.toLowerCase().includes("cisa")) { + return CISALogo; + } + if (complianceTitle.toLowerCase().includes("cis")) { + return CISLogo; + } + if (complianceTitle.toLowerCase().includes("ens")) { + return ENSLogo; + } + if (complianceTitle.toLowerCase().includes("ffiec")) { + return FFIECLogo; + } + if (complianceTitle.toLowerCase().includes("fedramp")) { + return FedRAMPLogo; + } + if (complianceTitle.toLowerCase().includes("gdpr")) { + return GDPRLogo; + } + if (complianceTitle.toLowerCase().includes("gxp")) { + return GxPLogo; + } + if (complianceTitle.toLowerCase().includes("hipaa")) { + return HIPAALogo; + } + if (complianceTitle.toLowerCase().includes("iso")) { + return ISOLogo; + } + if (complianceTitle.toLowerCase().includes("mitre")) { + return MITRELogo; + } + if (complianceTitle.toLowerCase().includes("nist")) { + return NISTLogo; + } + if (complianceTitle.toLowerCase().includes("pci")) { + return PCILogo; + } + if (complianceTitle.toLowerCase().includes("rbi")) { + return RBILogo; + } + if (complianceTitle.toLowerCase().includes("soc2")) { + return SOC2Logo; + } +}; diff --git a/components/icons/compliance/aws.svg b/components/icons/compliance/aws.svg new file mode 100644 index 0000000000..4715937ff0 --- /dev/null +++ b/components/icons/compliance/aws.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + diff --git a/components/icons/compliance/cis.svg b/components/icons/compliance/cis.svg new file mode 100644 index 0000000000..013f35a75e --- /dev/null +++ b/components/icons/compliance/cis.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/components/icons/compliance/cisa.svg b/components/icons/compliance/cisa.svg new file mode 100644 index 0000000000..c26f681d51 --- /dev/null +++ b/components/icons/compliance/cisa.svg @@ -0,0 +1,2 @@ + + diff --git a/components/icons/compliance/ens.png b/components/icons/compliance/ens.png new file mode 100644 index 0000000000..c3e6433f31 Binary files /dev/null and b/components/icons/compliance/ens.png differ diff --git a/components/icons/compliance/fedramp.svg b/components/icons/compliance/fedramp.svg new file mode 100644 index 0000000000..7706bd4eff --- /dev/null +++ b/components/icons/compliance/fedramp.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/components/icons/compliance/ffiec.svg b/components/icons/compliance/ffiec.svg new file mode 100644 index 0000000000..40951851e9 --- /dev/null +++ b/components/icons/compliance/ffiec.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/components/icons/compliance/gdpr.svg b/components/icons/compliance/gdpr.svg new file mode 100644 index 0000000000..31175e9afe --- /dev/null +++ b/components/icons/compliance/gdpr.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/components/icons/compliance/gxp-aws.svg b/components/icons/compliance/gxp-aws.svg new file mode 100644 index 0000000000..7b22b53de3 --- /dev/null +++ b/components/icons/compliance/gxp-aws.svg @@ -0,0 +1,455 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/components/icons/compliance/hipaa.svg b/components/icons/compliance/hipaa.svg new file mode 100644 index 0000000000..b9537032f6 --- /dev/null +++ b/components/icons/compliance/hipaa.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/components/icons/compliance/iso-27001.svg b/components/icons/compliance/iso-27001.svg new file mode 100644 index 0000000000..109c19521d --- /dev/null +++ b/components/icons/compliance/iso-27001.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/components/icons/compliance/mitre-attack.svg b/components/icons/compliance/mitre-attack.svg new file mode 100644 index 0000000000..c546eedd83 --- /dev/null +++ b/components/icons/compliance/mitre-attack.svg @@ -0,0 +1,315 @@ + + + + + + + + + + + + + + + diff --git a/components/icons/compliance/nist.svg b/components/icons/compliance/nist.svg new file mode 100644 index 0000000000..4231017c94 --- /dev/null +++ b/components/icons/compliance/nist.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/components/icons/compliance/pci-dss.svg b/components/icons/compliance/pci-dss.svg new file mode 100644 index 0000000000..52f93be38b --- /dev/null +++ b/components/icons/compliance/pci-dss.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/components/icons/compliance/rbi.svg b/components/icons/compliance/rbi.svg new file mode 100644 index 0000000000..ce1d81f83d --- /dev/null +++ b/components/icons/compliance/rbi.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/components/icons/compliance/soc2.svg b/components/icons/compliance/soc2.svg new file mode 100644 index 0000000000..d2b8782541 --- /dev/null +++ b/components/icons/compliance/soc2.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/components/icons/index.ts b/components/icons/index.ts index c057522527..7912d466fd 100644 --- a/components/icons/index.ts +++ b/components/icons/index.ts @@ -1,3 +1,4 @@ +export * from "./compliance/IconCompliance"; export * from "./Icons"; export * from "./prowler/ProwlerIcons"; export * from "./services/IconServices"; diff --git a/dataCompliance.json b/dataCompliance.json new file mode 100644 index 0000000000..a1bdd62831 --- /dev/null +++ b/dataCompliance.json @@ -0,0 +1,438 @@ +{ + "links": { + "first": "http://localhost:8080/api/v1/compliance?page%5Bnumber%5D=1", + "last": "http://localhost:8080/api/v1/compliance?page%5Bnumber%5D=1", + "next": null, + "prev": null + }, + "data": [ + { + "id": "001", + "attributes": { + "title": "AWS Account Security Onboarding", + "passingRequirements": 28, + "totalRequirements": 83, + "regions": ["us-east-1", "us-east-2"] + }, + "lastScan": { + "attributes": { + "passingRequirements": 28, + "totalRequirements": 83 + } + } + }, + { + "id": "002", + "attributes": { + "title": "AWS Audit Manager Control Tower Guardrails", + "passingRequirements": 10, + "totalRequirements": 14, + "regions": ["us-west-1", "us-west-2"] + }, + "lastScan": { + "attributes": { + "passingRequirements": 14, + "totalRequirements": 14 + } + } + }, + { + "id": "003", + "attributes": { + "title": "AWS Foundational Security Best Practices", + "passingRequirements": 22, + "totalRequirements": 37, + "regions": ["eu-west-1", "eu-west-2"] + }, + "lastScan": { + "attributes": { + "passingRequirements": 22, + "totalRequirements": 37 + } + } + }, + { + "id": "004", + "attributes": { + "title": "AWS Foundational Technical Review", + "passingRequirements": 3, + "totalRequirements": 45, + "regions": ["ap-southeast-1", "ap-southeast-2"] + }, + "lastScan": { + "attributes": { + "passingRequirements": 25, + "totalRequirements": 45 + } + } + }, + { + "id": "005", + "attributes": { + "title": "AWS Well Architected Framework Reliability Pillar", + "passingRequirements": 2, + "totalRequirements": 3, + "regions": [] + }, + "lastScan": { + "attributes": { + "passingRequirements": 0, + "totalRequirements": 3 + } + } + }, + { + "id": "006", + "attributes": { + "title": "AWS Well Architected Framework Security Pillar", + "passingRequirements": 19, + "totalRequirements": 57, + "regions": [] + }, + "lastScan": { + "attributes": { + "passingRequirements": 24, + "totalRequirements": 57 + } + } + }, + { + "id": "007", + "attributes": { + "title": "CIS 1.4", + "passingRequirements": 43, + "totalRequirements": 58, + "regions": [] + }, + "lastScan": { + "attributes": { + "passingRequirements": 40, + "totalRequirements": 58 + } + } + }, + { + "id": "008", + "attributes": { + "title": "CIS 1.5", + "passingRequirements": 48, + "totalRequirements": 63, + "regions": [] + }, + "lastScan": { + "attributes": { + "passingRequirements": 50, + "totalRequirements": 63 + } + } + }, + { + "id": "009", + "attributes": { + "title": "CIS 2.0", + "passingRequirements": 47, + "totalRequirements": 64, + "regions": [] + }, + "lastScan": { + "attributes": { + "passingRequirements": 64, + "totalRequirements": 64 + } + } + }, + { + "id": "010", + "attributes": { + "title": "CIS 3.0", + "passingRequirements": 45, + "totalRequirements": 62, + "regions": [] + }, + "lastScan": { + "attributes": { + "passingRequirements": 48, + "totalRequirements": 62 + } + } + }, + { + "id": "011", + "attributes": { + "title": "CISA", + "passingRequirements": 4, + "totalRequirements": 16, + "regions": [] + }, + "lastScan": { + "attributes": { + "passingRequirements": 4, + "totalRequirements": 16 + } + } + }, + { + "id": "012", + "attributes": { + "title": "ENS RD2022 Categoría ALTA", + "passingRequirements": 89, + "totalRequirements": 189, + "regions": [] + }, + "lastScan": { + "attributes": { + "passingRequirements": 82, + "totalRequirements": 189 + } + } + }, + { + "id": "013", + "attributes": { + "title": "FFIEC", + "passingRequirements": 8, + "totalRequirements": 44, + "regions": [] + }, + "lastScan": { + "attributes": { + "passingRequirements": 40, + "totalRequirements": 44 + } + } + }, + { + "id": "014", + "attributes": { + "title": "FedRAMP Low Revision 4", + "passingRequirements": 4, + "totalRequirements": 18, + "regions": [] + }, + "lastScan": { + "attributes": { + "passingRequirements": 4, + "totalRequirements": 18 + } + } + }, + { + "id": "015", + "attributes": { + "title": "FedRamp Moderate Revision 4", + "passingRequirements": 11, + "totalRequirements": 64, + "regions": [] + }, + "lastScan": { + "attributes": { + "passingRequirements": 28, + "totalRequirements": 64 + } + } + }, + { + "id": "016", + "attributes": { + "title": "GDPR", + "passingRequirements": 0, + "totalRequirements": 3, + "regions": [] + }, + "lastScan": { + "attributes": { + "passingRequirements": 0, + "totalRequirements": 3 + } + } + }, + { + "id": "017", + "attributes": { + "title": "GxP 21 CFR Part 11", + "passingRequirements": 1, + "totalRequirements": 11, + "regions": [] + }, + "lastScan": { + "attributes": { + "passingRequirements": 11, + "totalRequirements": 11 + } + } + }, + { + "id": "018", + "attributes": { + "title": "GxP EU Annex 11", + "passingRequirements": 5, + "totalRequirements": 14, + "regions": [] + }, + "lastScan": { + "attributes": { + "passingRequirements": 5, + "totalRequirements": 14 + } + } + }, + { + "id": "019", + "attributes": { + "title": "HIPAA", + "passingRequirements": 2, + "totalRequirements": 32, + "regions": [] + }, + "lastScan": { + "attributes": { + "passingRequirements": 9, + "totalRequirements": 32 + } + } + }, + { + "id": "020", + "attributes": { + "title": "ISO27001 2013", + "passingRequirements": 55, + "totalRequirements": 79, + "regions": [] + }, + "lastScan": { + "attributes": { + "passingRequirements": 66, + "totalRequirements": 79 + } + } + }, + { + "id": "021", + "attributes": { + "title": "MITRE ATTACK", + "passingRequirements": 7, + "totalRequirements": 46, + "regions": [] + }, + "lastScan": { + "attributes": { + "passingRequirements": 19, + "totalRequirements": 46 + } + } + }, + { + "id": "022", + "attributes": { + "title": "NIST 800 171 Revision 2", + "passingRequirements": 3, + "totalRequirements": 50, + "regions": [] + }, + "lastScan": { + "attributes": { + "passingRequirements": 3, + "totalRequirements": 50 + } + } + }, + { + "id": "023", + "attributes": { + "title": "NIST 800 53 Revision 4", + "passingRequirements": 21, + "totalRequirements": 64, + "regions": [] + }, + "lastScan": { + "attributes": { + "passingRequirements": 21, + "totalRequirements": 64 + } + } + }, + { + "id": "024", + "attributes": { + "title": "NIST 800 53 Revision 5", + "passingRequirements": 75, + "totalRequirements": 288, + "regions": [] + }, + "lastScan": { + "attributes": { + "passingRequirements": 75, + "totalRequirements": 288 + } + } + }, + { + "id": "025", + "attributes": { + "title": "NIST CSF 1.1", + "passingRequirements": 15, + "totalRequirements": 56, + "regions": [] + }, + "lastScan": { + "attributes": { + "passingRequirements": 46, + "totalRequirements": 56 + } + } + }, + { + "id": "026", + "attributes": { + "title": "PCI 3.2.1", + "passingRequirements": 11, + "totalRequirements": 19, + "regions": [] + }, + "lastScan": { + "attributes": { + "passingRequirements": 19, + "totalRequirements": 19 + } + } + }, + { + "id": "027", + "attributes": { + "title": "RBI Cyber Security Framework", + "passingRequirements": 3, + "totalRequirements": 9, + "regions": [] + }, + "lastScan": { + "attributes": { + "passingRequirements": 3, + "totalRequirements": 9 + } + } + }, + { + "id": "028", + "attributes": { + "title": "SOC2", + "passingRequirements": 7, + "totalRequirements": 56, + "regions": [] + }, + "lastScan": { + "attributes": { + "passingRequirements": 7, + "totalRequirements": 56 + } + } + } + ], + "meta": { + "pagination": { + "page": 1, + "pages": 1, + "count": 28 + }, + "version": "v1" + } +} diff --git a/images.d.ts b/images.d.ts new file mode 100644 index 0000000000..ad238adbf8 --- /dev/null +++ b/images.d.ts @@ -0,0 +1,29 @@ +declare module "*.png" { + const value: string; + export default value; +} + +declare module "*.jpg" { + const value: string; + export default value; +} + +declare module "*.jpeg" { + const value: string; + export default value; +} + +declare module "*.gif" { + const value: string; + export default value; +} + +declare module "*.svg" { + const content: any; + export default content; +} + +declare module "*.webp" { + const value: string; + export default value; +} diff --git a/tsconfig.json b/tsconfig.json index f204766d1b..0a3c1cf1de 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -25,5 +25,11 @@ "target": "es5" }, "exclude": ["node_modules"], - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"] + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + "images.d.ts" + ] }