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..977d984
--- /dev/null
+++ b/src/components/VirusTotalCard.tsx
@@ -0,0 +1,224 @@
+"use client";
+
+import { Shield, ExternalLink, Loader2, AlertTriangle } from "lucide-react";
+
+interface VirusTotalCardProps {
+ version: {
+ 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?.virustotal) return null;
+
+ const vt = version.virustotal;
+ const malicious = vt.malicious ?? 0;
+ const suspicious = vt.suspicious ?? 0;
+
+ const statusColor =
+ vt.status === "completed"
+ ? malicious === 0 && suspicious === 0
+ ? "var(--status-success)"
+ : malicious > 0
+ ? "var(--status-error)"
+ : "var(--status-warning)"
+ : vt.status === "failed"
+ ? "var(--text-muted)"
+ : "var(--status-warning)";
+
+ const statusLabel =
+ vt.status === "completed"
+ ? malicious === 0 && suspicious === 0
+ ? "Clean"
+ : malicious > 0
+ ? "Flagged"
+ : "Suspicious"
+ : vt.status === "scanning" || vt.status === "queued"
+ ? "Scanning..."
+ : vt.status === "failed"
+ ? "Scan Failed"
+ : "Not Scanned";
+
+ const formattedDate = vt.scanDate
+ ? new Date(vt.scanDate).toLocaleDateString("en-US", {
+ year: "numeric",
+ month: "short",
+ day: "numeric",
+ })
+ : null;
+
+ return (
+
+
+
+ VirusTotal Scan
+
+ {!vt.status && (
+
+ Pending
+
+ )}
+ {(vt.status === "queued" || vt.status === "scanning") && (
+
+ )}
+
+
+ {vt.status === "completed" && (
+
+
+ Status
+
+ {statusLabel}
+
+
+
+ Detection
+
+ {malicious}/{vt.total ?? 0} engines flagged
+
+
+ {formattedDate && (
+
+ Scanned
+ {formattedDate}
+
+ )}
+ {vt.permalink && (
+
+ View Full Report
+
+ )}
+
+ )}
+
+ {(vt.status === "queued" || vt.status === "scanning") && (
+
+
+ Scan in progress — this may take a few minutes.
+
+ )}
+
+ {vt.status === "failed" && (
+
+
+ Scan could not be completed. It will be retried on the next
+ submission.
+
+ )}
+
+ {!vt.status && (
+
+ This version has not been scanned yet.
+
+ )}
+
+ );
+}