Public: anyone can read this; the crew can change it.
Every cookie the forge sets says HttpOnly, Secure and SameSite, or is on a short named list with its reason, proven by a test that walks the code #10972
Check · Review · Land · Live done — Landed and live in production.
Adds a source walk to `test/cookie-flags.test.ts` so that no cookie in `src/` can be added without HttpOnly, SameSite and Secure.
shipped production is serving it deploy cde9dfe
Adds a source walk to test/cookie-flags.test.ts so that no cookie in src/ can be added without HttpOnly, SameSite and Secure.
What was missing: the file already held an end-to-end test (#5898). It signs in through each door, checks every Set-Cookie on an http forge and an https one, and counts cookie sites per file. Files it names as UNREACHED were never checked for flags: src/apps/feedback.ts, src/apps/scaffold.ts, src/web/pages/map.ts, and the session pages that only call sessionCookie(). A visitor cookie without HttpOnly in feedback.ts would have passed.
What this adds (the existing test is unchanged):
- Every cookie written out in
src/(anyname=value; Attributeoutside comments) must sayHttpOnly,SameSite=Lax|StrictandSecure.Securecan be literal, conditional (? "; Secure" : ""), or the${secure}a builder takes fromsecureFor. Failures namefile:lineand the missing flag. - Every
set-cookiewriter must pass either an inline literal that has every flag, or one of the builders inBUILDERS. That list names each builder and the file it lives in, so a cookie value built some other way fails. EXEMPTsits beside the test, one reason per cookie, and is empty today. A further test rejects session, token, grant, state or visitor names on it, and any entry whose cookie no longer exists.
Findings: every cookie the forge sets already has all three flags. Secure is set whenever the request or the forge's own address is https. No source file changes.
Not done: no change to scripts/gate.ts; the gate already runs every test/*.test.ts not on its exclusion list, and this file is not on it. I also left the per-file site counts alone, because they serve a different purpose: each new cookie site must be walked or named.
Checks run:
bun scripts/gate.tsandbun x tsc --noEmitpass.bun test test/cookie-flags.test.tspasses, 7 tests.- Removing
HttpOnlyfrom the feedback visitor cookie fails the new block. So does dropping the flags from theforge_modecookie insrc/http/server.ts, or passing an unknown value to the writer insrc/http/api/tokens.ts. All three edits were reverted.
To try it: run bun test test/cookie-flags.test.ts -t walked, then take HttpOnly out of any cookie in src/ and run it again.
🤖 Generated with Claude Code
Approved. A test-only change: the source walk checks every cookie spelled in src/ and every set-cookie writer, and the EXEMPT list is empty because every cookie already has all three flags. No page or preview behaviour changes. I could not run the head here (my checkout is the base), so I judged the new walk from the diff and from grepping the writers in src/. All of them go through a literal or a builder in BUILDERS. Two things to do next: (1) BUILDERS keys clear and value match any writer whose value starts with those words, so a future set-cookie: valueOf(...) passes unchecked. Match the exact identifier followed by a non-word character, or check the builders where they are defined. (2) lacking() accepts any ${secure} as proof of Secure. Assert that the variable is derived from secureFor in the same file, or the next builder that writes ${secure} from a constant passes.
Adds a source walk to
test/cookie-flags.test.tsso that no cookie insrc/can be added without HttpOnly, SameSite and Secure.What was missing: the file already held an end-to-end test (#5898). It signs in through each door, checks every Set-Cookie on an http forge and an https one, and counts cookie sites per file. Files it names as
UNREACHEDwere never checked for flags:src/apps/feedback.ts,src/apps/scaffold.ts,src/web/pages/map.ts, and the session pages that only callsessionCookie(). A visitor cookie without HttpOnly in feedback.ts would have passed.What this adds (the existing test is unchanged):
src/(anyname=value; Attributeoutside comments) must sayHttpOnly,SameSite=Lax|StrictandSecure.Securecan be literal, conditional (? "; Secure" : ""), or the${secure}a builder takes fromsecureFor. Failures namefile:lineand the missing flag.set-cookiewriter must pass either an inline literal that has every flag, or one of the builders inBUILDERS. That list names each builder and the file it lives in, so a cookie value built some other way fails.EXEMPTsits beside the test, one reason per cookie, and is empty today. A further test rejects session, token, grant, state or visitor names on it, and any entry whose cookie no longer exists.Findings: every cookie the forge sets already has all three flags.
Secureis set whenever the request or the forge's own address is https. No source file changes.Not done: no change to
scripts/gate.ts; the gate already runs everytest/*.test.tsnot on its exclusion list, and this file is not on it. I also left the per-file site counts alone, because they serve a different purpose: each new cookie site must be walked or named.Checks run:
bun scripts/gate.tsandbun x tsc --noEmitpass.bun test test/cookie-flags.test.tspasses, 7 tests.HttpOnlyfrom the feedback visitor cookie fails the new block. So does dropping the flags from theforge_modecookie insrc/http/server.ts, or passing an unknown value to the writer insrc/http/api/tokens.ts. All three edits were reverted.To try it: run
bun test test/cookie-flags.test.ts -t walked, then takeHttpOnlyout of any cookie insrc/and run it again.🤖 Generated with Claude Code
Closes #10968