Skip to content
61 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 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 address1/**
2 * The headers every answer from the site carries, and the policy its pages
3 * run under. No Workers imports, so it can be tested under Node.
4 *
5 * - Nothing is sniffed: a download or a data request is only the type it says.
6 * - A link to another site sends the origin, never the path.
7 * - No other site may put g1t's pages in a frame.
8 * - A page runs only the scripts the site served it: its own files, and the
9 * inline scripts React and React Router write, each carrying the page's
Merge site-audit: SEO routes, analytics with consent first in Europe, security page, founder and X account, docs header; the tour opens on the pull request and leads with Code, on the real shell, with Agents and Docs working today10 * nonce, plus Cloudflare's and HeyCatch's analytics scripts. Styles may
11 * be inline (highlighting and layout set them); images may come from any
12 * HTTPS address (pictures in a README); requests may go to any HTTPS
13 * address (the status page's summary, analytics events).
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 address14 */
15
16/** A fresh nonce for one page: 128 random bits, base64. */
17export function makeNonce(): string {
18 const bytes = crypto.getRandomValues(new Uint8Array(16));
19 return btoa(String.fromCharCode(...bytes));
20}
21
22/**
23 * The Content-Security-Policy of a page rendered with `nonce`. `usercontent`
24 * is where repository files and avatars are served (lib/usercontent.ts),
25 * named for an installation that serves them over plain HTTP.
26 */
27export function pagePolicy(nonce: string, usercontent?: string): string {
28 const files = usercontent && /^http:/.test(usercontent) ? ` ${new URL(usercontent).origin}` : "";
29 return [
30 "default-src 'self'",
Merge site-audit: SEO routes, analytics with consent first in Europe, security page, founder and X account, docs header; the tour opens on the pull request and leads with Code, on the real shell, with Agents and Docs working today31 // Cloudflare's Web Analytics beacon, when the zone turns it on, and
32 // HeyCatch's helper for product analytics (lib/analytics.client.ts).
33 `script-src 'self' 'nonce-${nonce}' https://static.cloudflareinsights.com https://in.heycatch.ai`,
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 address34 "style-src 'self' 'unsafe-inline'",
35 `img-src 'self' https: data: blob:${files}`,
36 `media-src 'self' https:${files}`,
37 "font-src 'self' data:",
38 "connect-src 'self' https:",
39 "frame-src 'none'",
40 "object-src 'none'",
41 "base-uri 'self'",
42 "frame-ancestors 'none'",
43 ].join("; ");
44}
45
46/**
47 * The answer with the site's headers added. A header the answer already
48 * has is kept. An upgrade to a WebSocket is passed on as it is.
49 */
50export function withSiteHeaders(response: Response): Response {
51 if (response.status === 101 || (response as Response & { webSocket?: unknown }).webSocket) return response;
52 // A redirect's headers cannot be changed, so the answer is copied.
53 const answer = new Response(response.body, response);
54 const headers = answer.headers;
55 if (!headers.has("x-content-type-options")) headers.set("x-content-type-options", "nosniff");
56 if (!headers.has("referrer-policy")) headers.set("referrer-policy", "strict-origin-when-cross-origin");
57 if (/^text\/html\b/i.test(headers.get("content-type") ?? "") && !headers.has("x-frame-options")) {
58 headers.set("x-frame-options", "DENY");
59 }
60 return answer;
61}

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