This commit is contained in:
valenti
2026-09-08 07:21:34 -04:00
commit a4b224da8e
169 changed files with 27483 additions and 0 deletions
+84
View File
@@ -0,0 +1,84 @@
"use client";
import React from "react";
import { ShieldCheck, ShieldAlert, Bug, TestTube2, Lock } from "lucide-react";
import { Card, CardBody, CardHeader, CardTitle } from "@/components/ui/Card";
import { Badge } from "@/components/ui/Badge";
import { EmptyState } from "@/components/ui/EmptyState";
import { MetricCard } from "@/components/ui/MetricCard";
import { ProgressRing } from "@/components/ui/ProgressRing";
const FINDINGS = [
{ sev: "critical", title: "Hardcoded secret in asf-core/config.py#L18", rule: "SECRET-LEAK-001", file: "services/asf-core/app/config.py" },
{ sev: "high", title: "SQL injection vector in raw query", rule: "SQLI-014", file: "services/asf-core/app/repositories/artifact_repo.py" },
{ sev: "medium", title: "CORS permissive in dev-trust mode", rule: "CORS-002", file: "services/asf-core/app/main.py" },
{ sev: "medium", title: "Missing rate limit on /ask-asf", rule: "RATE-010", file: "services/asf-core/app/api/v1/ask_asf.py" },
{ sev: "low", title: "Insecure cookie flags hint", rule: "COOKIE-004", file: "services/asf-core/app/dependencies.py" },
];
const SEV_TONES: Record<string, "red" | "amber" | "neutral"> = {
critical: "red", high: "red", medium: "amber", low: "neutral",
};
export default function QualityPage() {
return (
<div className="p-6 lg:p-8 max-w-[1400px] mx-auto">
<header className="mb-6 flex flex-wrap items-start justify-between gap-3">
<div>
<h1 className="text-2xl font-semibold tracking-tight text-slate-900 flex items-center gap-2">
<ShieldCheck className="w-6 h-6 text-emerald-600" />
Quality & Security
</h1>
<p className="mt-1 text-sm text-slate-500">
Static analysis, test coverage, security findings, build health — consolidated from every ASF connector.
</p>
</div>
<Badge tone="neutral">SonarQube · Semgrep connectors</Badge>
</header>
<section className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-6">
<MetricCard label="Critical findings" value={1} tone="red" icon={<ShieldAlert className="w-5 h-5" />} />
<MetricCard label="Tests passing" value={498} tone="green" icon={<TestTube2 className="w-5 h-5" />} subtitle="of 512 total" delta={0.6} />
<MetricCard label="Coverage" value={68.9} isPercentage tone="brand" icon={<Bug className="w-5 h-5" />} />
<MetricCard label="Reliability score" value="A-" tone="green" icon={<Lock className="w-5 h-5" />} />
</section>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-4 mb-6">
<Card>
<CardHeader><CardTitle>Scores</CardTitle></CardHeader>
<CardBody className="space-y-4">
<ProgressRing value={72} tone="green" label="Security" sublabel="Findings + hardening" />
<ProgressRing value={68.9} tone="brand" label="Coverage" sublabel="Statement coverage" />
<ProgressRing value={81} tone="amber" label="Maintainability" sublabel="Duplication + complexity" />
</CardBody>
</Card>
<Card className="lg:col-span-2">
<CardHeader>
<CardTitle>Security findings</CardTitle>
<Badge tone="red">{FINDINGS.filter((f) => f.sev === "critical" || f.sev === "high").length} critical/high</Badge>
</CardHeader>
<CardBody className="p-0">
{FINDINGS.length === 0 ? (
<div className="p-6"><EmptyState title="No findings" description="Your first scan will populate this panel." icon={ShieldCheck} /></div>
) : (
<ul className="divide-y divide-slate-100">
{FINDINGS.map((f, i) => (
<li key={i} className="p-3 hover:bg-slate-50 flex items-start gap-3">
<Badge tone={SEV_TONES[f.sev]} className="uppercase mt-0.5">{f.sev}</Badge>
<div className="min-w-0 flex-1">
<div className="text-sm font-medium text-slate-800">{f.title}</div>
<div className="mt-1 text-xs text-slate-500 flex flex-wrap items-center gap-3">
<span className="asf-monospace">{f.rule}</span>
<span className="asf-monospace truncate">{f.file}</span>
</div>
</div>
</li>
))}
</ul>
)}
</CardBody>
</Card>
</div>
</div>
);
}