From 937006c1fb977ea29ac72fcb9dcc00948bc1db7c Mon Sep 17 00:00:00 2001 From: Mathew Goldsborough <1759329+mgoldsborough@users.noreply.github.com> Date: Fri, 20 Feb 2026 16:30:20 -1000 Subject: [PATCH] Surface scanner version on security tab Extract scanner_version from the scan report metadata in the API response and display it on the security tab next to the scan date. --- apps/registry/src/routes/packages.ts | 5 +++++ apps/registry/src/schemas/generated/api-responses.ts | 1 + apps/web/src/components/SecurityReportSection.tsx | 7 +++++++ apps/web/src/schemas/generated/api-responses.ts | 1 + packages/schemas/src/api-responses.ts | 1 + 5 files changed, 15 insertions(+) diff --git a/apps/registry/src/routes/packages.ts b/apps/registry/src/routes/packages.ts index 588502f..ff3256b 100644 --- a/apps/registry/src/routes/packages.ts +++ b/apps/registry/src/routes/packages.ts @@ -607,10 +607,15 @@ export const packageRoutes: FastifyPluginAsync = async (fastify) => { remediation: (f['remediation'] as string) ?? null, })); + // Extract scanner version from report metadata + const scanMeta = report?.['scan'] as Record | undefined; + const scannerVersion = (scanMeta?.['scanner_version'] as string) ?? null; + return { status: scan['status'], risk_score: scan['riskScore'], scanned_at: scan['completedAt'], + scanner_version: scannerVersion, certification: scan['certificationLevel'] !== null ? { level: scan['certificationLevel'], level_name: getCertificationLevelName(scan['certificationLevel'] as number | null), diff --git a/apps/registry/src/schemas/generated/api-responses.ts b/apps/registry/src/schemas/generated/api-responses.ts index 88e123d..748c3e0 100644 --- a/apps/registry/src/schemas/generated/api-responses.ts +++ b/apps/registry/src/schemas/generated/api-responses.ts @@ -61,6 +61,7 @@ export const SecurityScanSchema = z.object({ status: z.enum(['pending', 'scanning', 'completed', 'failed']), risk_score: z.enum(['CRITICAL', 'HIGH', 'MEDIUM', 'LOW']).nullable(), scanned_at: z.union([z.string(), z.date()]).nullable(), + scanner_version: z.string().nullable().optional(), certification: CertificationSchema.nullable().optional(), summary: z.object({ components: z.number(), diff --git a/apps/web/src/components/SecurityReportSection.tsx b/apps/web/src/components/SecurityReportSection.tsx index 4d8cab8..6391a74 100644 --- a/apps/web/src/components/SecurityReportSection.tsx +++ b/apps/web/src/components/SecurityReportSection.tsx @@ -44,6 +44,7 @@ interface SecurityScan { status: 'pending' | 'scanning' | 'completed' | 'failed'; risk_score: 'CRITICAL' | 'HIGH' | 'MEDIUM' | 'LOW' | null; scanned_at: string | Date | null; + scanner_version?: string | null; certification?: Certification | null; summary?: SecurityScanSummary; scans?: Record; @@ -227,6 +228,12 @@ export default function SecurityReportSection({ securityScan, version }: Props) )} | Scanned {formatDate(securityScan.scanned_at)} + {securityScan.scanner_version && ( + <> + | + Scanner v{securityScan.scanner_version} + + )} diff --git a/apps/web/src/schemas/generated/api-responses.ts b/apps/web/src/schemas/generated/api-responses.ts index debabdc..d474792 100644 --- a/apps/web/src/schemas/generated/api-responses.ts +++ b/apps/web/src/schemas/generated/api-responses.ts @@ -96,6 +96,7 @@ export const SecurityScanSchema = z.object({ status: z.enum(['pending', 'scanning', 'completed', 'failed']), risk_score: z.enum(['CRITICAL', 'HIGH', 'MEDIUM', 'LOW']).nullable(), scanned_at: z.union([z.string(), z.date()]).nullable(), + scanner_version: z.string().nullable().optional(), certification: CertificationSchema.nullable().optional(), summary: z.object({ components: z.number(), // Total SBOM components diff --git a/packages/schemas/src/api-responses.ts b/packages/schemas/src/api-responses.ts index a8702e2..e212379 100644 --- a/packages/schemas/src/api-responses.ts +++ b/packages/schemas/src/api-responses.ts @@ -111,6 +111,7 @@ export const SecurityScanSchema = z.object({ status: z.enum(["pending", "scanning", "completed", "failed"]), risk_score: z.enum(["CRITICAL", "HIGH", "MEDIUM", "LOW"]).nullable(), scanned_at: z.union([z.string(), z.date()]).nullable(), + scanner_version: z.string().nullable().optional(), certification: CertificationSchema.nullable().optional(), summary: z .object({