Commit

Security matches the workspace's other pages and leaves out deleted repositories; every section has a name in the trail

syntaqxcommitted Parent5a58561Browse files
3 files+24−130/3 viewed
+10−0
12251225 audit: "Audit log",
12261226 tree: "Files",
12271227 blob: "Files",
1228+ agents: "Agents",
1229+ context: "Context",
1230+ memory: "Memory",
1231+ security: "Security",
1232+ packages: "Packages",
1233+ runners: "Runners",
1234+ workflows: "Workflows",
1235+ observability: "Observability",
1236+ insights: "Insights",
1237+ sessions: "Sessions",
12281238 };
12291239
12301240 /** Where the page is, as a trail of links: workspace / repository / section. */
+4−0
8787 title: "Self-hosted runners",
8888 about: "Your own machines, which run workflow jobs, and if you choose agents' work, for free. They connect out to g1t; nothing reaches in.",
8989 },
90+ security: {
91+ title: "Security",
92+ about: "Open alerts in every project: secrets found in pushes and history, and vulnerable dependencies. Each project's Security page has the details and the security update g1t opened for each.",
93+ },
9094 integrations: {
9195 title: "Integrations",
9296 about: "Model providers, alerts and trackers. Secrets are sealed when saved, and agents never see them.",
+10−13
1−import { ChevronRight, ShieldCheck } from "lucide-react";
1+import { ChevronRight } from "lucide-react";
22 import { Link, data } from "react-router";
33
44 import { SEVERITIES, type SeverityCounts } from "@g1t/contracts";
88 import { SeverityCountsGrid, SeverityCountsInline } from "../../components/security";
99 import { TimeAgo } from "../../components/ui";
1010 import { Badge } from "../../components/ui/badge";
11−import { security } from "../../lib/services.server";
11+import { repos, security } from "../../lib/services.server";
1212 import { getViewer, roleIn, unwrap } from "../../lib/session.server";
1313
1414 export function meta({ params, ...args }: Route.MetaArgs) {
1818 export async function loader({ params, context }: Route.LoaderArgs) {
1919 const viewer = getViewer(context);
2020 if (!roleIn(viewer, params.owner)) throw data(null, { status: 404 });
21− const projects = unwrap(await security.workspace(params.owner, viewer));
21+ const [scanned, current] = await Promise.all([
22+ security.workspace(params.owner, viewer),
23+ repos.list(viewer, { namespace: params.owner.toLowerCase() }),
24+ ]);
25+ // Only repositories that are still there: a deleted one's alerts stay
26+ // with it for its 30 days, but not on this page.
27+ const live = new Set(current.map((repo) => repo.id));
28+ const projects = unwrap(scanned).filter((project) => live.has(project.repoId));
2229 const total = Object.fromEntries(SEVERITIES.map((severity) => [severity, 0])) as SeverityCounts;
2330 for (const project of projects) {
2431 for (const severity of SEVERITIES) total[severity] += project.counts[severity];
3542 const { projects, total } = loaderData;
3643 return (
3744 <div className="space-y-6">
38− <div>
39− <h2 className="flex items-center gap-2 text-xl font-semibold tracking-tight">
40− <ShieldCheck size={19} className="text-accent" />
41− Security across projects
42− </h2>
43− <p className="mt-1.5 max-w-2xl text-sm text-muted">
44− Open alerts in every project of {params.owner}: secrets found in pushes and history, and vulnerable
45− dependencies. Each project's Security page has the details and the security update g1t opened for each.
46− </p>
47− </div>
4845 <div>
4946 <SeverityCountsGrid counts={total} />
5047 <p className="mt-2 text-xs text-faint">