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 && (