fix(dashboard-auth): reject passwords longer than bcrypt's 72-byte limit#598
Merged
Conversation
Fixes #597. `bcrypt.hashpw` enforces a hard 72-byte input limit on the encoded password (bcrypt 5.x raises `ValueError("password cannot be longer than 72 bytes ...")`). `_hash_password` calls `bcrypt.hashpw` directly, and the `/password/setup` and `/password/change` endpoints only validate the 8-character minimum before hashing. So a password whose UTF-8 encoded length exceeds 72 bytes (long ASCII strings, emoji-only strings, etc.) propagated as an unhandled `ValueError` and surfaced as a 500 with a generic "Unexpected error" on the dashboard frontend. Add a shared `_validate_password_length` helper that enforces both the existing 8-character minimum and a new 72-UTF-8-byte maximum, and route both endpoints through it. Failures now return HTTP 422 with code `password_too_long` and a message that references the limit, so the dashboard can render a clear, actionable error instead of swallowing the failure as 'Unexpected error'. The limit is measured against the UTF-8 encoded byte length to match bcrypt's own ceiling exactly (multi-byte characters like emoji count for more than one byte). Regression coverage exercises 73-byte ASCII rejection, exact 72-byte acceptance, emoji-only multi-byte rejection, and the `/password/change` `new_password` path.
Owner
Author
|
@codex review |
|
Codex Review: Didn't find any major issues. Another round soon, please! ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
This was referenced May 13, 2026
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #597.
Root cause
bcrypt.hashpwenforces a hard 72-byte input limit on the encoded password (bcrypt 5.x raisesValueError("password cannot be longer than 72 bytes ..."))._hash_passwordinapp/modules/dashboard_auth/service.pycallsbcrypt.hashpwdirectly, and the two API entry points that lead there only validatelen(password) < 8before hashing.So a password whose UTF-8 encoded length exceeds 72 bytes propagated as an unhandled
ValueErrorand surfaced as a 500 with a generic "Unexpected error" on the dashboard frontend (the symptom reported in #597).Fix
_validate_password_lengthhelper inapp/modules/dashboard_auth/api.pythat enforces both the existing 8-character minimum and a new 72-UTF-8-byte maximum.POST /api/dashboard-auth/password/setupandPOST /api/dashboard-auth/password/changethrough it, so the two endpoints validate identically before hashing.DashboardValidationError(HTTP 422) with codepassword_too_longand a message that references the 72-byte limit, so the dashboard can render a clear, actionable error.The limit is measured against the UTF-8 encoded byte length, not codepoint count, so multi-byte characters (emoji, non-ASCII letters) count for as many bytes as bcrypt itself counts. This matches bcrypt's own ceiling exactly.
Why 72 bytes specifically
bcrypt(the underlying password hashing primitive used by all major language bindings) silently truncates input after 72 bytes when hashing on most platforms, but the Pythonbcrypt5.x package explicitly rejects oversize inputs withValueErrorinstead of silently truncating. Anything that exceeds bcrypt's 72-byte ceiling cannot be hashed at all, so the right place to surface that is at the API layer, not as an unhandledValueError.Tests
Added four integration tests in
tests/integration/test_dashboard_password_auth.py:Verification on this branch:
OpenSpec
Added an OpenSpec change folder
openspec/changes/validate-password-length/capturing the new requirement under theadmin-authcapability (length is bounded by bcrypt's input limit, measured in UTF-8 bytes, applied to bothsetupandchange).Migration / Compatibility
Hotfix candidate for v1.17.1
This is a small, scoped fix on the dashboard-auth API surface with no runtime impact. It is a good candidate for the next patch release.