Skip to content

Enforce username rules on every account-creation path - #20

Merged
phulin merged 1 commit into
phulin:mainfrom
jackowayed:claude/username-validation-security-coua1g
Aug 16, 2026
Merged

phulin merged 1 commit into
phulin:mainfrom
jackowayed:claude/username-validation-security-coua1g

Conversation

@jackowayed

Copy link
Copy Markdown
Contributor

Username format rules (2-20 chars, [a-zA-Z0-9_] only) were enforced only by POST /api/auth/username/check, which is advisory. The four paths that actually insert users — email registration completion, OAuth completion, and native Apple/Google sign-in — ran only the profanity filter, so a 300-character username containing HTML, newlines, and unicode was accepted and stored.

Usernames are rendered directly into push notification bodies ("${user.username} sent you an Oy!"), so a stored username is attacker -controlled lock-screen copy for anyone who receives an Oy.

validateUsername in lib.ts is now the single gate: length, character set, and profanity. normalizeUsername (previously dead code) trims and lowercases, matching how usernames are stored and looked up. Both are applied at each creation path, and the check endpoint now delegates to the same function so advisory results match what creation accepts.

Also removes fetchUserByUsername, which had no callers.

Claude-Session: https://claude.ai/code/session_01PvpccXvmDzszQ1fCJnaChX

@jackowayed
jackowayed force-pushed the claude/username-validation-security-coua1g branch from 6b46199 to e686221 Compare August 11, 2026 16:04
@jackowayed

Copy link
Copy Markdown
Contributor Author

#20

@jackowayed jackowayed closed this Aug 11, 2026
@jackowayed

Copy link
Copy Markdown
Contributor Author

oops meant to close the one on my fork

@phulin

phulin commented Aug 16, 2026

Copy link
Copy Markdown
Owner

can your claude resolve the conflict please?

Username format rules (2-20 chars, [a-zA-Z0-9_] only) were enforced only
by POST /api/auth/username/check, which is advisory. The four paths that
actually insert users — email registration completion, OAuth completion,
and native Apple/Google sign-in — ran only the profanity filter, so a
300-character username containing HTML, newlines, and unicode was
accepted and stored.

Usernames are rendered directly into push notification bodies
("${user.username} sent you an Oy!"), so a stored username is attacker
-controlled lock-screen copy for anyone who receives an Oy.

validateUsername in lib.ts is now the single gate: length, character set,
and profanity. normalizeUsername (previously dead code) trims and
lowercases, matching how usernames are stored and looked up. Both are
applied at each creation path, and the check endpoint now delegates to
the same function so advisory results match what creation accepts.

Also removes fetchUserByUsername, which had no callers.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PvpccXvmDzszQ1fCJnaChX
Co-authored-by: jackowayed <18899+jackowayed@users.noreply.github.com>
@jackowayed
jackowayed force-pushed the claude/username-validation-security-coua1g branch from e686221 to af4350b Compare August 16, 2026 13:42
@phulin
phulin merged commit fcf2d21 into phulin:main Aug 16, 2026
1 check passed
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.

3 participants