mirror of
https://github.com/PerpetualSoftware/pad.git
synced 2026-09-10 23:15:40 +00:00
696b477b12
test(server): the mobile shells' unversioned contracts fail CI when they move (TASK-2053)
The native shells talk to this server over string contracts nothing protects:
route paths, cookie names, JSON keys, a header shape. None sits behind the MCP
tool-surface version or any other gate, and the min-server-version warning the
app shows covers the opposite direction — it protects a NEW app against an OLD
server and says nothing when the server moves forward under a shipped build. A
shipped build cannot be patched on our schedule, so a renamed key is a silent
break for every installed copy until its owner updates.
One file, one table, six contracts, each naming the shell surface that breaks:
- Both session cookie spellings. __Host-pad_session is the one that matters —
every deployment a phone talks to is TLS — and it appeared in NO Go test
assertion, only in a config-test comment. The insecure spelling is in
nineteen test files as a helper building a request, which is a mention and
not a claim about the name.
- The six auth routes, asserted by WALKING the route table rather than by
firing requests: a request-based check answers "did something handle this",
which a catch-all or a redirect satisfies while the route itself is gone.
- The /auth/session keys the app branches on before showing any UI, version
included. A rename there reads on a phone as a blank screen or a login form
on an instance that needs setup, not as an error.
- The full two-step 2FA sign-in. requires_2fa, challenge_token and
recovery_code were live request/response keys appearing in ZERO test files;
the test drives login → login-verify with a real recovery code, so a rename
on either side fails here.
- Content-Disposition carrying a filename on BOTH dispositions (BUG-2910).
The existing download tests assert the inline;/attachment; prefix and say
nothing about the parameter after it, so the filename could be dropped from
either branch with the suite green.
Scope is stated in the file rather than left for a reader to re-derive.
/auth/apple/native is a pad-cloud route; the app-scheme redirect allowlist does
not exist in this repo (greps for the scheme forms and for app_scheme/appScheme
return nothing in Go, TypeScript or Svelte); and the OAuth error codes this repo
emits belong to Dynamic Client Registration for MCP clients, not to mobile
sign-in — pinning them here would look like mobile coverage while protecting a
different client entirely.
Verified by an eight-mutant matrix: renaming either cookie name, the
login-verify route, requires_2fa, challenge_token or recovery_code, dropping
version from the session payload, or dropping the filename from
Content-Disposition each fails a named test. Every mutant is a change someone
could plausibly make in an afternoon, and every one of them was silent before.
Two of my own assumptions were caught by running it rather than by reading:
chi's router is built lazily, so walking it on a server that has served nothing
panicked; and the "downloaded document" case used a .txt, which is on the read
path's inline-safe allowlist and came back inline — the case would have tested
the same branch twice under a name claiming otherwise. It uses a real zip now.
Claude-Session: https://claude.ai/code/session_01HeChkgZVYb3NTgTcckF5KR