1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
|
import { describe, expect, it } from "bun:test";
import { readdirSync, readFileSync, statSync } from "fs";
import { join } from "path";
const ROOT = "src/routes";
const MIDDLEWARE_GUARD =
/requireAuth|requireApiAuth|requireAdmin|requireScope|requireRepoAccess|requireOrgRole|agentAuth|adminOnly/;
const HANDLER_GUARD =
/\bgate\(c\)|require[A-Z]\w*\(c\)|isSiteAdmin|assertAdmin|assertRepoReadable|assertRepoWritable|resolveRepoAccess|c\.get\("user"\)!|const viewer = c\.get\("user"\)|const user = c\.get\("user"\)|if \(!c\.get\("user"\)\)|verifyBearer|authenticateBearer|verifyPatAuth|verifySignature|EMERGENCY_PAT_SECRET|scimAuth|verifyScimToken/;
const PUBLIC_WRITES: Record<string, string> = {
"POST /register": "signup",
"POST /login": "sign-in",
"POST /login/2fa": "second factor; the session exists but is not yet valid",
"POST /login/magic": "magic-link request",
"POST /api/auth/register": "signup (API)",
"POST /api/auth/login": "sign-in (API)",
"POST /oauth/token": "OAuth token exchange; authenticates via client creds",
"POST /api/passkeys/auth/options": "WebAuthn sign-in challenge; username optional, no session yet",
"POST /api/passkeys/auth/verify": "WebAuthn sign-in; authenticates via the signed assertion",
"POST /forgot-password": "password reset request",
"POST /reset-password": "password reset completion; authenticates via token",
"POST /:owner/:repo.git/git-upload-pack": "git protocol, own auth",
"POST /:owner/:repo.git/git-receive-pack": "git protocol, own auth",
"POST /api/hooks/gatetest": "GateTest callback",
"POST /hooks/pagerduty": "incident webhook",
"POST /hooks/datadog": "incident webhook",
"POST /hooks/opsgenie": "incident webhook",
"POST /hooks/incident": "incident webhook",
"POST /api/v2/integrations/slack/events": "Slack Events API",
"POST /api/v2/integrations/discord/interactions": "Discord interactions",
"POST /api/webhooks/stripe": "Stripe webhook; verified by stripe-signature",
"POST /sso/saml/:orgSlug/callback": "SAML assertion consumer; authenticates via the signed assertion",
"POST /api/v1/runs/:runId/artifacts": "CI artifact upload; glc_ PAT bearer with repo/write scope",
"DELETE /api/v1/artifacts/:artifactId": "CI artifact delete; glc_ PAT bearer",
"POST /deploy/started": "deploy event; Authorization: Bearer DEPLOY_EVENT_TOKEN",
"POST /deploy/finished": "deploy event; Authorization: Bearer DEPLOY_EVENT_TOKEN",
"POST /deploy/step": "deploy event; Authorization: Bearer DEPLOY_EVENT_TOKEN",
"POST /v2/*": "OCI registry; Docker Basic auth validated against api_tokens",
"PUT /v2/*": "OCI registry; Docker Basic auth validated against api_tokens",
"PATCH /v2/*": "OCI registry; Docker Basic auth validated against api_tokens",
"DELETE /v2/*": "OCI registry; Docker Basic auth validated against api_tokens",
"POST /oauth/register": "RFC 7591 dynamic client registration",
"POST /oauth/revoke": "RFC 7009 revocation; authenticates via the token itself",
"POST /api/v2/integrations/slack/install": "OAuth install redirect start",
"POST /api/v2/integrations/discord/install": "OAuth install redirect start",
"POST /loops/:slug/invoke": "public hosted loop; gated on loop.isPublic",
"POST /enterprise/contact": "contact form",
"POST /markdown/preview": "stateless render, touches no state",
"POST /play": "playground account creation; the point is to need no account",
"POST /status/subscribe": "status-page email subscription",
"POST /flywheel-telemetry/feedback": "anonymous product telemetry",
"POST /api/claude/session": "fire-and-forget session telemetry, documented as no-auth",
"POST /setup": "first-run bootstrap; refuses once the users table is non-empty",
};
function walk(d: string): string[] {
const out: string[] = [];
for (const e of readdirSync(d)) {
const p = join(d, e);
if (statSync(p).isDirectory()) out.push(...walk(p));
else if (e.endsWith(".ts") || e.endsWith(".tsx")) out.push(p);
}
return out;
}
type Route = { method: string; path: string; file: string; guard: string };
function analyze(): Route[] {
const routes: Route[] = [];
for (const f of walk(ROOT)) {
const src = readFileSync(f, "utf8");
const rel = f.replace(/\\/g, "/").split("/routes/")[1];
const prefixGuards: string[] = [];
let guardAll = false;
for (const m of src.matchAll(/\.use\(\s*(["'`])([^"'`]*)\1\s*,([\s\S]{0,200}?)\)\s*;/g)) {
if (!MIDDLEWARE_GUARD.test(m[3])) continue;
if (m[2] === "*") guardAll = true;
else prefixGuards.push(m[2]);
}
const re = /\.(post|put|patch|delete)\(\s*(["'`])([^"'`\n]*)\2/g;
let m;
while ((m = re.exec(src))) {
const path = m[3];
if (!path.startsWith("/")) continue;
const window = src.slice(m.index, m.index + 1200);
const arrow = window.indexOf("=>");
const sig = arrow > -1 ? window.slice(0, arrow) : window.slice(0, 300);
let guard = "NONE";
if (MIDDLEWARE_GUARD.test(sig)) guard = "inline";
else if (guardAll) guard = "router-*";
else if (prefixGuards.some((g) => { const p = g.replace(/\*$/, ""); return p && path.startsWith(p); })) guard = "router-prefix";
else if (HANDLER_GUARD.test(window)) guard = "in-handler";
routes.push({ method: m[1].toUpperCase(), path, file: rel, guard });
}
}
return routes;
}
const ROUTES = analyze();
describe("state-changing routes", () => {
it("finds a substantial write surface (guards against the scan silently breaking)", () => {
expect(ROUTES.length).toBeGreaterThan(300);
});
it("every unguarded write is explicitly declared public", () => {
const unexplained = ROUTES
.filter((r) => r.guard === "NONE")
.filter((r) => !(`${r.method} ${r.path}` in PUBLIC_WRITES))
.map((r) => `${r.method} ${r.path} (${r.file})`)
.sort();
expect(unexplained).toEqual([]);
});
it("every PUBLIC_WRITES entry carries a reason", () => {
for (const [route, reason] of Object.entries(PUBLIC_WRITES)) {
expect(reason.length, `${route} needs a reason`).toBeGreaterThan(3);
}
});
it("PUBLIC_WRITES has no stale entries", () => {
const live = new Set(
ROUTES.filter((r) => r.guard === "NONE").map((r) => `${r.method} ${r.path}`)
);
const stale = Object.keys(PUBLIC_WRITES).filter((k) => !live.has(k));
expect(stale).toEqual([]);
});
});
|