ENG-3466: Add username character validation on user creation#7953
ENG-3466: Add username character validation on user creation#7953
Conversation
Restrict usernames to alphanumeric characters, underscores, and hyphens (1-100 chars) to prevent special characters from breaking invite links. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. 2 Skipped Deployments
|
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #7953 +/- ##
==========================================
+ Coverage 85.03% 85.04% +0.01%
==========================================
Files 629 629
Lines 40971 40997 +26
Branches 4763 4769 +6
==========================================
+ Hits 34838 34865 +27
Misses 5052 5052
+ Partials 1081 1080 -1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review: ENG-3466 — Username character validation
The change is well-scoped: replace a space-only check with a strict allowlist regex, add parametrized tests for both valid and invalid cases. The logic is correct and the approach is sound. Three findings below.
Critical (Must Fix)
Error message does not match the allowed pattern.
USERNAME_PATTERN is [a-zA-Z0-9._-]{1,100}, which allows dots. The error message says "may only contain letters, numbers, underscores, and hyphens" — dots are absent. john.doe is tested as a valid username but a user reading the error would not know to try it. The message and pattern must agree. See inline comment on line 18 of user.py.
Suggestions
Missing follow-up issue for the email template fix.
The PR description says the template URL-encoding fix will come in a separate PR, but the pre-merge checklist has "No followup issues" checked and no issue linked. The root-cause template (user_invite.html) still interpolates {{username}} raw into the URL. The new validation blocks the worst offenders (&, =, <, >), but the fix is incomplete — dots are now allowed and are URL-safe, but this is fragile reasoning. A tracked follow-up issue should exist before this merges. See inline comment on the template file.
Invalid-username test does not assert on the error message.
pytest.raises(ValueError) without a match= argument passes for any ValueError, including one from an unrelated field. Add match="Usernames must be" to make the test specifically verify username validation is firing. See inline comment on line 46 of the test file.
re.fullmatch with a string constant recompiles on each call.
Minor: prefer re.compile(USERNAME_PATTERN) at module level and call .fullmatch() on the compiled object. CPython's regex cache means this is not a performance problem in practice, but it makes the intent explicit. See inline comment on line 44 of user.py.
Nice to Have
The parametrized valid-username test asserts user.username == username. That's correct and sufficient. No issues there.
🔬 Codegraph: connected (46831 nodes) — note: graph snapshot reflects main (795eb71), not the PR head, so cross-repo impact analysis reflects pre-PR state. No architecture violations detected for this change.
💡 Write /code-review in a comment to re-run this review.
- Include periods in the validation error message to match the pattern - Add match="Usernames must be" to pytest.raises for specificity Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ticket ENG-3466
Description Of Changes
Usernames were only validated to reject spaces, allowing special characters like
&,=,<,>that break invite links when interpolated into the email template URL. This tightens the backend Pydantic validator onUserCreateto restrict usernames to[a-zA-Z0-9._-]{1,100}.This is a creation-only change — existing users are unaffected since the validator only runs on
UserCreate, not on login (UserLogin), read (UserResponse), or update (UserUpdate) paths.The email template logic will be updated in a separate PR.
Code Changes
UserCreate.validate_usernamewithre.fullmatchagainst aUSERNAME_PATTERNconstant ([a-zA-Z0-9._-]{1,100})&,=,<, empty, 101 chars) and valid usernames (plain, hyphen, underscore, mixed, single char, 100 chars)Steps to Confirm
test-user_123) — should succeeduser&name,user name, empty) — should return a validation errorPre-Merge Checklist
CHANGELOG.mdupdatedmaindowngrade()migration is correct and works