Pin the login disabled guard and the checkOrigin invariant !267

merged merged by cmc on 2026-09-05 18:22 UTC · krz/gitbay:pin-security-guards into main

Discussion

cmc

Two guards that work and that nothing pinned.

login() re-reads the user after consuming a login token and refuses a disabled account. SetUserDisabled also deletes login_tokens, so the existing e2e cases pass with either layer alone — mutation testing confirmed dropping either keeps them green. The guard is the stronger layer: SetUserDisabled is not transactional, so a request in flight between its UPDATE and its DELETE can consume a still-present token and land a session afterwards. The new test sets disabled directly on the row, bypassing SetUserDisabled entirely, so it pins the guard alone.

The session cookie is SameSite=Lax because checkOrigin is the primary CSRF defense, which holds only while every mutating route is wrapped. A wrapped handler cannot be inspected, so the test is behavioural: every Mutating: true route gets a cross-site Origin and must answer 403. Two routes are exempt with a comment each — POST /api/v1/cmd takes only a bearer token, and GET /login carries a single-use token and no cookie.

The route walk sets registration = "open", without which POST /register is never registered and so was neither tested nor allowlisted — invisible, in the one configuration production actually runs. TestTopLevelRouteWordsAreReserved had the same omission and is corrected with it.

Test-only; no production behaviour changes.

Closes #156 Closes #157