Skip to content
167 linesCodeBlameRaw

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 bar1import { Link, useFetcher } from "react-router";
2
3import { CODE_SCANNING_GATES, type CodeScanningGate, type RepoSecuritySettings, type ReviewFailOn } from "@g1t/contracts";
4
5import type { Route } from "./+types/security-settings";
6import { page } from "../../lib/meta";
7import { ActivationPrompt, CARD, SectionHeader } from "../../components/security-suite";
Merge branch 'worktree-agent-ae1299e92e4462012'8import { Hint } from "../../components/ui/hint";
Chat controls, public profiles, shadcn selects, and no Docs tab in a project9import { SelectField } from "../../components/ui/select";
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar10import { Switch } from "../../components/ui/switch";
11import { securitySuite } from "../../lib/services.server";
Merge main (membership, two-factor, GitHub repo roles) into tokens12import { assertSameOrigin, getViewer, managesSecurity, requireUser, unwrap } from "../../lib/session.server";
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar13import { refusal, requireInsider } from "../../lib/access.server";
14import { whyNot } from "../../lib/access";
15import { activationPrice } from "../../lib/security-suite.server";
16
17export function meta({ params, ...args }: Route.MetaArgs) {
18 return page(args, { title: `Security settings · ${params.owner}/${params.repo} · g1t` });
19}
20
21export async function loader({ params, context, request }: Route.LoaderArgs) {
22 const viewer = getViewer(context) ?? requireUser(context, request);
23 const { access } = await requireInsider(context, params, "push");
24 const repo = { namespace: params.owner, name: params.repo };
25 const [view, price] = await Promise.all([securitySuite.settings(repo, viewer), activationPrice(params.owner, viewer)]);
Merge main (membership, two-factor, GitHub repo roles) into tokens26 return { view: unwrap(view), price, can: access.can, owner: managesSecurity(viewer, params.owner) };
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar27}
28
29const GATES = new Set(CODE_SCANNING_GATES.map((gate) => gate.gate));
30const FAIL_ON = new Set(["critical", "high", "medium", "low", "none"]);
31
32/** The settings a form posts. */
33export function settingsFrom(form: FormData): RepoSecuritySettings | string {
34 const gate = String(form.get("codeScanningGate") ?? "");
35 const failOn = String(form.get("reviewFailOn") ?? "");
36 if (!GATES.has(gate as CodeScanningGate)) return "Choose when code scanning fails.";
37 if (!FAIL_ON.has(failOn)) return "Choose when dependency review fails.";
38 return {
39 codeScanningGate: gate as CodeScanningGate,
40 dependencyReview: form.get("dependencyReview") === "on",
41 reviewFailOn: failOn as ReviewFailOn,
42 reviewDenyLicenses: String(form.get("reviewDenyLicenses") ?? "")
43 .split(/[\s,]+/)
44 .map((id) => id.trim())
45 .filter(Boolean)
46 .slice(0, 50),
47 reviewComment: form.get("reviewComment") === "on",
48 };
49}
50
51export async function action({ params, context, request }: Route.ActionArgs) {
52 assertSameOrigin(request);
53 const user = requireUser(context, request);
54 const refused = await refusal(context, params, "manage_settings");
55 if (refused) return { ok: false, error: refused };
56 const settings = settingsFrom(await request.formData());
57 if (typeof settings === "string") return { ok: false, error: settings };
58 const saved = await securitySuite.setSettings(user, { namespace: params.owner, name: params.repo }, settings);
59 return saved.ok ? { ok: true } : { ok: false, error: saved.error.message };
60}
61
Chat controls, public profiles, shadcn selects, and no Docs tab in a project62const SELECT = "w-full sm:w-72";
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar63
64export default function SecuritySettings({ loaderData, params }: Route.ComponentProps) {
65 const { view, price, can, owner } = loaderData;
66 const base = `/${params.owner}/${params.repo}`;
67 const fetcher = useFetcher<{ ok: boolean; error?: string }>();
68 const { settings } = view;
69 const disabled = !can.manage_settings || !view.entitled;
70 return (
71 <div className="max-w-3xl space-y-6">
72 <SectionHeader
73 title="Security settings"
74 about="When the pull request checks this repository's security suite reports fail. Require them in branch protection to block merges on them, for people and agents alike."
75 />
76 {!view.entitled && <ActivationPrompt workspace={params.owner} feature="Code scanning and dependency review" monthlyCents={price} isOwner={owner} />}
77 <fetcher.Form method="post" className="space-y-4">
78 <fieldset disabled={disabled} className={`${CARD} space-y-3 p-4 disabled:opacity-60`}>
79 <legend className="sr-only">Code scanning</legend>
80 <p className="text-sm font-medium">Code scanning results</p>
81 <p className="text-sm text-muted">The Code scanning check fails when a pull request brings new results on the lines it changes at this level.</p>
Chat controls, public profiles, shadcn selects, and no Docs tab in a project82 <SelectField
83 name="codeScanningGate"
84 defaultValue={settings.codeScanningGate}
85 disabled={disabled}
86 className={SELECT}
87 aria-label="When code scanning fails"
88 options={CODE_SCANNING_GATES.map((gate) => ({ value: gate.gate, label: gate.label }))}
89 />
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar90 </fieldset>
91 <fieldset disabled={disabled} className={`${CARD} space-y-3 p-4 disabled:opacity-60`}>
92 <legend className="sr-only">Dependency review</legend>
93 <label className="flex items-start justify-between gap-4">
94 <span>
95 <span className="block text-sm font-medium">Dependency review</span>
96 <span className="mt-1 block text-sm text-muted">
97 Pull requests that change a lockfile get the Dependency review check, which fails when they add a package with a
98 known vulnerability or a license you do not allow.
99 </span>
100 </span>
101 <Switch name="dependencyReview" defaultChecked={settings.dependencyReview} className="mt-0.5" />
102 </label>
103 <label className="block">
104 <span className="mb-1.5 block text-xs font-medium text-muted">Fail on vulnerabilities of</span>
Chat controls, public profiles, shadcn selects, and no Docs tab in a project105 <SelectField
106 name="reviewFailOn"
107 defaultValue={settings.reviewFailOn}
108 disabled={disabled}
109 className={SELECT}
110 options={[
111 { value: "critical", label: "Critical severity" },
112 { value: "high", label: "High severity or higher" },
113 { value: "medium", label: "Medium severity or higher" },
114 { value: "low", label: "Any severity" },
115 { value: "none", label: "Never fail on vulnerabilities" },
116 ]}
117 />
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar118 </label>
119 <label className="block">
120 <span className="mb-1.5 block text-xs font-medium text-muted">Licenses not allowed (SPDX ids)</span>
121 <input
122 name="reviewDenyLicenses"
123 defaultValue={settings.reviewDenyLicenses.join(", ")}
124 placeholder="GPL-3.0-only, AGPL-3.0-only"
125 className="h-9 w-full rounded-md border border-line bg-bg px-2.5 font-mono text-sm outline-none hover:border-line-strong focus:border-accent-dim"
126 />
127 </label>
128 <label className="flex items-center justify-between gap-4">
129 <span className="text-sm">Comment the review's summary on the pull request</span>
130 <Switch name="reviewComment" defaultChecked={settings.reviewComment} />
131 </label>
132 </fieldset>
133 <div className="flex flex-wrap items-center gap-3">
Merge branch 'worktree-agent-ae1299e92e4462012'134 <Hint label={whyNot(can, "manage_settings")} disabled={!can.manage_settings}>
135 <button
136 type="submit"
137 disabled={disabled || fetcher.state !== "idle"}
138 className="rounded-md bg-fg px-3.5 py-2 text-sm font-medium text-bg hover:bg-white disabled:opacity-50"
139 >
140 {fetcher.state !== "idle" ? "Saving…" : "Save"}
141 </button>
142 </Hint>
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar143 <Link to={`${base}/settings/branches`} className="text-sm text-muted underline underline-offset-2 hover:text-fg">
144 Require the checks in branch protection
145 </Link>
Merge branch 'worktree-agent-ad7c6d88d93adc817'146 {fetcher.data?.ok && <span className="text-sm text-success">Saved.</span>}
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar147 {fetcher.data?.error && <span className="text-sm text-danger">{fetcher.data.error}</span>}
148 </div>
149 </fetcher.Form>
150 <p className="text-sm text-muted">
151 Security updates are on the{" "}
152 <Link to={`${base}/security/vulnerabilities`} className="underline underline-offset-2 hover:text-fg">
153 Vulnerabilities
154 </Link>{" "}
155 page, and version updates on{" "}
156 <Link to={`${base}/security/dependency-updates`} className="underline underline-offset-2 hover:text-fg">
157 Dependency updates
158 </Link>
159 . Delegated bypass and validity checks are the workspace's, in{" "}
160 <Link to={`/${params.owner}/-/security/settings`} className="underline underline-offset-2 hover:text-fg">
161 its Security settings
162 </Link>
163 .
164 </p>
165 </div>
166 );
167}

This file's history is long; its oldest lines are credited to the oldest commit read.