Skip to content

feat(beyondleetcode): C33d refuse control and bidi characters in names - #190

Merged
GauranshMathur merged 2 commits into
mainfrom
C33d-name-characters
Oct 22, 2026
Merged

GauranshMathur merged 2 commits into
mainfrom
C33d-name-characters

Conversation

@GauranshMathur

@GauranshMathur GauranshMathur commented Oct 16, 2026 •

Copy link
Copy Markdown
Owner

Refuses control (U+0000-U+001F, U+007F-U+009F) and bidi (U+202A-U+202E, U+2066-U+2069) characters in names. U+200B, U+200D, U+2060 stay allowed, per the maintainer decision.

  • rule + hooks.before / user.create.before (code NAME_INVALID_CHARACTERS, 400, fixed text)
  • Setup and Sign up field errors
  • tests
  • self-review; Codex ran (2 findings, not changed, see below)
  • CI

Notes for the security review

  • Order in both hooks: admission (403), blank, too long, then characters, all before Better Auth's email lookup; known and unknown email answer the same (tested).
  • The rule runs on the trimmed name (what is stored), so a tab/newline at the edges is trimmed away, one inside is refused.
  • Length is checked before the characters, so a huge name is never scanned by the regex.
  • Uses \p{Cc} (exactly C0+C1) plus the two bidi ranges. Exported helper hasForbiddenNameCharacter for C38 (OAuth names).
  • Setup/Sign up check locally before calling Better Auth, and map the Better Auth code too; the first admin's Instance claim is untouched when refused.

Codex second opinion

Two P2 findings: the Setup and Sign up forms hand the rejected name back to the same form (typed). Not changed: every refusal on these forms (blank, too long, bad email) returns what was typed so the user can fix it, as C33c does, and it goes only to the person who typed it. The API refusal (APIError) and the HTTP body never echo the name (tested).

🤖 Generated with Claude Code

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@GauranshMathur
GauranshMathur marked this pull request as ready for review October 16, 2026 14:09
@GauranshMathur

Copy link
Copy Markdown
Owner Author

Security review (orchestrator-dispatched, 2026-10-22)

Verdict: merge. Two readers on one shared brief (the card's decisions as a checklist), both read-only, cwd the C33d worktree at a1a02c5:

  • Fable advisor: agree.
  • Codex (read-only codex exec): concerns, one point, overruled by the maintainer (below).

(An earlier Codex run on 2026-10-16 never read the code: its sandbox could not start after a reboot. That run is not counted.)

Both readers: no reachable path stores a forbidden character

  • FORBIDDEN_NAME_CHARACTERS (\p{Cc} plus U+202A-U+202E and U+2066-U+2069) matches the decision exactly. U+200B, U+200D and U+2060 are not matched.
  • The checked string and the stored string are both name.trim(), so a tab or newline at the edges is trimmed away and one inside is refused.
  • /update-user, /admin/create-user, /admin/update-user and /admin/set-role are in disabledPaths. The only server-side signUpEmail callers (Setup and Sign up) go through the database hook.
  • Check order (admission, blank, length, characters) runs before Better Auth's email lookup, so known and unknown emails answer the same. A refused name leaves no User, Account or Learner, and Setup rolls back the Instance claim. Both are tested.

Codex's point, decided by the maintainer

Codex flagged that Setup and Sign up return the typed name in the form's failure data (typed). The orchestrator's brief had stated decision 4 more broadly than C33c decided it ("or HTTP body"). The C33c decision covers the Better Auth APIError and the HTTP sign-up endpoint, and both are tested not to echo the name. The forms give the name back only to the person who typed it, the same as every other refusal on those forms, and Svelte escapes it, while SvelteKit's origin check refuses cross-site posts. Decided (maintainer, 2026-10-22): merge as is.

Not blocking (Fable), carried to later cards

  • No user.update.before hook exists yet, so a future name-editing path would skip the name rules. Appended to C38: if it adds that hook, the hook runs the whole nameRefusal, characters included.
  • U+2028 and U+2029 (line and paragraph separators) stay allowed under the decision and can break a line in the nav. Noted for the maintainer.

@GauranshMathur
GauranshMathur merged commit 5db366d into main Oct 22, 2026
10 checks passed
@GauranshMathur
GauranshMathur deleted the C33d-name-characters branch October 22, 2026 04:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant