From f236d2087a1f1aa52a8ff9ed53d0133e8bb58e9a Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Mon, 19 Aug 2024 12:11:50 +0200 Subject: [PATCH] feat: Attack Surface component is ready --- app/(prowler)/page.tsx | 3 +- components/charts/StatusChart.tsx | 12 ---- components/overview/AttackSurface.tsx | 44 ++++++++++++ components/overview/index.ts | 1 + components/ui/action-card/ActionCard.tsx | 86 ++++++++++++++++++++++++ components/ui/index.ts | 1 + lib/utils.ts | 19 +++++- tailwind.config.js | 49 ++++++++++++++ 8 files changed, 200 insertions(+), 15 deletions(-) create mode 100644 components/overview/AttackSurface.tsx create mode 100644 components/overview/index.ts create mode 100644 components/ui/action-card/ActionCard.tsx diff --git a/app/(prowler)/page.tsx b/app/(prowler)/page.tsx index 7cf4e2273f..2c2c286166 100644 --- a/app/(prowler)/page.tsx +++ b/app/(prowler)/page.tsx @@ -2,6 +2,7 @@ import { Spacer } from "@nextui-org/react"; import { StatusChart } from "@/components/charts"; import { FilterControls } from "@/components/filters"; +import { AttackSurface } from "@/components/overview"; import { Header } from "@/components/ui"; import { CustomBox } from "@/components/ui/custom"; @@ -29,7 +30,7 @@ export default function Home() { preTitle={"Attack Surface"} className="col-span-12 sm:col-span-12 xl:col-span-4 3xl:col-span-5" > -

hi hi

+ diff --git a/components/charts/StatusChart.tsx b/components/charts/StatusChart.tsx index ec0253b529..316820aa3d 100644 --- a/components/charts/StatusChart.tsx +++ b/components/charts/StatusChart.tsx @@ -39,22 +39,10 @@ const chartConfig = { number: { label: "Findings", }, - chrome: { - label: "Chrome", - color: "hsl(var(--chart-1))", - }, success: { label: "Success", color: "hsl(var(--chart-success))", }, - firefox: { - label: "Firefox", - color: "hsl(var(--chart-3))", - }, - edge: { - label: "Edge", - color: "hsl(var(--chart-4))", - }, fail: { label: "Fail", color: "hsl(var(--chart-fail))", diff --git a/components/overview/AttackSurface.tsx b/components/overview/AttackSurface.tsx new file mode 100644 index 0000000000..61bb2fdfd2 --- /dev/null +++ b/components/overview/AttackSurface.tsx @@ -0,0 +1,44 @@ +import React from "react"; + +import ActionCard from "../ui/action-card/ActionCard"; + +const cardData = [ + { + findings: 3, + icon: "solar:danger-triangle-bold", + title: "Internet Exposed Resources", + }, + { + findings: 15, + icon: "solar:danger-triangle-bold", + title: "Exposed Secrets", + }, + { + findings: 0, + icon: "heroicons:shield-check-solid", + title: "IAM Policies Leading to Privilege Escalation", + }, + { + findings: 0, + icon: "heroicons:shield-check-solid", + title: "EC2 with Metadata Service V1 (IMDSv1)", + }, +]; + +export const AttackSurface = () => { + return ( +
+ {cardData.map((card, index) => ( + 0 ? "fail" : "success"} + icon={card.findings > 0 ? "solar:danger-triangle-bold" : card.icon} + title={card.title} + description={ + card.findings > 0 ? "Review Required" : "No Issues Found" + } + /> + ))} +
+ ); +}; diff --git a/components/overview/index.ts b/components/overview/index.ts new file mode 100644 index 0000000000..452749b068 --- /dev/null +++ b/components/overview/index.ts @@ -0,0 +1 @@ +export * from "./AttackSurface"; diff --git a/components/ui/action-card/ActionCard.tsx b/components/ui/action-card/ActionCard.tsx new file mode 100644 index 0000000000..e4c0234ffd --- /dev/null +++ b/components/ui/action-card/ActionCard.tsx @@ -0,0 +1,86 @@ +"use client"; + +import { Icon } from "@iconify/react"; +import type { CardProps } from "@nextui-org/react"; +import { Card, CardBody } from "@nextui-org/react"; +import React from "react"; + +import { cn } from "@/lib"; + +export type ActionCardProps = CardProps & { + icon: string; + title: string; + color?: "success" | "secondary" | "warning" | "fail"; + description: string; +}; + +const ActionCard = React.forwardRef( + ({ color, title, icon, description, children, className, ...props }, ref) => { + const colors = React.useMemo(() => { + switch (color) { + case "success": + return { + card: "border-system-success-medium", + iconWrapper: "bg-system-success-lighter border-system-success", + icon: "text-system-success", + }; + case "secondary": + return { + card: "border-secondary-100", + iconWrapper: "bg-secondary-50 border-secondary-100", + icon: "text-secondary", + }; + case "warning": + return { + card: "border-warning-500", + iconWrapper: "bg-warning-50 border-warning-100", + icon: "text-warning-600", + }; + case "fail": + return { + card: "border-danger-300", + iconWrapper: "bg-danger-50 border-danger-100", + icon: "text-danger", + }; + + default: + return { + card: "border-default-200", + iconWrapper: "bg-default-50 border-default-100", + icon: "text-default-500", + }; + } + }, [color]); + + return ( + + +
+ +
+
+

{title}

+

+ {description || children} +

+
+
+
+ ); + }, +); + +ActionCard.displayName = "ActionCard"; + +export default ActionCard; diff --git a/components/ui/index.ts b/components/ui/index.ts index 86e8f0143a..f026779b96 100644 --- a/components/ui/index.ts +++ b/components/ui/index.ts @@ -1,3 +1,4 @@ +export * from "./action-card/ActionCard"; export * from "./alert/Alert"; export * from "./chart/Chart"; export * from "./dialog/Dialog"; diff --git a/lib/utils.ts b/lib/utils.ts index 84a5fe24e3..c236eefe6a 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -1,12 +1,27 @@ import { type ClassValue, clsx } from "clsx"; -import { twMerge } from "tailwind-merge"; +import { extendTailwindMerge } from "tailwind-merge"; +const COMMON_UNITS = ["small", "medium", "large"]; import { MetaDataProps } from "@/types"; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); } - +const twMerge = extendTailwindMerge({ + extend: { + theme: { + opacity: ["disabled"], + spacing: ["divider"], + borderWidth: COMMON_UNITS, + borderRadius: COMMON_UNITS, + }, + classGroups: { + shadow: [{ shadow: COMMON_UNITS }], + "font-size": [{ text: ["tiny", ...COMMON_UNITS] }], + "bg-image": ["bg-stripe-gradient"], + }, + }, +}); export const parseStringify = (value: any) => JSON.parse(JSON.stringify(value)); export const convertFileToUrl = (file: File) => URL.createObjectURL(file); diff --git a/tailwind.config.js b/tailwind.config.js index 333f9f5d0f..7bf6fecf2c 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -18,6 +18,34 @@ module.exports = { }, }, extend: { + colors: { + system: { + success: { + DEFAULT: "#09BF3D", + medium: "#3CEC6D", + light: "#B5FDC8", + lighter: "#D9FFE3", + }, + error: { + DEFAULT: "#E11D48", + medium: "#FB718F", + light: "#FECDD8", + lighter: "#FFE4EA", + }, + info: { + DEFAULT: "#7C3AED", + medium: "#B48BFA", + light: "#E5D6FE", + lighter: "#F1E9FE", + }, + warning: { + DEFAULT: "#FBBF24", + medium: "#FDDD8A", + light: "#feefc7", + lighter: "#FFF9EB", + }, + }, + }, fontFamily: { sans: ["var(--font-sans)"], mono: ["var(--font-geist-mono)"], @@ -31,6 +59,27 @@ module.exports = { from: { height: "var(--radix-accordion-content-height)" }, to: { height: "0" }, }, + advance: { from: { width: 0 }, to: { width: "100%" } }, + "fade-in": { from: { opacity: 0 }, to: { opacity: 1 } }, + "fade-out": { from: { opacity: 1 }, to: { opacity: 0 } }, + "slide-in": { + from: { transform: "translateX(100%)" }, + to: { transform: "translateX(0)" }, + }, + "slide-out": { + from: { transform: "translateX(0)" }, + to: { transform: "translateX(100%)" }, + }, + woosh: { + "0, 10%": { left: 0, right: "100%" }, + "40%, 60%": { left: 0, right: 0 }, + "90%, 100%": { left: "100%", right: 0 }, + }, + lineAnim: { + "0%": { left: "-40%" }, + "50%": { left: "20%", width: "80%" }, + "100%": { left: "100%", width: "100%" }, + }, }, animation: { "accordion-down": "accordion-down 0.2s ease-out",