From e69b7a951df21c2ca2828857f8f298f4265a1060 Mon Sep 17 00:00:00 2001 From: Rezn1r Date: Fri, 22 May 2026 00:55:00 +0800 Subject: [PATCH 1/3] feat: add VirusTotal scan results sidebar card to plugin detail page --- src/app/plugins/[slug]/page.tsx | 6 + src/components/VirusTotalCard.tsx | 223 ++++++++++++++++++++++++++++++ 2 files changed, 229 insertions(+) create mode 100644 src/components/VirusTotalCard.tsx diff --git a/src/app/plugins/[slug]/page.tsx b/src/app/plugins/[slug]/page.tsx index 8f8f345..98ad167 100644 --- a/src/app/plugins/[slug]/page.tsx +++ b/src/app/plugins/[slug]/page.tsx @@ -65,6 +65,9 @@ const DependencyGraph = dynamic(() => import("@/components/DependencyGraph"), { const PluginRatings = dynamic(() => import("@/components/PluginRatings"), { ssr: false, }); +const VirusTotalCard = dynamic(() => import("@/components/VirusTotalCard"), { + ssr: false, +}); async function getPlugin(slug: string) { const { data } = await fetchApi(`/api/v1/plugins/${slug}`, { @@ -1158,6 +1161,9 @@ export default async function PluginDetailPage({ {/* Dependency Graph */} + + {/* VirusTotal Scan */} + diff --git a/src/components/VirusTotalCard.tsx b/src/components/VirusTotalCard.tsx new file mode 100644 index 0000000..21f0604 --- /dev/null +++ b/src/components/VirusTotalCard.tsx @@ -0,0 +1,223 @@ +"use client"; + +import { Shield, ExternalLink, Loader2, AlertTriangle } from "lucide-react"; + +interface VirusTotalCardProps { + version: { + vtStatus?: string | null; + vtMalicious?: number | null; + vtSuspicious?: number | null; + vtUndetected?: number | null; + vtTotal?: number | null; + vtPermalink?: string | null; + vtScanDate?: string | null; + } | null; +} + +export default function VirusTotalCard({ version }: VirusTotalCardProps) { + if (!version) return null; + + const { vtStatus, vtMalicious, vtSuspicious, vtTotal, vtPermalink, vtScanDate } = + version; + + const malicious = vtMalicious ?? 0; + const suspicious = vtSuspicious ?? 0; + + const statusColor = + vtStatus === "completed" + ? malicious === 0 && suspicious === 0 + ? "var(--status-success)" + : malicious > 0 + ? "var(--status-error)" + : "var(--status-warning)" + : vtStatus === "failed" + ? "var(--text-muted)" + : "var(--status-warning)"; + + const statusLabel = + vtStatus === "completed" + ? malicious === 0 && suspicious === 0 + ? "Clean" + : malicious > 0 + ? "Flagged" + : "Suspicious" + : vtStatus === "scanning" || vtStatus === "queued" + ? "Scanning..." + : vtStatus === "failed" + ? "Scan Failed" + : "Not Scanned"; + + const formattedDate = vtScanDate + ? new Date(vtScanDate).toLocaleDateString("en-US", { + year: "numeric", + month: "short", + day: "numeric", + }) + : null; + + return ( +
+
+

+ VirusTotal Scan +

+ {!vtStatus && ( + + Pending + + )} + {(vtStatus === "queued" || vtStatus === "scanning") && ( + + )} +
+ + {vtStatus === "completed" && ( +
+
+ Status + + {statusLabel} + +
+
+ Detection + + {malicious}/{vtTotal ?? 0} engines flagged + +
+ {formattedDate && ( +
+ Scanned + {formattedDate} +
+ )} + {vtPermalink && ( + + View Full Report + + )} +
+ )} + + {(vtStatus === "queued" || vtStatus === "scanning") && ( +

+ + Scan in progress — this may take a few minutes. +

+ )} + + {vtStatus === "failed" && ( +

+ + Scan could not be completed. It will be retried on the next submission. +

+ )} + + {!vtStatus && ( +

+ This version has not been scanned yet. +

+ )} +
+ ); +} From 6d1ea90637c974cf2d5df02873f28a79c7b2daa8 Mon Sep 17 00:00:00 2001 From: Rezn1r Date: Fri, 22 May 2026 01:00:16 +0800 Subject: [PATCH 2/3] style: format VirusTotalCard with prettier --- src/components/VirusTotalCard.tsx | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/components/VirusTotalCard.tsx b/src/components/VirusTotalCard.tsx index 21f0604..8582643 100644 --- a/src/components/VirusTotalCard.tsx +++ b/src/components/VirusTotalCard.tsx @@ -17,8 +17,14 @@ interface VirusTotalCardProps { export default function VirusTotalCard({ version }: VirusTotalCardProps) { if (!version) return null; - const { vtStatus, vtMalicious, vtSuspicious, vtTotal, vtPermalink, vtScanDate } = - version; + const { + vtStatus, + vtMalicious, + vtSuspicious, + vtTotal, + vtPermalink, + vtScanDate, + } = version; const malicious = vtMalicious ?? 0; const suspicious = vtSuspicious ?? 0; @@ -204,7 +210,8 @@ export default function VirusTotalCard({ version }: VirusTotalCardProps) { }} > - Scan could not be completed. It will be retried on the next submission. + Scan could not be completed. It will be retried on the next + submission.

)} From ef5d99874d961b1e9caa902d2ebc20dd8d1dd6f6 Mon Sep 17 00:00:00 2001 From: Rezn1r Date: Fri, 22 May 2026 01:16:43 +0800 Subject: [PATCH 3/3] refactor: use nested virustotal object in VirusTotalCard props --- src/components/VirusTotalCard.tsx | 64 ++++++++++++++----------------- 1 file changed, 29 insertions(+), 35 deletions(-) diff --git a/src/components/VirusTotalCard.tsx b/src/components/VirusTotalCard.tsx index 8582643..977d984 100644 --- a/src/components/VirusTotalCard.tsx +++ b/src/components/VirusTotalCard.tsx @@ -4,57 +4,51 @@ import { Shield, ExternalLink, Loader2, AlertTriangle } from "lucide-react"; interface VirusTotalCardProps { version: { - vtStatus?: string | null; - vtMalicious?: number | null; - vtSuspicious?: number | null; - vtUndetected?: number | null; - vtTotal?: number | null; - vtPermalink?: string | null; - vtScanDate?: string | null; + virustotal?: { + status?: string | null; + malicious?: number | null; + suspicious?: number | null; + undetected?: number | null; + total?: number | null; + permalink?: string | null; + scanDate?: string | null; + } | null; } | null; } export default function VirusTotalCard({ version }: VirusTotalCardProps) { - if (!version) return null; + if (!version?.virustotal) return null; - const { - vtStatus, - vtMalicious, - vtSuspicious, - vtTotal, - vtPermalink, - vtScanDate, - } = version; - - const malicious = vtMalicious ?? 0; - const suspicious = vtSuspicious ?? 0; + const vt = version.virustotal; + const malicious = vt.malicious ?? 0; + const suspicious = vt.suspicious ?? 0; const statusColor = - vtStatus === "completed" + vt.status === "completed" ? malicious === 0 && suspicious === 0 ? "var(--status-success)" : malicious > 0 ? "var(--status-error)" : "var(--status-warning)" - : vtStatus === "failed" + : vt.status === "failed" ? "var(--text-muted)" : "var(--status-warning)"; const statusLabel = - vtStatus === "completed" + vt.status === "completed" ? malicious === 0 && suspicious === 0 ? "Clean" : malicious > 0 ? "Flagged" : "Suspicious" - : vtStatus === "scanning" || vtStatus === "queued" + : vt.status === "scanning" || vt.status === "queued" ? "Scanning..." - : vtStatus === "failed" + : vt.status === "failed" ? "Scan Failed" : "Not Scanned"; - const formattedDate = vtScanDate - ? new Date(vtScanDate).toLocaleDateString("en-US", { + const formattedDate = vt.scanDate + ? new Date(vt.scanDate).toLocaleDateString("en-US", { year: "numeric", month: "short", day: "numeric", @@ -89,7 +83,7 @@ export default function VirusTotalCard({ version }: VirusTotalCardProps) { > VirusTotal Scan - {!vtStatus && ( + {!vt.status && ( )} - {(vtStatus === "queued" || vtStatus === "scanning") && ( + {(vt.status === "queued" || vt.status === "scanning") && ( - {vtStatus === "completed" && ( + {vt.status === "completed" && (
Detection - {malicious}/{vtTotal ?? 0} engines flagged + {malicious}/{vt.total ?? 0} engines flagged
{formattedDate && ( @@ -153,9 +147,9 @@ export default function VirusTotalCard({ version }: VirusTotalCardProps) { {formattedDate} )} - {vtPermalink && ( + {vt.permalink && ( )} - {(vtStatus === "queued" || vtStatus === "scanning") && ( + {(vt.status === "queued" || vt.status === "scanning") && (

)} - {vtStatus === "failed" && ( + {vt.status === "failed" && (

)} - {!vtStatus && ( + {!vt.status && (