Skip to content
201 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.

Merge branch 'worktree-agent-a8752162fea25f63f' into spend-guardrails1import assert from "node:assert/strict";
2import { readFileSync } from "node:fs";
3import { test } from "node:test";
4
5import {
6 LIMIT_PERIOD_SECONDS,
7 RATE_LIMITS,
8 type RateLimitBinding,
9 checkLimit,
10 isLimited,
11 secretKey,
12} from "@g1t/contracts/rate-limits";
13
Merge g1tusercontent.com: registry answers run nothing in a browser, the site's pages run only their own scripts, repository files and avatars on their own origin, raw files rate limited per address14import { gitLimited, heavy, pageLimited, sessionCookie, unlimited, usercontentLimited } from "./front-door-limits.ts";
Merge branch 'worktree-agent-a8752162fea25f63f' into spend-guardrails15
16/** A binding that lets `allow` requests through per key, recording each key it was asked. */
17function binding(allow: number): RateLimitBinding & { keys: string[] } {
18 const counts = new Map<string, number>();
19 const keys: string[] = [];
20 return {
21 keys,
22 async limit({ key }) {
23 keys.push(key);
24 const count = (counts.get(key) ?? 0) + 1;
25 counts.set(key, count);
26 return { success: count <= allow };
27 },
28 };
29}
30
31const broken: RateLimitBinding = {
32 async limit() {
33 throw new Error("the binding is down");
34 },
35};
36
37function request(path: string, headers: Record<string, string> = {}): Request {
38 return new Request(`https://g1t.sh${path}`, { headers: { "cf-connecting-ip": "203.0.113.9", ...headers } });
39}
40
41test("a limit lets requests through until it is reached", async () => {
42 const limit = binding(2);
43 assert.equal(await checkLimit(limit, "ip:1"), "allowed");
44 assert.equal(await checkLimit(limit, "ip:1"), "allowed");
45 assert.equal(await checkLimit(limit, "ip:1"), "limited");
46 assert.equal(await checkLimit(limit, "ip:2"), "allowed", "each key counts apart");
47});
48
49test("a missing or failing binding fails open", async () => {
50 const logged = console.error;
51 console.error = () => {};
52 try {
53 assert.equal(await checkLimit(undefined, "ip:1"), "unavailable");
54 assert.equal(await checkLimit(broken, "ip:1"), "unavailable");
55 assert.equal(await isLimited(broken, "ip:1"), false);
56 assert.equal(await gitLimited({ GIT_ANONYMOUS_LIMIT: broken }, request("/acme/rocket.git/info/refs")), null);
57 assert.equal(await pageLimited({ WEB_ADDRESS_LIMIT: broken, WEB_ANONYMOUS_LIMIT: broken }, request("/acme/rocket"), "/acme/rocket"), null);
58 } finally {
59 console.error = logged;
60 }
61});
62
63test("secrets are keyed by a short hash, never as they are", async () => {
64 const key = await secretKey("session", "s3cret-session");
65 assert.match(key, /^session:[0-9a-f]{16}$/);
66 assert.equal(await secretKey("session", "s3cret-session"), key);
67 assert.notEqual(await secretKey("session", "another"), key);
68});
69
70test("git without credentials is limited by address, with a message git shows", async () => {
71 const env = { GIT_ANONYMOUS_LIMIT: binding(1), GIT_SIGNED_LIMIT: binding(1) };
72 const clone = () => request("/acme/rocket.git/info/refs");
73 assert.equal(await gitLimited(env, clone()), null);
74 const refused = await gitLimited(env, clone());
75 assert.equal(refused?.status, 429);
76 assert.equal(refused?.headers.get("retry-after"), String(LIMIT_PERIOD_SECONDS));
77 assert.match(refused?.headers.get("content-type") ?? "", /^text\/plain/);
78 assert.match((await refused?.text()) ?? "", /Too many git requests/);
79 assert.deepEqual(env.GIT_ANONYMOUS_LIMIT.keys, ["ip:203.0.113.9", "ip:203.0.113.9"]);
80});
81
82test("git with credentials counts by a hash of them, apart from the address", async () => {
83 const env = { GIT_ANONYMOUS_LIMIT: binding(0), GIT_SIGNED_LIMIT: binding(5) };
84 const authorization = `Basic ${btoa("ada:g1t_token")}`;
85 assert.equal(await gitLimited(env, request("/acme/rocket.git/git-upload-pack", { authorization })), null);
86 assert.equal(env.GIT_ANONYMOUS_LIMIT.keys.length, 0);
87 assert.match(env.GIT_SIGNED_LIMIT.keys[0]!, /^git:[0-9a-f]{16}$/);
88 assert.ok(!env.GIT_SIGNED_LIMIT.keys[0]!.includes("g1t_token"));
89});
90
91test("signed-out pages count by address, and costly ones against a tighter limit too", async () => {
92 const env = { WEB_ADDRESS_LIMIT: binding(100), WEB_ANONYMOUS_LIMIT: binding(100), WEB_HEAVY_LIMIT: binding(1) };
93 assert.equal(await pageLimited(env, request("/acme/rocket"), "/acme/rocket"), null);
94 assert.equal(env.WEB_HEAVY_LIMIT.keys.length, 0);
95 const archive = "/acme/rocket/archive/main.zip";
96 assert.equal(await pageLimited(env, request(archive), archive), null);
97 const refused = await pageLimited(env, request(archive), archive);
98 assert.equal(refused?.status, 429);
99 assert.match((await refused?.text()) ?? "", /Signed-in accounts have a higher limit/);
100 assert.equal(await pageLimited(env, request("/acme/rocket/issues"), "/acme/rocket/issues"), null, "other pages go on");
101});
102
103test("signed-in requests count by session, and every request by address", async () => {
104 const env = { WEB_ADDRESS_LIMIT: binding(1), WEB_SESSION_LIMIT: binding(100), WEB_ANONYMOUS_LIMIT: binding(0) };
105 const signedIn = () => request("/acme/rocket/archive/main.zip", { cookie: "theme=dark; g1t_session=abc123" });
106 assert.equal(await pageLimited(env, signedIn(), "/acme/rocket/archive/main.zip"), null);
107 assert.equal(env.WEB_ANONYMOUS_LIMIT.keys.length, 0);
108 assert.match(env.WEB_SESSION_LIMIT.keys[0]!, /^session:[0-9a-f]{16}$/);
109 // Made-up cookies still meet the ceiling per address.
110 const refused = await pageLimited(env, request("/", { cookie: "g1t_session=made-up" }), "/");
111 assert.equal(refused?.status, 429);
112});
113
Merge main into Artifacts Phase 2114test("requests with an access token count by a hash of the token, at the API's limit", async () => {
115 const env = { WEB_ADDRESS_LIMIT: binding(100), WEB_TOKEN_LIMIT: binding(1), WEB_SESSION_LIMIT: binding(100), WEB_ANONYMOUS_LIMIT: binding(0) };
116 const withToken = () => request("/acme/rocket", { authorization: "Bearer g1t_secret", cookie: "g1t_session=abc123" });
117 assert.equal(await pageLimited(env, withToken(), "/acme/rocket"), null);
118 assert.equal(env.WEB_SESSION_LIMIT.keys.length, 0, "the token counts, not a cookie beside it");
119 assert.equal(env.WEB_ANONYMOUS_LIMIT.keys.length, 0);
120 assert.match(env.WEB_TOKEN_LIMIT.keys[0]!, /^token:[0-9a-f]{16}$/);
121 assert.ok(!env.WEB_TOKEN_LIMIT.keys[0]!.includes("g1t_secret"));
122 const refused = await pageLimited(env, withToken(), "/acme/rocket");
123 assert.equal(refused?.status, 429);
124 assert.match((await refused?.text()) ?? "", /this access token/);
125 assert.equal(RATE_LIMITS.WEB_TOKEN_LIMIT.limit, RATE_LIMITS.API_TOKEN_LIMIT.limit);
126});
127
Merge branch 'worktree-agent-a8752162fea25f63f' into spend-guardrails128test("files the Worker serves itself are never limited", async () => {
129 const env = { WEB_ADDRESS_LIMIT: binding(0), WEB_ANONYMOUS_LIMIT: binding(0) };
130 for (const path of ["/assets/app-1a2b.js", "/fonts/hanken.woff2", "/favicon.ico", "/robots.txt", "/llms.txt", "/sitemap.xml"]) {
131 assert.ok(unlimited(path), path);
132 assert.equal(await pageLimited(env, request(path), path), null, path);
133 }
134 assert.ok(!unlimited("/acme/rocket/blob/main/logo.png"), "a file in a repository is a page");
135});
136
137test("costly pages are recognised", () => {
138 for (const path of [
139 "/search",
140 "/search.data",
141 "/acme/rocket/archive/main.zip",
142 "/acme/rocket/actions/runs/run_1",
143 "/acme/rocket/actions/runs/run_1.data",
144 "/acme/rocket/actions/runs/run_1/logs.zip",
145 "/acme/rocket/actions/runs/run_1/artifacts/dist",
146 "/acme/rocket/actions/jobs/job_1/log.txt",
147 ]) {
148 assert.ok(heavy(path), path);
149 }
150 for (const path of ["/", "/acme/rocket", "/acme/rocket/actions", "/acme/rocket/issues/1", "/acme/search"]) {
151 assert.ok(!heavy(path), path);
152 }
153});
154
155test("the session cookie is read from among others", () => {
156 assert.equal(sessionCookie("theme=dark; g1t_session=abc; x=1"), "abc");
157 assert.equal(sessionCookie("g1t_session=abc"), "abc");
158 assert.equal(sessionCookie("not_g1t_session=abc"), null);
159 assert.equal(sessionCookie(null), null);
160});
161
162/** A wrangler.jsonc as JSON: comments and trailing commas out, strings kept. */
163function readJsonc(path: string): { ratelimits?: { name: string; namespace_id: string; simple: { limit: number; period: number } }[] } {
164 const text = readFileSync(new URL(path, import.meta.url), "utf8")
165 .replace(/("(?:\\.|[^"\\])*")|\/\/[^\n]*|\/\*[\s\S]*?\*\//g, (_, string: string | undefined) => string ?? "")
166 .replace(/,(\s*[}\]])/g, "$1");
167 return JSON.parse(text);
168}
169
170test("every wrangler.jsonc declares the rate limits RATE_LIMITS lists, and only those", () => {
171 const ids = new Set<number>();
172 const workers = new Set(Object.values(RATE_LIMITS).map((spec) => spec.worker));
173 for (const worker of workers) {
174 const declared = readJsonc(`../../../../${worker}/wrangler.jsonc`).ratelimits ?? [];
175 const listed = Object.entries(RATE_LIMITS).filter(([, spec]) => spec.worker === worker);
176 assert.deepEqual(
177 declared.map((binding) => binding.name).sort(),
178 listed.map(([name]) => name).sort(),
179 `${worker}'s bindings`,
180 );
181 for (const [name, spec] of listed) {
182 const binding = declared.find((b) => b.name === name)!;
183 assert.equal(Number(binding.namespace_id), spec.namespaceId, `${name}'s namespace id`);
184 assert.equal(binding.simple.limit, spec.limit, `${name}'s limit`);
185 assert.equal(binding.simple.period, LIMIT_PERIOD_SECONDS, `${name}'s period`);
186 }
187 }
188 for (const spec of Object.values(RATE_LIMITS)) {
189 assert.ok(!ids.has(spec.namespaceId), `namespace id ${spec.namespaceId} is used once`);
190 ids.add(spec.namespaceId);
191 }
192});
Merge g1tusercontent.com: registry answers run nothing in a browser, the site's pages run only their own scripts, repository files and avatars on their own origin, raw files rate limited per address193
194test("repository files on the usercontent origin count per address; avatars do not", async () => {
195 const env = { WEB_ANONYMOUS_LIMIT: binding(1) };
196 const raw = "/acme/rocket/raw/main/logo.png";
197 assert.equal(await usercontentLimited(env, request(raw), raw), null);
198 assert.equal((await usercontentLimited(env, request(raw), raw))?.status, 429);
199 assert.equal(await usercontentLimited(env, request("/avatars/" + "a".repeat(64)), "/avatars/" + "a".repeat(64)), null);
200 assert.deepEqual(env.WEB_ANONYMOUS_LIMIT.keys, ["ip:203.0.113.9", "ip:203.0.113.9"]);
201});

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