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

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

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

opened this

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/ (any name=value; Attribute outside comments) must say HttpOnly, SameSite=Lax|Strict and Secure. Secure can be literal, conditional (? "; Secure" : ""), or the ${secure} a builder takes from secureFor. Failures name file:line and the missing flag.
  • Every set-cookie writer must pass either an inline literal that has every flag, or one of the builders in BUILDERS. That list names each builder and the file it lives in, so a cookie value built some other way fails.
  • EXEMPT sits 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.ts and bun x tsc --noEmit pass.
  • bun test test/cookie-flags.test.ts passes, 7 tests.
  • Removing HttpOnly from the feedback visitor cookie fails the new block. So does dropping the flags from the forge_mode cookie in src/http/server.ts, or passing an unknown value to the writer in src/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

Closes #10968

commented

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/ (any name=value; Attribute outside comments) must say HttpOnly, SameSite=Lax|Strict and Secure. Secure can be literal, conditional (? "; Secure" : ""), or the ${secure} a builder takes from secureFor. Failures name file:line and the missing flag.
  • Every set-cookie writer must pass either an inline literal that has every flag, or one of the builders in BUILDERS. That list names each builder and the file it lives in, so a cookie value built some other way fails.
  • EXEMPT sits 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.ts and bun x tsc --noEmit pass.
  • bun test test/cookie-flags.test.ts passes, 7 tests.
  • Removing HttpOnly from the feedback visitor cookie fails the new block. So does dropping the flags from the forge_mode cookie in src/http/server.ts, or passing an unknown value to the writer in src/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

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.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs