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.
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 1 | /** |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 2 | * Security, as a project's page shows it: open alerts by severity, the |
| 3 | * secrets found in pushes and history, vulnerable dependencies with the | |
| 4 | * security update g1t opened for each, and what happened to every alert. | |
| 5 | * The page posts the intents in `routes/repo/security.tsx`'s action. | |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 6 | */ |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 7 | import { |
| 8 | Bot, | |
| 9 | CircleAlert, | |
| 10 | CircleCheck, | |
| 11 | CircleDot, | |
| 12 | CircleSlash, | |
| 13 | ExternalLink, | |
| 14 | GitBranch, | |
| 15 | GitMerge, | |
| 16 | GitPullRequest, | |
| 17 | GitPullRequestClosed, | |
| 18 | History, | |
| 19 | KeyRound, | |
| 20 | Loader, | |
| 21 | Package, | |
| A desktop release goes out with what can be built. Linux is built on g1t's machines in every release; Windows and macOS each wait on a repository variable: a machine registered for them, or for Windows a cross build on Linux once aka.ms and download.visualstudio.microsoft.com are workflow-only domains, which is where the first release stopped. A platform with no machine is skipped and the download page says it is coming, with a word for the visitor whose platform this release lacks. The guide and CONTRIBUTING say so. | 22 | PauseCircle, |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 23 | ShieldAlert, |
| 24 | ShieldCheck, | |
| 25 | } from "lucide-react"; | |
| 26 | import { type ReactNode, useEffect, useRef, useState } from "react"; | |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 27 | import { Link, useFetcher } from "react-router"; |
| 28 | ||
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 29 | import { |
| 30 | type AlertActivity, | |
| 31 | type AlertState, | |
| 32 | DEPENDENCY_DISMISS_REASONS, | |
| 33 | type DismissReason, | |
| 34 | SECRET_DISMISS_REASONS, | |
| 35 | SEVERITIES, | |
| 36 | type PullStatus, | |
| 37 | type SecretFinding, | |
| 38 | type SecurityUpdate, | |
| 39 | type Severity, | |
| 40 | type SeverityCounts, | |
| 41 | type Vulnerability, | |
| 42 | dismissLabel, | |
| 43 | } from "@g1t/contracts"; | |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 44 | |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 45 | import { |
| 46 | type ActivityEntry, | |
| 47 | type PackageGroup, | |
| 48 | UPDATE_STATES, | |
| 49 | alertActivity, | |
| 50 | groupByPackage, | |
| 51 | highestFix, | |
| 52 | latestUpdate, | |
| 53 | splitSecrets, | |
| 54 | worstSeverity, | |
| 55 | } from "../lib/security-alerts"; | |
| 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. | 56 | import { TimeAgo } from "./ui"; |
| 57 | import { Avatar } from "./ui/avatar"; | |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 58 | import { Badge, type BadgeTone } from "./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. | 59 | import { Button } from "./ui/button"; |
| 60 | import { Card } from "./ui/card"; | |
| Merge branch 'worktree-agent-ae1299e92e4462012' | 61 | import { Hint } from "./ui/hint"; |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 62 | import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from "./ui/dialog"; |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 63 | import { RadioGroup, RadioOption } from "./ui/radio-group"; |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 64 | |
| 65 | type Done = { ok: boolean; error?: string } | undefined; | |
| 66 | ||
| 67 | const SEVERITY: Record<Severity, { label: string; tone: BadgeTone }> = { | |
| 68 | critical: { label: "Critical", tone: "danger" }, | |
| 69 | high: { label: "High", tone: "warn" }, | |
| 70 | medium: { label: "Medium", tone: "merged" }, | |
| 71 | low: { label: "Low", tone: "info" }, | |
| 72 | unknown: { label: "Unrated", tone: "neutral" }, | |
| 73 | }; | |
| 74 | ||
| 75 | ||
| 76 | export function SeverityBadge({ severity }: { severity: Severity }) { | |
| 77 | return <Badge tone={SEVERITY[severity].tone}>{SEVERITY[severity].label}</Badge>; | |
| 78 | } | |
| 79 | ||
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 80 | /** Open alerts by severity, one tile each. */ |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 81 | export function SeverityCountsGrid({ counts }: { counts: SeverityCounts }) { |
| 82 | return ( | |
| 83 | <div className="grid grid-cols-2 gap-3 sm:grid-cols-5"> | |
| 84 | {SEVERITIES.map((severity) => ( | |
| 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. | 85 | <Card key={severity} className="px-4 py-3"> |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 86 | <p className="text-xs text-muted">{SEVERITY[severity].label}</p> |
| 87 | <p className={`mt-1 text-2xl font-semibold tabular-nums ${counts[severity] > 0 && severity === "critical" ? "text-danger" : ""}`}> | |
| 88 | {counts[severity]} | |
| 89 | </p> | |
| 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. | 90 | </Card> |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 91 | ))} |
| 92 | </div> | |
| 93 | ); | |
| 94 | } | |
| 95 | ||
| 96 | /** A compact row of severity counts, for a list of projects. */ | |
| 97 | export function SeverityCountsInline({ counts }: { counts: SeverityCounts }) { | |
| 98 | const shown = SEVERITIES.filter((severity) => counts[severity] > 0); | |
| 99 | if (shown.length === 0) { | |
| 100 | return ( | |
| Merge branch 'worktree-agent-ad7c6d88d93adc817' | 101 | <span className="inline-flex items-center gap-1 text-xs text-success"> |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 102 | <ShieldCheck size={13} /> |
| 103 | Nothing open | |
| 104 | </span> | |
| 105 | ); | |
| 106 | } | |
| 107 | return ( | |
| 108 | <span className="flex flex-wrap gap-1.5"> | |
| 109 | {shown.map((severity) => ( | |
| 110 | <Badge key={severity} tone={SEVERITY[severity].tone}> | |
| 111 | {counts[severity]} {SEVERITY[severity].label.toLowerCase()} | |
| 112 | </Badge> | |
| 113 | ))} | |
| 114 | </span> | |
| 115 | ); | |
| 116 | } | |
| 117 | ||
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 118 | const STATE_FILTERS: { state: AlertState; label: string; icon: ReactNode }[] = [ |
| 119 | { state: "open", label: "Open", icon: <CircleDot size={14} /> }, | |
| 120 | { state: "dismissed", label: "Dismissed", icon: <CircleSlash size={14} /> }, | |
| 121 | { state: "fixed", label: "Fixed", icon: <CircleCheck size={14} /> }, | |
| 122 | ]; | |
| 123 | ||
| 124 | /** Open, Dismissed and Fixed, with how many alerts each holds. */ | |
| 125 | export function StateFilter({ | |
| 126 | counts, | |
| 127 | value, | |
| 128 | onChange, | |
| 129 | }: { | |
| 130 | counts: Record<AlertState, number>; | |
| 131 | value: AlertState; | |
| 132 | onChange: (state: AlertState) => void; | |
| 133 | }) { | |
| 134 | return ( | |
| 135 | <div role="group" aria-label="Filter alerts" className="flex flex-wrap items-center gap-x-4 gap-y-1 text-sm"> | |
| 136 | {STATE_FILTERS.map(({ state, label, icon }) => ( | |
| 137 | <button | |
| 138 | key={state} | |
| 139 | type="button" | |
| 140 | aria-pressed={value === state} | |
| 141 | onClick={() => onChange(state)} | |
| 142 | className={`inline-flex items-center gap-1.5 transition-colors ${ | |
| 143 | value === state ? "font-medium text-fg" : "text-muted hover:text-fg" | |
| 144 | }`} | |
| 145 | > | |
| 146 | {icon} | |
| 147 | {counts[state]} {label} | |
| 148 | </button> | |
| 149 | ))} | |
| 150 | </div> | |
| 151 | ); | |
| 152 | } | |
| 153 | ||
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 154 | const TEXTAREA = |
| 155 | "w-full rounded-md border border-line bg-bg px-3 py-2 text-sm outline-none transition-colors placeholder:text-faint hover:border-line-strong focus:border-accent-dim"; | |
| 156 | ||
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 157 | const SMALL_BUTTON = |
| 158 | "rounded-md border border-line px-2.5 py-1 text-xs font-medium text-muted transition-colors hover:border-line-strong hover:text-fg disabled:opacity-50"; | |
| 159 | ||
| 160 | /** Dismiss an alert with one of `reasons` and an optional comment. */ | |
| Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar | 161 | export function DismissDialog({ |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 162 | id, |
| 163 | title, | |
| 164 | detail, | |
| 165 | reasons, | |
| 166 | note, | |
| 167 | action, | |
| 168 | defaultReason, | |
| 169 | trigger = "Dismiss", | |
| 170 | }: { | |
| 171 | id: string; | |
| 172 | title: string; | |
| 173 | /** What the alert is, in a line of code type. */ | |
| 174 | detail: string; | |
| 175 | reasons: { reason: DismissReason; label: string; about: string }[]; | |
| 176 | note?: string; | |
| 177 | action: string; | |
| 178 | defaultReason?: DismissReason; | |
| 179 | trigger?: string; | |
| 180 | }) { | |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 181 | const fetcher = useFetcher<Done>(); |
| 182 | const [open, setOpen] = useState(false); | |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 183 | const [reason, setReason] = useState<string>(defaultReason ?? ""); |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 184 | useEffect(() => { |
| 185 | if (fetcher.state === "idle" && fetcher.data?.ok) setOpen(false); | |
| 186 | }, [fetcher.state, fetcher.data]); | |
| 187 | return ( | |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 188 | <Dialog |
| 189 | open={open} | |
| 190 | onOpenChange={(next) => { | |
| 191 | setOpen(next); | |
| 192 | if (next) setReason(defaultReason ?? ""); | |
| 193 | }} | |
| 194 | > | |
| 195 | <DialogTrigger className={SMALL_BUTTON}>{trigger}</DialogTrigger> | |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 196 | <DialogContent> |
| 197 | <DialogHeader> | |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 198 | <DialogTitle>{title}</DialogTitle> |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 199 | <DialogDescription> |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 200 | Say why it can stay. The alert keeps your name, the reason and your comment, and anyone with access can reopen it. |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 201 | </DialogDescription> |
| 202 | </DialogHeader> | |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 203 | <fetcher.Form method="post" action={action} className="space-y-4"> |
| 204 | <input type="hidden" name="intent" value="dismiss" /> | |
| 205 | <input type="hidden" name="id" value={id} /> | |
| 206 | <p className="font-mono text-xs break-all text-muted">{detail}</p> | |
| 207 | <fieldset> | |
| 208 | <legend className="text-xs font-medium text-muted">Reason</legend> | |
| 209 | <RadioGroup name="reason" value={reason} onValueChange={setReason} required className="mt-2 gap-3"> | |
| 210 | {reasons.map((option) => ( | |
| 211 | <RadioOption key={option.reason} value={option.reason} label={option.label} description={option.about} /> | |
| 212 | ))} | |
| 213 | </RadioGroup> | |
| 214 | </fieldset> | |
| 215 | {note && <p className="rounded-md border border-warn/30 bg-warn/5 px-3 py-2 text-xs text-warn">{note}</p>} | |
| 216 | <label className="block"> | |
| 217 | <span className="mb-1.5 block text-xs font-medium text-muted">Comment (optional)</span> | |
| 218 | <textarea name="comment" rows={3} maxLength={500} placeholder="What someone reading this later should know." className={TEXTAREA} /> | |
| 219 | </label> | |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 220 | <div className="flex justify-end"> |
| 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. | 221 | <Button |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 222 | type="submit" |
| 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. | 223 | disabled={fetcher.state !== "idle" || !reason}> |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 224 | {fetcher.state !== "idle" ? "Dismissing…" : "Dismiss alert"} |
| 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. | 225 | </Button> |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 226 | </div> |
| 227 | {fetcher.data?.error && <p className="text-sm text-danger">{fetcher.data.error}</p>} | |
| 228 | </fetcher.Form> | |
| 229 | </DialogContent> | |
| 230 | </Dialog> | |
| 231 | ); | |
| 232 | } | |
| 233 | ||
| Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar | 234 | export function ReopenButton({ id, action }: { id: string; action: string }) { |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 235 | const fetcher = useFetcher<Done>(); |
| 236 | return ( | |
| 237 | <span className="flex flex-col items-end gap-1"> | |
| 238 | <button | |
| 239 | type="button" | |
| 240 | disabled={fetcher.state !== "idle"} | |
| 241 | onClick={() => fetcher.submit({ intent: "reopen", id }, { method: "post", action })} | |
| 242 | className={SMALL_BUTTON} | |
| 243 | > | |
| 244 | {fetcher.state !== "idle" ? "Reopening…" : "Reopen"} | |
| 245 | </button> | |
| 246 | {fetcher.data?.error && <span className="text-xs text-danger">{fetcher.data.error}</span>} | |
| 247 | </span> | |
| 248 | ); | |
| 249 | } | |
| 250 | ||
| 251 | /** What happened to an alert, oldest first, folded away until asked for. */ | |
| 252 | function ActivityLog({ entries, base, open }: { entries: ActivityEntry[]; base: string; open?: boolean }) { | |
| 253 | if (entries.length === 0) return null; | |
| 254 | return ( | |
| 255 | <details className="group mt-2" open={open}> | |
| 256 | <summary className="inline-flex cursor-pointer list-none items-center gap-1.5 text-xs text-muted hover:text-fg [&::-webkit-details-marker]:hidden"> | |
| 257 | <History size={12} /> | |
| 258 | <span className="group-open:hidden">Show activity ({entries.length})</span> | |
| 259 | <span className="hidden group-open:inline">Hide activity</span> | |
| 260 | </summary> | |
| 261 | <ol className="mt-2 space-y-2 border-l border-line pl-3"> | |
| 262 | {entries.map((entry) => ( | |
| 263 | <li key={entry.key} className="text-xs"> | |
| 264 | <p className="flex flex-wrap items-center gap-x-1.5 gap-y-1 text-muted"> | |
| 265 | {entry.actor ? ( | |
| 266 | <> | |
| 267 | <Avatar name={entry.actor} size={16} /> | |
| 268 | <span className="font-medium text-fg">{entry.actor}</span> | |
| 269 | </> | |
| 270 | ) : ( | |
| 271 | <CircleDot size={12} className="text-faint" /> | |
| 272 | )} | |
| 273 | <span>{entry.text}</span> | |
| 274 | {entry.ref && ( | |
| 275 | <Link | |
| 276 | to={`${base}/${entry.ref.kind === "pull" ? "pull" : "issues"}/${entry.ref.number}`} | |
| 277 | className="font-medium text-fg-soft hover:text-fg hover:underline" | |
| 278 | > | |
| 279 | #{entry.ref.number} | |
| 280 | </Link> | |
| 281 | )} | |
| 282 | {entry.reason && <Badge>{dismissLabel(entry.reason)}</Badge>} | |
| 283 | <span className="text-faint"> | |
| 284 | <TimeAgo at={entry.at} /> | |
| 285 | </span> | |
| 286 | </p> | |
| 287 | {entry.comment && <p className="mt-1 text-fg-soft wrap-anywhere">“{entry.comment}”</p>} | |
| 288 | </li> | |
| 289 | ))} | |
| 290 | </ol> | |
| 291 | </details> | |
| 292 | ); | |
| 293 | } | |
| 294 | ||
| 295 | function useFocus<T extends HTMLElement>(focused: boolean) { | |
| 296 | const ref = useRef<T>(null); | |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 297 | useEffect(() => { |
| 298 | if (focused) ref.current?.scrollIntoView({ block: "center" }); | |
| 299 | }, [focused]); | |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 300 | return ref; |
| 301 | } | |
| 302 | ||
| 303 | const FOCUSED = "bg-accent/5 ring-1 ring-accent/40 ring-inset"; | |
| 304 | ||
| 305 | function secretBadge(finding: SecretFinding): { label: string; tone: BadgeTone; about: string } { | |
| 306 | if (finding.state === "dismissed") { | |
| 307 | return { | |
| 308 | label: "Dismissed", | |
| 309 | tone: "neutral", | |
| 310 | about: finding.status === "allowed" ? "Pushes carrying it go through." : "Dismissed.", | |
| 311 | }; | |
| 312 | } | |
| Merge branch 'worktree-agent-ad7c6d88d93adc817' | 313 | if (finding.state === "fixed") return { label: "Revoked", tone: "success", about: "Revoked or rotated." }; |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 314 | if (finding.status === "blocked") { |
| 315 | return { label: "Push blocked", tone: finding.testValue ? "neutral" : "warn", about: "A push carrying it was refused, so it never landed." }; | |
| 316 | } | |
| 317 | return { | |
| 318 | label: "In history", | |
| 319 | tone: finding.testValue ? "neutral" : "danger", | |
| 320 | about: "In the repository's history. Rotate it with whoever issued it, then dismiss it as revoked.", | |
| 321 | }; | |
| 322 | } | |
| 323 | ||
| 324 | function SecretItem({ | |
| 325 | finding, | |
| 326 | activity, | |
| 327 | base, | |
| 328 | action, | |
| 329 | focused, | |
| 330 | canDismiss, | |
| 331 | }: { | |
| 332 | finding: SecretFinding; | |
| 333 | activity: AlertActivity[]; | |
| 334 | base: string; | |
| 335 | action: string; | |
| 336 | focused: boolean; | |
| 337 | canDismiss: boolean; | |
| 338 | }) { | |
| 339 | const ref = useFocus<HTMLLIElement>(focused); | |
| 340 | const badge = secretBadge(finding); | |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 341 | const landed = finding.source === "history" || finding.status === "open"; |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 342 | const where = `${finding.path}:${finding.line}`; |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 343 | return ( |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 344 | <li ref={ref} id={finding.id} className={`flex flex-col gap-3 px-4 py-3 sm:flex-row sm:items-start ${focused ? FOCUSED : ""}`}> |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 345 | <KeyRound size={15} className="mt-0.5 hidden shrink-0 text-muted sm:block" /> |
| 346 | <div className="min-w-0 grow"> | |
| 347 | <div className="flex flex-wrap items-center gap-2"> | |
| Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar | 348 | <Link to={`${base}/security/secret-scanning/${finding.id}`} className="text-sm font-medium first-letter:uppercase hover:underline"> |
| 349 | {finding.label} | |
| 350 | </Link> | |
| Merge branch 'worktree-agent-ae1299e92e4462012' | 351 | <Hint label={badge.about}> |
| 352 | <Badge tone={badge.tone}>{badge.label}</Badge> | |
| 353 | </Hint> | |
| 354 | {finding.testValue && ( | |
| 355 | <Hint label={finding.testValue}> | |
| 356 | <Badge>Likely test value</Badge> | |
| 357 | </Hint> | |
| 358 | )} | |
| Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar | 359 | {finding.validity === "active" && ( |
| Merge branch 'worktree-agent-ae1299e92e4462012' | 360 | <Hint label="Its issuer says it still works"> |
| 361 | <Badge tone="danger">Active</Badge> | |
| 362 | </Hint> | |
| Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar | 363 | )} |
| Merge branch 'worktree-agent-ae1299e92e4462012' | 364 | {finding.validity === "inactive" && ( |
| 365 | <Hint label="Its issuer refused it: revoked or expired"> | |
| 366 | <Badge>Inactive</Badge> | |
| 367 | </Hint> | |
| 368 | )} | |
| 369 | {finding.bypass && ( | |
| 370 | <Hint label={`Bypassed by ${finding.bypass.by}`}> | |
| 371 | <Badge tone="warn">Bypassed</Badge> | |
| 372 | </Hint> | |
| 373 | )} | |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 374 | {finding.state === "dismissed" && finding.dismissedReason && <Badge>{dismissLabel(finding.dismissedReason)}</Badge>} |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 375 | </div> |
| 376 | <p className="mt-1 truncate font-mono text-xs"> | |
| 377 | {landed ? ( | |
| 378 | <Link to={`${base}/blob/${finding.commit}/${finding.path}#L${finding.line}`} className="text-fg-soft hover:text-fg hover:underline"> | |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 379 | {where} |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 380 | </Link> |
| 381 | ) : ( | |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 382 | <span className="text-fg-soft">{where}</span> |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 383 | )} |
| 384 | <span className="text-faint"> · {finding.preview}</span> | |
| 385 | </p> | |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 386 | {finding.testValue && <p className="mt-1 text-xs text-muted">{finding.testValue}</p>} |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 387 | <p className="mt-1.5 flex flex-wrap gap-x-3 gap-y-1 text-xs text-faint"> |
| 388 | <span> | |
| 389 | {finding.source === "push" ? "in a push" : "in history"} | |
| 390 | {finding.foundBy && <> by {finding.foundBy}</>}, commit{" "} | |
| 391 | {landed ? ( | |
| 392 | <Link to={`${base}/commit/${finding.commit}`} className="font-mono hover:text-fg"> | |
| 393 | {finding.commit.slice(0, 7)} | |
| 394 | </Link> | |
| 395 | ) : ( | |
| 396 | <span className="font-mono">{finding.commit.slice(0, 7)}</span> | |
| 397 | )} | |
| 398 | </span> | |
| 399 | <span> | |
| 400 | found <TimeAgo at={finding.foundAt} /> | |
| 401 | </span> | |
| 402 | </p> | |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 403 | <ActivityLog entries={alertActivity(finding, activity)} base={base} open={focused && finding.state !== "open"} /> |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 404 | </div> |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 405 | {canDismiss && ( |
| 406 | <div className="flex shrink-0 items-center gap-1.5"> | |
| 407 | {finding.state === "open" ? ( | |
| 408 | <DismissDialog | |
| 409 | id={finding.id} | |
| 410 | title={`Dismiss ${finding.label}`} | |
| 411 | detail={`${where} · ${finding.preview}`} | |
| 412 | reasons={SECRET_DISMISS_REASONS} | |
| 413 | note={ | |
| 414 | finding.status === "blocked" && !finding.testValue | |
| 415 | ? "Dismissing it lets the same push through, unless you dismiss it as revoked." | |
| 416 | : undefined | |
| 417 | } | |
| 418 | action={action} | |
| 419 | /> | |
| 420 | ) : ( | |
| 421 | <ReopenButton id={finding.id} action={action} /> | |
| 422 | )} | |
| 423 | </div> | |
| 424 | )} | |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 425 | </li> |
| 426 | ); | |
| 427 | } | |
| 428 | ||
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 429 | function Empty({ title, children }: { title: string; children: ReactNode }) { |
| 430 | return ( | |
| 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. | 431 | <Card tone="plain" className="border-dashed px-6 py-10 text-center"> |
| Merge branch 'worktree-agent-ad7c6d88d93adc817' | 432 | <ShieldCheck size={22} className="mx-auto text-success" /> |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 433 | <p className="mt-2 font-medium">{title}</p> |
| 434 | <p className="mt-1 text-sm text-muted">{children}</p> | |
| 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. | 435 | </Card> |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 436 | ); |
| 437 | } | |
| 438 | ||
| 439 | const LIST = "divide-y divide-line overflow-hidden rounded-xl border border-line bg-surface"; | |
| 440 | ||
| 441 | /** The secret alerts in one state: on Open, real ones first, then likely test values. */ | |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 442 | export function SecretsList({ |
| 443 | secrets, | |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 444 | state, |
| 445 | activity, | |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 446 | base, |
| 447 | action, | |
| 448 | focus, | |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 449 | canDismiss, |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 450 | }: { |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 451 | /** Already filtered to `state`. */ |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 452 | secrets: SecretFinding[]; |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 453 | state: AlertState; |
| 454 | activity: AlertActivity[]; | |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 455 | base: string; |
| 456 | action: string; | |
| 457 | focus: string | null; | |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 458 | /** Whether the viewer may dismiss and reopen secret alerts (Admin). */ |
| 459 | canDismiss: boolean; | |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 460 | }) { |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 461 | const item = (finding: SecretFinding) => ( |
| 462 | <SecretItem | |
| 463 | key={finding.id} | |
| 464 | finding={finding} | |
| 465 | activity={activity} | |
| 466 | base={base} | |
| 467 | action={action} | |
| 468 | focused={finding.id === focus} | |
| 469 | canDismiss={canDismiss} | |
| 470 | /> | |
| 471 | ); | |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 472 | if (secrets.length === 0) { |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 473 | return state === "open" ? ( |
| 474 | <Empty title="No open secret alerts"> | |
| 475 | Pushes that add a key or a token are refused before they land, and the history is scanned once in the background. | |
| 476 | </Empty> | |
| 477 | ) : ( | |
| 478 | <Empty title={state === "dismissed" ? "No dismissed secret alerts" : "No revoked secrets"}> | |
| 479 | {state === "dismissed" | |
| 480 | ? "Alerts someone dismissed as a false positive, a test value or accepted are listed here." | |
| 481 | : "Secrets dismissed as revoked are listed here."} | |
| 482 | </Empty> | |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 483 | ); |
| 484 | } | |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 485 | if (state !== "open") return <ul className={LIST}>{secrets.map(item)}</ul>; |
| 486 | const { real, tests } = splitSecrets(secrets); | |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 487 | return ( |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 488 | <div className="space-y-5"> |
| 489 | {real.length > 0 ? ( | |
| 490 | <ul className={LIST}>{real.map(item)}</ul> | |
| 491 | ) : ( | |
| 492 | <Empty title="No secrets that look real">Only likely test values are open, and they never block a push.</Empty> | |
| 493 | )} | |
| 494 | {tests.length > 0 && ( | |
| 495 | <section> | |
| 496 | <h4 className="text-sm font-medium">Likely test values</h4> | |
| 497 | <p className="mt-0.5 mb-2 text-xs text-muted"> | |
| 498 | These look made for tests or documentation. They never block a push and are not counted as critical; dismiss them | |
| 499 | to clear the list. | |
| 500 | </p> | |
| 501 | <ul className={LIST}>{tests.map(item)}</ul> | |
| 502 | </section> | |
| 503 | )} | |
| 504 | </div> | |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 505 | ); |
| 506 | } | |
| 507 | ||
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 508 | /** Where the legacy upgrade issue for a package stands, as the page loads it. */ |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 509 | export type UpgradeFix = { |
| 510 | number: number; | |
| 511 | state: "open" | "closed"; | |
| 512 | /** The newest pull request for the issue, if any. */ | |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 513 | pull: { number: number; status: PullStatus; agent: string | null } | null; |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 514 | resolvedBy: number | null; |
| 515 | }; | |
| 516 | ||
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 517 | /** A security update's pull request as the page loads it. */ |
| 518 | export type PullInfo = { number: number; status: PullStatus; title: string }; | |
| 519 | ||
| 520 | function FixLink({ issue, fix, base }: { issue: number; fix: UpgradeFix | undefined; base: string }) { | |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 521 | const pull = fix?.pull; |
| 522 | return ( | |
| 523 | <span className="flex flex-wrap items-center gap-x-2 gap-y-1 text-xs"> | |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 524 | <span className="text-muted">Upgrade issue</span> |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 525 | <Link to={`${base}/issues/${issue}`} className="inline-flex items-center gap-1 text-fg-soft hover:text-fg"> |
| Merge branch 'worktree-agent-ad7c6d88d93adc817' | 526 | {fix?.state === "closed" ? <CircleCheck size={12} className="text-merged" /> : <CircleDot size={12} className="text-success" />}#{issue} |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 527 | </Link> |
| 528 | {pull && ( | |
| 529 | <Link to={`${base}/pull/${pull.number}`} className="inline-flex items-center gap-1 text-muted hover:text-fg"> | |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 530 | {pull.agent ? <Bot size={12} /> : <GitPullRequest size={12} />}#{pull.number} {pull.status === "draft" ? "in progress" : pull.status} |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 531 | </Link> |
| 532 | )} | |
| 533 | </span> | |
| 534 | ); | |
| 535 | } | |
| 536 | ||
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 537 | const PULL_ICON: Record<PullStatus, ReactNode> = { |
| 538 | draft: <GitPullRequest size={13} className="text-muted" />, | |
| Merge branch 'worktree-agent-ad7c6d88d93adc817' | 539 | open: <GitPullRequest size={13} className="text-success" />, |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 540 | merged: <GitMerge size={13} className="text-merged" />, |
| 541 | closed: <GitPullRequestClosed size={13} className="text-danger" />, | |
| 542 | }; | |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 543 | |
| A desktop release goes out with what can be built. Linux is built on g1t's machines in every release; Windows and macOS each wait on a repository variable: a machine registered for them, or for Windows a cross build on Linux once aka.ms and download.visualstudio.microsoft.com are workflow-only domains, which is where the first release stopped. A platform with no machine is skipped and the download page says it is coming, with a word for the visitor whose platform this release lacks. The guide and CONTRIBUTING say so. | 544 | /** |
| 545 | * Where g1t's security update for a package stands. On a mirror g1t does | |
| 546 | * not lead (`updatesWait`, the reason), an update that has not opened a | |
| 547 | * pull request waits: nothing is made or pushed there until a takeover. | |
| 548 | */ | |
| 549 | function UpdateStatus({ update, name, pulls, base, updatesWait }: { | |
| 550 | update: SecurityUpdate; | |
| 551 | name: string; | |
| 552 | pulls: Record<number, PullInfo>; | |
| 553 | base: string; | |
| 554 | updatesWait?: string | null; | |
| 555 | }) { | |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 556 | const meta = UPDATE_STATES[update.state]; |
| 557 | const pull = update.pull != null ? pulls[update.pull] : undefined; | |
| A desktop release goes out with what can be built. Linux is built on g1t's machines in every release; Windows and macOS each wait on a repository variable: a machine registered for them, or for Windows a cross build on Linux once aka.ms and download.visualstudio.microsoft.com are workflow-only domains, which is where the first release stopped. A platform with no machine is skipped and the download page says it is coming, with a word for the visitor whose platform this release lacks. The guide and CONTRIBUTING say so. | 558 | if (updatesWait && (update.state === "requested" || update.state === "failed" || update.state === "needs_code")) { |
| 559 | return ( | |
| 560 | <Card tone="plain" radius="lg" className="mt-2.5 bg-bg/40 px-3 py-2 text-xs sm:ml-7"> | |
| 561 | <div className="flex flex-wrap items-center gap-x-2.5 gap-y-1.5"> | |
| 562 | <PauseCircle size={13} className="text-muted" /> | |
| 563 | <Badge>Security update waiting</Badge> | |
| 564 | <span className="text-faint"> | |
| 565 | <TimeAgo at={update.updatedAt} /> | |
| 566 | </span> | |
| 567 | </div> | |
| 568 | <p className="mt-1 text-muted wrap-anywhere"> | |
| 569 | Raising {name} to {update.target} is on hold until {base.replace(/^\//, "")} is taken over or moved to g1t. | |
| 570 | </p> | |
| 571 | </Card> | |
| 572 | ); | |
| 573 | } | |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 574 | const about: Record<SecurityUpdate["state"], ReactNode> = { |
| 575 | requested: <>A sandbox is raising {name} to {update.target}.</>, | |
| 576 | open: <>It raises {name} to {update.target} and lands through your branch's required checks.</>, | |
| 577 | merged: <>{name} was raised to {update.target}.</>, | |
| 578 | closed: <>The pull request was closed without merging.</>, | |
| 579 | superseded: <>A newer update replaced it, or the package is no longer vulnerable, so g1t closed it.</>, | |
| 580 | needs_code: ( | |
| 581 | <> | |
| g1t is one name: its agent's work, commits and comments show as @g1t, and nobody can claim g1t or g1t-agent | 582 | Raising {name} to {update.target} needs code changes, so g1t opened an issue to make them |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 583 | {update.issue != null && ( |
| 584 | <> | |
| 585 | :{" "} | |
| 586 | <Link to={`${base}/issues/${update.issue}`} className="font-medium text-fg-soft hover:text-fg hover:underline"> | |
| 587 | #{update.issue} | |
| 588 | </Link> | |
| 589 | </> | |
| 590 | )} | |
| 591 | . | |
| 592 | </> | |
| 593 | ), | |
| 594 | failed: <>{update.error ?? "g1t could not make the change."}</>, | |
| 595 | }; | |
| 596 | return ( | |
| 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. | 597 | <Card tone="plain" radius="lg" className="mt-2.5 bg-bg/40 px-3 py-2 text-xs sm:ml-7"> |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 598 | <div className="flex flex-wrap items-center gap-x-2.5 gap-y-1.5"> |
| 599 | {update.state === "requested" ? ( | |
| 600 | <Loader size={13} className="animate-spin text-info motion-reduce:animate-none" /> | |
| 601 | ) : update.state === "failed" || update.state === "needs_code" ? ( | |
| 602 | <CircleAlert size={13} className={update.state === "failed" ? "text-danger" : "text-warn"} /> | |
| 603 | ) : ( | |
| 604 | PULL_ICON[pull?.status ?? (update.state === "merged" ? "merged" : update.state === "open" ? "open" : "closed")] | |
| 605 | )} | |
| 606 | <Badge tone={meta.tone}>{meta.label}</Badge> | |
| 607 | {update.pull != null && ( | |
| 608 | <Link to={`${base}/pull/${update.pull}`} className="font-medium text-fg-soft hover:text-fg hover:underline"> | |
| 609 | #{update.pull} | |
| 610 | {pull && <span className="font-normal text-muted"> {pull.status === "draft" ? "draft" : pull.status}</span>} | |
| 611 | </Link> | |
| 612 | )} | |
| 613 | {update.branch && ( | |
| 614 | <span className="inline-flex min-w-0 items-center gap-1 font-mono text-muted"> | |
| 615 | <GitBranch size={12} className="shrink-0" /> | |
| 616 | <span className="truncate">{update.branch}</span> | |
| 617 | </span> | |
| 618 | )} | |
| 619 | <span className="text-faint"> | |
| 620 | <TimeAgo at={update.updatedAt} /> | |
| 621 | </span> | |
| 622 | </div> | |
| 623 | <p className={`mt-1 ${update.state === "failed" ? "text-danger" : "text-muted"} wrap-anywhere`}>{about[update.state]}</p> | |
| 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. | 624 | </Card> |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 625 | ); |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 626 | } |
| 627 | ||
| 628 | export function VulnerabilityList({ | |
| 629 | vulnerabilities, | |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 630 | state, |
| 631 | activity, | |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 632 | fixes, |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 633 | pulls, |
| 634 | upkeep, | |
| A desktop release goes out with what can be built. Linux is built on g1t's machines in every release; Windows and macOS each wait on a repository variable: a machine registered for them, or for Windows a cross build on Linux once aka.ms and download.visualstudio.microsoft.com are workflow-only domains, which is where the first release stopped. A platform with no machine is skipped and the download page says it is coming, with a word for the visitor whose platform this release lacks. The guide and CONTRIBUTING say so. | 635 | updatesWait, |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 636 | base, |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 637 | action, |
| 638 | focus, | |
| 639 | canDismiss, | |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 640 | }: { |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 641 | /** Already filtered to `state`. */ |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 642 | vulnerabilities: Vulnerability[]; |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 643 | state: AlertState; |
| 644 | activity: AlertActivity[]; | |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 645 | fixes: Record<number, UpgradeFix>; |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 646 | pulls: Record<number, PullInfo>; |
| 647 | /** Whether security updates are on. */ | |
| 648 | upkeep: boolean; | |
| A desktop release goes out with what can be built. Linux is built on g1t's machines in every release; Windows and macOS each wait on a repository variable: a machine registered for them, or for Windows a cross build on Linux once aka.ms and download.visualstudio.microsoft.com are workflow-only domains, which is where the first release stopped. A platform with no machine is skipped and the download page says it is coming, with a word for the visitor whose platform this release lacks. The guide and CONTRIBUTING say so. | 649 | /** Why no update opens now: the repository is a mirror g1t does not lead (lib/mirror.ts). */ |
| 650 | updatesWait?: string | null; | |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 651 | base: string; |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 652 | action: string; |
| 653 | focus: string | null; | |
| 654 | /** Whether the viewer may dismiss and reopen dependency alerts (Write). */ | |
| 655 | canDismiss: boolean; | |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 656 | }) { |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 657 | const packages = groupByPackage(vulnerabilities); |
| 658 | if (packages.length === 0) { | |
| 659 | return state === "open" ? ( | |
| 660 | <Empty title="No known vulnerabilities"> | |
| 661 | Every package the lockfiles resolve is checked against the OSV database on each push to the default branch, and daily. | |
| 662 | </Empty> | |
| 663 | ) : ( | |
| 664 | <Empty title={state === "dismissed" ? "No dismissed dependency alerts" : "Nothing fixed yet"}> | |
| 665 | {state === "dismissed" | |
| 666 | ? "Alerts someone dismissed, with their reason, are listed here." | |
| 667 | : "Alerts whose package was upgraded, or is no longer vulnerable, are listed here."} | |
| 668 | </Empty> | |
| 669 | ); | |
| 670 | } | |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 671 | return ( |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 672 | <ul className={LIST}> |
| 673 | {packages.map((group) => ( | |
| 674 | <PackageItem | |
| 675 | key={group.key} | |
| 676 | group={group} | |
| 677 | activity={activity} | |
| 678 | fixes={fixes} | |
| 679 | pulls={pulls} | |
| 680 | upkeep={upkeep} | |
| A desktop release goes out with what can be built. Linux is built on g1t's machines in every release; Windows and macOS each wait on a repository variable: a machine registered for them, or for Windows a cross build on Linux once aka.ms and download.visualstudio.microsoft.com are workflow-only domains, which is where the first release stopped. A platform with no machine is skipped and the download page says it is coming, with a word for the visitor whose platform this release lacks. The guide and CONTRIBUTING say so. | 681 | updatesWait={updatesWait} |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 682 | base={base} |
| 683 | action={action} | |
| 684 | focus={focus} | |
| 685 | canDismiss={canDismiss} | |
| 686 | /> | |
| 687 | ))} | |
| 688 | </ul> | |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 689 | ); |
| 690 | } | |
| 691 | ||
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 692 | function PackageItem({ |
| 693 | group, | |
| 694 | activity, | |
| 695 | fixes, | |
| 696 | pulls, | |
| 697 | upkeep, | |
| A desktop release goes out with what can be built. Linux is built on g1t's machines in every release; Windows and macOS each wait on a repository variable: a machine registered for them, or for Windows a cross build on Linux once aka.ms and download.visualstudio.microsoft.com are workflow-only domains, which is where the first release stopped. A platform with no machine is skipped and the download page says it is coming, with a word for the visitor whose platform this release lacks. The guide and CONTRIBUTING say so. | 698 | updatesWait, |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 699 | base, |
| 700 | action, | |
| 701 | focus, | |
| 702 | canDismiss, | |
| 703 | }: { | |
| 704 | group: PackageGroup; | |
| 705 | activity: AlertActivity[]; | |
| 706 | fixes: Record<number, UpgradeFix>; | |
| 707 | pulls: Record<number, PullInfo>; | |
| 708 | upkeep: boolean; | |
| A desktop release goes out with what can be built. Linux is built on g1t's machines in every release; Windows and macOS each wait on a repository variable: a machine registered for them, or for Windows a cross build on Linux once aka.ms and download.visualstudio.microsoft.com are workflow-only domains, which is where the first release stopped. A platform with no machine is skipped and the download page says it is coming, with a word for the visitor whose platform this release lacks. The guide and CONTRIBUTING say so. | 709 | updatesWait?: string | null; |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 710 | base: string; |
| 711 | action: string; | |
| 712 | focus: string | null; | |
| 713 | canDismiss: boolean; | |
| 714 | }) { | |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 715 | const versions = [...new Set(group.vulns.map((vuln) => vuln.version))]; |
| 716 | const manifests = [...new Set(group.vulns.map((vuln) => vuln.manifest))]; | |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 717 | const target = highestFix(group.vulns); |
| 718 | const update = latestUpdate(group.vulns); | |
| 719 | const issue = update ? null : (group.vulns.find((vuln) => vuln.issue != null)?.issue ?? null); | |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 720 | return ( |
| 721 | <li className="px-4 py-3"> | |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 722 | <div className="flex items-start gap-3"> |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 723 | <Package size={15} className="mt-0.5 hidden shrink-0 text-muted sm:block" /> |
| 724 | <div className="min-w-0 grow"> | |
| 725 | <div className="flex flex-wrap items-center gap-2"> | |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 726 | <span className="font-mono text-sm font-medium break-all">{group.name}</span> |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 727 | <span className="font-mono text-xs text-muted">{versions.join(", ")}</span> |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 728 | <Badge>{group.ecosystem}</Badge> |
| 729 | <SeverityBadge severity={worstSeverity(group.vulns)} /> | |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 730 | </div> |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 731 | <p className="mt-1 text-xs text-faint wrap-anywhere"> |
| 732 | {target ? <>Fixed in {target}</> : "No patched version"} · locked in{" "} | |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 733 | <span className="font-mono">{manifests.join(", ")}</span> |
| 734 | </p> | |
| 735 | </div> | |
| 736 | </div> | |
| A desktop release goes out with what can be built. Linux is built on g1t's machines in every release; Windows and macOS each wait on a repository variable: a machine registered for them, or for Windows a cross build on Linux once aka.ms and download.visualstudio.microsoft.com are workflow-only domains, which is where the first release stopped. A platform with no machine is skipped and the download page says it is coming, with a word for the visitor whose platform this release lacks. The guide and CONTRIBUTING say so. | 737 | {update && <UpdateStatus update={update} name={group.name} pulls={pulls} base={base} updatesWait={updatesWait} />} |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 738 | {issue != null && ( |
| 739 | <div className="mt-2 sm:ml-7"> | |
| 740 | <FixLink issue={issue} fix={fixes[issue]} base={base} /> | |
| 741 | </div> | |
| 742 | )} | |
| 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. | 743 | <Card asChild tone="plain" radius="lg" className="mt-2.5 space-y-px overflow-hidden sm:ml-7"> |
| 744 | <ul> | |
| 745 | {group.vulns.map((vuln) => ( | |
| 746 | <AdvisoryItem | |
| 747 | key={vuln.id} | |
| 748 | vuln={vuln} | |
| 749 | showManifest={manifests.length > 1} | |
| 750 | activity={activity} | |
| 751 | upkeep={upkeep} | |
| A desktop release goes out with what can be built. Linux is built on g1t's machines in every release; Windows and macOS each wait on a repository variable: a machine registered for them, or for Windows a cross build on Linux once aka.ms and download.visualstudio.microsoft.com are workflow-only domains, which is where the first release stopped. A platform with no machine is skipped and the download page says it is coming, with a word for the visitor whose platform this release lacks. The guide and CONTRIBUTING say so. | 752 | updatesWait={updatesWait} |
| 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. | 753 | base={base} |
| 754 | action={action} | |
| 755 | focused={vuln.id === focus} | |
| 756 | canDismiss={canDismiss} | |
| 757 | /> | |
| 758 | ))} | |
| 759 | </ul> | |
| 760 | </Card> | |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 761 | </li> |
| 762 | ); | |
| 763 | } | |
| 764 | ||
| 765 | function AdvisoryItem({ | |
| 766 | vuln, | |
| 767 | showManifest, | |
| 768 | activity, | |
| 769 | upkeep, | |
| A desktop release goes out with what can be built. Linux is built on g1t's machines in every release; Windows and macOS each wait on a repository variable: a machine registered for them, or for Windows a cross build on Linux once aka.ms and download.visualstudio.microsoft.com are workflow-only domains, which is where the first release stopped. A platform with no machine is skipped and the download page says it is coming, with a word for the visitor whose platform this release lacks. The guide and CONTRIBUTING say so. | 770 | updatesWait, |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 771 | base, |
| 772 | action, | |
| 773 | focused, | |
| 774 | canDismiss, | |
| 775 | }: { | |
| 776 | vuln: Vulnerability; | |
| 777 | showManifest: boolean; | |
| 778 | activity: AlertActivity[]; | |
| 779 | upkeep: boolean; | |
| A desktop release goes out with what can be built. Linux is built on g1t's machines in every release; Windows and macOS each wait on a repository variable: a machine registered for them, or for Windows a cross build on Linux once aka.ms and download.visualstudio.microsoft.com are workflow-only domains, which is where the first release stopped. A platform with no machine is skipped and the download page says it is coming, with a word for the visitor whose platform this release lacks. The guide and CONTRIBUTING say so. | 780 | updatesWait?: string | null; |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 781 | base: string; |
| 782 | action: string; | |
| 783 | focused: boolean; | |
| 784 | canDismiss: boolean; | |
| 785 | }) { | |
| 786 | const ref = useFocus<HTMLLIElement>(focused); | |
| 787 | const osv = `https://osv.dev/vulnerability/${vuln.osvId}`; | |
| 788 | const dismiss = (reason?: DismissReason, trigger?: string) => ( | |
| 789 | <DismissDialog | |
| 790 | id={vuln.id} | |
| 791 | title={`Dismiss ${vuln.advisory}`} | |
| 792 | detail={`${vuln.package} ${vuln.version} · ${vuln.manifest}`} | |
| 793 | reasons={DEPENDENCY_DISMISS_REASONS} | |
| 794 | action={action} | |
| 795 | defaultReason={reason} | |
| 796 | trigger={trigger} | |
| 797 | /> | |
| 798 | ); | |
| 799 | return ( | |
| 800 | <li ref={ref} id={vuln.id} className={`bg-bg/30 px-3 py-2.5 not-first:border-t not-first:border-line ${focused ? FOCUSED : ""}`}> | |
| 801 | <div className="flex flex-col gap-2 sm:flex-row sm:items-start"> | |
| 802 | <div className="min-w-0 grow text-xs"> | |
| 803 | <p className="flex flex-wrap items-center gap-x-2 gap-y-1"> | |
| 804 | <a href={osv} target="_blank" rel="noreferrer" className="inline-flex items-center gap-1 font-mono text-fg-soft hover:text-fg"> | |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 805 | {vuln.advisory} |
| 806 | <ExternalLink size={10} /> | |
| 807 | </a> | |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 808 | <SeverityBadge severity={vuln.severity} /> |
| 809 | {vuln.fixedVersion && <span className="text-faint">fixed in {vuln.fixedVersion}</span>} | |
| 810 | {showManifest && <span className="font-mono text-faint">{vuln.manifest}</span>} | |
| 811 | {vuln.state === "dismissed" && vuln.dismissedReason && <Badge>{dismissLabel(vuln.dismissedReason)}</Badge>} | |
| 812 | </p> | |
| 813 | <p className="mt-1 text-muted wrap-anywhere">{vuln.summary}</p> | |
| 814 | </div> | |
| 815 | {canDismiss && vuln.state !== "fixed" && ( | |
| 816 | <div className="flex shrink-0 items-center gap-1.5"> | |
| 817 | {vuln.state === "open" ? dismiss() : <ReopenButton id={vuln.id} action={action} />} | |
| 818 | </div> | |
| 819 | )} | |
| 820 | </div> | |
| 821 | {vuln.state === "open" && !vuln.fixedVersion && ( | |
| 822 | <div className="mt-2 rounded-md border border-warn/30 bg-warn/5 px-3 py-2 text-xs"> | |
| 823 | <p className="flex items-center gap-1.5 font-medium text-warn"> | |
| 824 | <ShieldAlert size={13} /> | |
| 825 | No patched version available | |
| 826 | </p> | |
| 827 | <p className="mt-1 text-muted"> | |
| 828 | No release fixes this yet. Dependencies are checked again daily | |
| A desktop release goes out with what can be built. Linux is built on g1t's machines in every release; Windows and macOS each wait on a repository variable: a machine registered for them, or for Windows a cross build on Linux once aka.ms and download.visualstudio.microsoft.com are workflow-only domains, which is where the first release stopped. A platform with no machine is skipped and the download page says it is coming, with a word for the visitor whose platform this release lacks. The guide and CONTRIBUTING say so. | 829 | {updatesWait |
| 830 | ? ". A fix is applied once the repository is taken over or moved to g1t." | |
| 831 | : upkeep | |
| 832 | ? ", and g1t opens a pull request when a fix is published." | |
| 833 | : "; turn on security updates and g1t opens a pull request when a fix is published."} | |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 834 | </p> |
| 835 | <div className="mt-2 flex flex-wrap items-center gap-2"> | |
| 836 | <a href={osv} target="_blank" rel="noreferrer" className="inline-flex items-center gap-1 text-fg-soft hover:text-fg hover:underline"> | |
| 837 | Read the advisory | |
| 838 | <ExternalLink size={10} /> | |
| 839 | </a> | |
| 840 | {canDismiss && dismiss("tolerable_risk", "Dismiss as tolerable risk")} | |
| 841 | </div> | |
| 842 | </div> | |
| 843 | )} | |
| 844 | <ActivityLog entries={alertActivity(vuln, activity)} base={base} open={focused && vuln.state !== "open"} /> | |
| Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put | 845 | </li> |
| 846 | ); | |
| 847 | } | |
| 848 | ||
| 849 | export function ScanSummary({ | |
| 850 | scan, | |
| 851 | }: { | |
| 852 | scan: { history: string; commitsScanned: number; historyFinishedAt: string | null; dependenciesScannedAt: string | null; dependenciesError: string | null; lockfiles: string[] }; | |
| 853 | }) { | |
| 854 | const history = | |
| 855 | scan.history === "done" ? ( | |
| 856 | <> | |
| 857 | History scanned: {scan.commitsScanned.toLocaleString()} commits | |
| 858 | {scan.historyFinishedAt && ( | |
| 859 | <> | |
| 860 | , <TimeAgo at={scan.historyFinishedAt} /> | |
| 861 | </> | |
| 862 | )} | |
| 863 | </> | |
| 864 | ) : scan.history === "stopped" ? ( | |
| 865 | "History scan paused: the workspace reached its spending limit" | |
| 866 | ) : scan.history === "running" ? ( | |
| 867 | `Scanning history: ${scan.commitsScanned.toLocaleString()} commits so far` | |
| 868 | ) : ( | |
| 869 | "History scan queued" | |
| 870 | ); | |
| 871 | return ( | |
| 872 | <div className="flex flex-wrap gap-x-5 gap-y-1 text-xs text-muted"> | |
| 873 | <span className="inline-flex items-center gap-1.5"> | |
| 874 | <KeyRound size={12} /> | |
| 875 | {history} | |
| 876 | </span> | |
| 877 | <span className="inline-flex items-center gap-1.5"> | |
| 878 | <Package size={12} /> | |
| 879 | {scan.dependenciesScannedAt ? ( | |
| 880 | <> | |
| 881 | Dependencies read <TimeAgo at={scan.dependenciesScannedAt} /> | |
| 882 | {scan.lockfiles.length > 0 ? <> from {scan.lockfiles.join(", ")}</> : " (no lockfiles found)"} | |
| 883 | </> | |
| 884 | ) : ( | |
| 885 | "Dependencies not read yet" | |
| 886 | )} | |
| 887 | </span> | |
| 888 | {scan.dependenciesError && ( | |
| 889 | <span className="inline-flex items-center gap-1.5 text-warn"> | |
| 890 | <ShieldAlert size={12} /> | |
| 891 | {scan.dependenciesError} | |
| 892 | </span> | |
| Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily | 893 | )} |
| 894 | </div> | |
| 895 | ); | |
| 896 | } |
This file's history is long; its oldest lines are credited to the oldest commit read.