| 1 | /** |
| 2 | * "Set up a security policy": commits a SECURITY.md on a new branch as the |
| 3 | * person asking and opens it as their pull request, then goes to it, as |
| 4 | * "Set up code scanning" does with its workflow. It adds a file to the |
| 5 | * repository: Push. |
| 6 | */ |
| 7 | import { redirect } from "react-router"; |
| 8 | |
| 9 | import type { Route } from "./+types/security-policy-setup"; |
| 10 | import { refusal, requireWritableRepo } from "../../lib/access.server"; |
| 11 | import { securityPolicyBranch, securityPolicyPullBody, securityPolicyTemplate } from "../../lib/security-overview"; |
| 12 | import { advisories, repos, work } from "../../lib/services.server"; |
| 13 | import { assertSameOrigin, requireUser } from "../../lib/session.server"; |
| 14 | |
| 15 | export async function action({ request, params, context }: Route.ActionArgs) { |
| 16 | assertSameOrigin(request); |
| 17 | const user = requireUser(context, request); |
| 18 | await requireWritableRepo(context, params); |
| 19 | const refused = await refusal(context, params, "push"); |
| 20 | if (refused) return { error: refused }; |
| 21 | const path = { namespace: params.owner, name: params.repo }; |
| 22 | const base = `/${params.owner}/${params.repo}`; |
| 23 | const [branches, reporting] = await Promise.all([repos.branches(path, user), advisories.reportSettings(path, user)]); |
| 24 | const branch = securityPolicyBranch(branches.ok ? branches.value.map((known) => known.name) : []); |
| 25 | const committed = await repos.commitFile(path, user, { |
| 26 | branch, |
| 27 | path: "SECURITY.md", |
| 28 | content: securityPolicyTemplate(`${params.owner}/${params.repo}`, reporting.ok && reporting.value.privateVulnerabilityReporting, base), |
| 29 | message: "Add a security policy", |
| 30 | }); |
| 31 | if (!committed.ok) return { error: committed.error.message }; |
| 32 | const opened = await work.openPull(user, path, { |
| 33 | branch, |
| 34 | title: "Add a security policy", |
| 35 | body: securityPolicyPullBody("SECURITY.md"), |
| 36 | agent: user.username, |
| 37 | runtime: "external", |
| 38 | }); |
| 39 | if (!opened.ok) return { error: opened.error.message }; |
| 40 | throw redirect(`${base}/pull/${opened.value.number}`); |
| 41 | } |
| 42 | |
| 43 | export async function loader({ params }: Route.LoaderArgs) { |
| 44 | throw redirect(`/${params.owner}/${params.repo}/security`); |
| 45 | } |