Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.
| Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar | 1 | import { ArrowLeft, CircleAlert, CircleCheck } from "lucide-react"; |
| 2 | import { Link } from "react-router"; | |
| 3 | ||
| 4 | import type { Route } from "./+types/security-pull"; | |
| 5 | import { page } from "../../lib/meta"; | |
| 6 | import { SeverityBadge } from "../../components/security"; | |
| 7 | import { CARD, LIST, SectionHeader, shortRule } from "../../components/security-suite"; | |
| 8 | import { Badge } from "../../components/ui/badge"; | |
| The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after. | 9 | import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "../../components/ui/table"; |
| 10 | import { cn } from "../../lib/cn"; | |
| Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar | 11 | import { securitySuite } from "../../lib/services.server"; |
| 12 | import { getViewer, requireUser, unwrap } from "../../lib/session.server"; | |
| 13 | import { requireInsider } from "../../lib/access.server"; | |
| 14 | ||
| 15 | export function meta({ params, ...args }: Route.MetaArgs) { | |
| 16 | return page(args, { title: `Security checks · #${params.number} · ${params.owner}/${params.repo} · g1t` }); | |
| 17 | } | |
| 18 | ||
| 19 | export async function loader({ params, context, request }: Route.LoaderArgs) { | |
| 20 | const viewer = getViewer(context) ?? requireUser(context, request); | |
| Merge main (membership, two-factor, GitHub repo roles) into tokens | 21 | await requireInsider(context, params, "security_alerts"); |
| Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar | 22 | const repo = { namespace: params.owner, name: params.repo }; |
| 23 | return { scanning: unwrap(await securitySuite.pullScanning(repo, Number(params.number) || 0, viewer)) }; | |
| 24 | } | |
| 25 | ||
| 26 | function Verdict({ passed, text }: { passed: boolean; text: string }) { | |
| 27 | return ( | |
| Merge branch 'worktree-agent-ad7c6d88d93adc817' | 28 | <p className={`flex items-center gap-1.5 text-sm ${passed ? "text-success" : "text-danger"}`}> |
| Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar | 29 | {passed ? <CircleCheck size={15} /> : <CircleAlert size={15} />} |
| 30 | {text} | |
| 31 | </p> | |
| 32 | ); | |
| 33 | } | |
| 34 | ||
| 35 | /** What the Code scanning and Dependency review checks found on a pull request: the page their statuses link to. */ | |
| 36 | export default function PullSecurity({ loaderData, params }: Route.ComponentProps) { | |
| 37 | const { scanning } = loaderData; | |
| 38 | const base = `/${params.owner}/${params.repo}`; | |
| 39 | const onLines = scanning.results.filter((result) => result.new && result.onChangedLine); | |
| 40 | const others = scanning.results.filter((result) => !(result.new && result.onChangedLine)); | |
| 41 | return ( | |
| 42 | <div className="max-w-4xl space-y-8"> | |
| 43 | <Link to={`${base}/pull/${params.number}`} className="inline-flex items-center gap-1.5 text-sm text-muted hover:text-fg"> | |
| 44 | <ArrowLeft size={14} /> Pull request #{params.number} | |
| 45 | </Link> | |
| 46 | <SectionHeader | |
| 47 | title={`Security checks for #${params.number}`} | |
| 48 | about={`What code scanning and dependency review found on this pull request${scanning.commit ? ` at ${scanning.commit.slice(0, 7)}` : ""}. Require the checks in branch protection to block merges on them.`} | |
| 49 | /> | |
| 50 | <section className="space-y-3"> | |
| 51 | <h3 className="text-base font-semibold tracking-tight">Code scanning</h3> | |
| 52 | {scanning.codeStatus ? ( | |
| 53 | <Verdict passed={scanning.codeStatus === "success"} text={scanning.codeDescription ?? ""} /> | |
| 54 | ) : ( | |
| 55 | <p className="text-sm text-muted">No code scanning results for this pull request yet.</p> | |
| 56 | )} | |
| 57 | {onLines.length > 0 && ( | |
| 58 | <ul className={LIST}> | |
| 59 | {onLines.map((result, at) => ( | |
| 60 | <li key={at} className="px-4 py-3"> | |
| 61 | <div className="flex flex-wrap items-center gap-2"> | |
| 62 | <span className="text-sm font-medium">{shortRule(result.ruleId)}</span> | |
| 63 | <SeverityBadge severity={result.severity} /> | |
| 64 | {result.failing && <Badge tone="danger">Fails the check</Badge>} | |
| 65 | <span className="text-xs text-faint">{result.tool}</span> | |
| 66 | </div> | |
| 67 | <p className="mt-1 text-xs text-muted">{result.message}</p> | |
| 68 | {result.path && ( | |
| 69 | <p className="mt-1 font-mono text-xs text-fg-soft"> | |
| 70 | {result.path} | |
| 71 | {result.line ? `:${result.line}` : ""} | |
| 72 | </p> | |
| 73 | )} | |
| 74 | </li> | |
| 75 | ))} | |
| 76 | </ul> | |
| 77 | )} | |
| 78 | {others.length > 0 && ( | |
| 79 | <p className="text-xs text-faint"> | |
| 80 | {others.length} other {others.length === 1 ? "result" : "results"}: already open on the default branch, or on lines this pull request does not change. | |
| 81 | </p> | |
| 82 | )} | |
| 83 | </section> | |
| 84 | <section className="space-y-3"> | |
| 85 | <h3 className="text-base font-semibold tracking-tight">Dependency review</h3> | |
| 86 | {!scanning.review ? ( | |
| 87 | <p className="text-sm text-muted">Not reviewed yet: it runs when a pull request opens and each time its head moves.</p> | |
| 88 | ) : ( | |
| 89 | <> | |
| 90 | <Verdict passed={scanning.review.passed} text={scanning.review.headline} /> | |
| 91 | {scanning.review.changes.length > 0 && ( | |
| The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after. | 92 | <div className={CARD}> |
| 93 | <Table className="min-w-[36rem]"> | |
| 94 | <TableHeader> | |
| 95 | <TableRow> | |
| 96 | <TableHead className="px-4">Change</TableHead> | |
| 97 | <TableHead>Package</TableHead> | |
| 98 | <TableHead>License</TableHead> | |
| 99 | <TableHead>Vulnerabilities</TableHead> | |
| 100 | </TableRow> | |
| 101 | </TableHeader> | |
| 102 | <TableBody> | |
| Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar | 103 | {scanning.review.changes.map((change) => ( |
| The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after. | 104 | <TableRow key={`${change.changeType}:${change.manifest}:${change.name}@${change.version}`}> |
| 105 | <TableCell className="px-4 text-xs">{change.changeType === "added" ? "Added" : "Removed"}</TableCell> | |
| 106 | <TableCell className="font-mono text-xs"> | |
| Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar | 107 | {change.name}@{change.version} |
| The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after. | 108 | </TableCell> |
| 109 | <TableCell className={cn("text-xs", change.deniedLicense ? "text-danger" : "text-muted")}>{change.license ?? "—"}</TableCell> | |
| 110 | <TableCell className="text-xs"> | |
| Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar | 111 | {change.vulnerabilities.length === 0 ? ( |
| 112 | <span className="text-faint">none known</span> | |
| 113 | ) : ( | |
| 114 | change.vulnerabilities.map((vuln) => ( | |
| 115 | <a key={vuln.osvId} href={vuln.url} className="mr-2 inline-flex items-center gap-1 hover:underline" rel="noreferrer"> | |
| 116 | <SeverityBadge severity={vuln.severity} /> {vuln.advisory} | |
| 117 | </a> | |
| 118 | )) | |
| 119 | )} | |
| The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after. | 120 | </TableCell> |
| 121 | </TableRow> | |
| Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar | 122 | ))} |
| The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after. | 123 | </TableBody> |
| 124 | </Table> | |
| Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar | 125 | </div> |
| 126 | )} | |
| 127 | </> | |
| 128 | )} | |
| 129 | </section> | |
| 130 | </div> | |
| 131 | ); | |
| 132 | } |
This file's history is long; its oldest lines are credited to the oldest commit read.