Repository navigation
feat(beyondleetcode): C33c cap names at 100 characters - #188
Conversation
…ign up and Setup Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ale registration comments corrected Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
Security review (orchestrator-dispatched, 2026-10-16): verdict fix first. All three decisions hold: the cap applies on Setup, on Sign up and over HTTP; a refusal stores nothing; errors are fixed strings. Counting is cheap, and no path today changes a name without the create hook. To fix in this PR:
Open for the maintainer: zero-width, control and bidi characters in names, which allow display spoofing on Users. Routed to C38: OAuth's updateUser name writes skip the create hook. Codex second opinion did not run (not installed). 🤖 Generated with Claude Code |
…il lookup, so it is no probe Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
Fixes verified (orchestrator, 2026-10-16) at abee3d5, updated with main, CI green. hooks.before on /sign-up/email refuses a blank or too-long name right after the admission check, before Better Auth looks the email up. A known and an unknown email now get the same 400, and a closed Instance still answers 403 first. The create hook keeps the same check as a backstop. Merging. 🤖 Generated with Claude Code |
Card C33c (access control: security review before merge). Worker: builder-signin.
A name longer than 100 characters is refused on Setup and on Sign up with a field error, and by a direct
POST /api/auth/sign-up/email, and nothing is stored. The cap is 100 characters, counted in Unicode code points on the trimmed name (maintainer, 2026-10-16).Checklist
auth.ts:MAX_NAME_LENGTH,isNameTooLong, and theuser.create.beforehook refuses a longer name with a fixedAPIError(NAME_TOO_LONG, 400, "The name is too long."), never echoing the name. It runs after the admission check (a closed sign-up answers 403 whatever the name) and before any Learner is made (a refused name leaves no Learner, not even the named Local one)sign-up.ts: fixed field error "Use at most 100 characters." before calling Better Auth (after the blank-name check, before email and password), and Better Auth's ownNAME_TOO_LONGmapped to the same text+page.server.ts: the same field error, before the Instance is claimed (after the already-set-up check), and the same mappingsignup.multi.e2e.ts: browser tests of Sign up (101 characters refused on the field, exactly 100 emoji accepted, a direct POST refused and nothing stored); Playwright 95 pass locally under the shared locksignup.multi.e2e.tsandseed-open-instance.ts: the Users toggle (C34b) can open registration now; the seed is a test fixture nothing in the app importscodexis not installed on this machineFrom the security review
hooks.beforeon/sign-up/email, right after the admission check (so a closed sign-up still answers 403 first), throwing the same fixedNAME_TOO_LONGerror. The database-hook check stays as the backstop. Tests: a known and an unknown email with a long name get the same 400 and the same body; a closed Instance answers 403 for a long name, a blank one, a known email or not. A browser test on the open server checks the same over a real POST.NAME_REQUIREDerror, 400, "Enter a name.". Sign up and Setup map it to the forms' existing blank-name field error. Tests: HTTP for three blank spellings (nothing stored, no Learner), the server-side call, a known email the same as an unknown one, and the two form mappings.Zero-width, control and bidi characters are left alone, as told: which characters a name may hold is a product rule.
Notes for the security review
"a"plus 400 KB of spaces would pass the cap and store 400 KB. The forms already send the name trimmed; a direct POST now gets the same. This is the one behaviour I added beyond the card's wording (a name with spaces around it is stored without them on every path); tell me if you would rather count the untrimmed name instead.isNameTooLongtrims, then decides from the UTF-16 length alone when it can (up to 100 units is within the cap, over 200 is not) and only spreads into code points between those. Tests cover the boundaries with ASCII, emoji (two units each), lone surrogates and Unicode whitespace.setup.multi.e2e.tsis outside this card's files.maxlength(the browser counts UTF-16 units, so it would refuse a 100-emoji name the server accepts).users.multi.e2e.ts'sbeforeAll(ensureLearner: Prisma "Operation has timed out" on the shared database); the next two runs passed in full. The same setup timed out twice during C32 whenever two test processes wrote to the shared SQLite file at once. Worth a card: a retry inmulti-fixture.ts, or a busy timeout on its connection.🤖 Generated with Claude Code