Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

g1t.sh
71 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.

The runner base keeps its old comment, so the pushed base image still matches its folder1import { RouterContextProvider, createRequestHandler } from "react-router";
2
3import { authorize, isSameOrigin, readSettings } from "../app/lib/access";
4import { denied, secure } from "../app/lib/guard";
5import { staffContext, zoneContext } from "../app/lib/staff";
6import { readZone } from "../app/lib/time";
7
8const requestHandler = createRequestHandler(
9 () => import("virtual:react-router/server-build"),
10 import.meta.env.MODE,
11);
12
13/** Files the build emits for the pages; still behind the same check. */
14const ASSET = /^\/(?:assets\/[\w.-]+|favicon\.svg)$/;
15
16/**
17 * Every request, assets included, passes the same gate before anything
18 * is served:
19 *
20 * 1. sudo is configured, or nothing is served at all;
21 * 2. Cloudflare Access's token verifies (signature, audience, issuer, time);
22 * 3. its email is on the staff list;
23 * 4. a change is a POST from sudo's own pages.
24 */
25async function handle(request: Request, env: Env): Promise<Response> {
26 const settings = readSettings(env);
27 if (!settings) {
28 return denied(
29 403,
30 "sudo is not configured",
31 "ACCESS_TEAM_DOMAIN, ACCESS_AUD and STAFF_EMAILS must all be set before sudo will answer. See apps/sudo/README.md.",
32 );
33 }
34
35 const auth = await authorize(request, settings);
36 if (!auth.ok) {
37 console.warn(JSON.stringify({ event: "sudo.denied", reason: auth.reason, email: auth.email ?? null, path: new URL(request.url).pathname }));
38 return auth.reason === "not staff"
39 ? denied(403, "Not staff", `${auth.email} is signed in, but is not on sudo's staff list.`)
40 : denied(403, "Not allowed", "sudo is for g1t staff, signed in through Cloudflare Access.");
41 }
42
43 const { method } = request;
44 if (method !== "GET" && method !== "HEAD" && method !== "POST") {
45 return denied(405, "Method not allowed", "sudo takes GET and POST only.");
46 }
47 if (method === "POST" && !isSameOrigin(request)) {
48 console.warn(JSON.stringify({ event: "sudo.cross_site", email: auth.email, origin: request.headers.get("origin") }));
49 return denied(403, "Refused", "Changes are only accepted from sudo's own pages.");
50 }
51
52 const { pathname } = new URL(request.url);
53 if (method !== "POST" && ASSET.test(pathname)) {
54 return env.ASSETS.fetch(request);
55 }
56
57 if (method === "POST") {
58 console.log(JSON.stringify({ event: "sudo.change", email: auth.email, path: pathname }));
59 }
60 const context = new RouterContextProvider();
61 context.set(staffContext, { email: auth.email });
62 // Times in the staff member's zone: their choice (/timezone), else where Cloudflare places them.
63 context.set(zoneContext, readZone(request.headers.get("cookie"), (request as { cf?: { timezone?: unknown } }).cf?.timezone));
64 return requestHandler(request, context);
65}
66
67export default {
68 async fetch(request, env) {
69 return secure(await handle(request, env));
70 },
71} satisfies ExportedHandler<Env>;

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