From 89c17d3b23e2a23320ad135b4e8f0a14fdd91bda Mon Sep 17 00:00:00 2001 From: kolaente Date: Wed, 25 Feb 2026 13:34:55 +0100 Subject: [PATCH] feat(api): enforce password validation on reset and update flows Add bcrypt_password validation to password reset and update endpoints: - Add validation tag to PasswordReset.NewPassword struct field - Add validation tag to UserPassword.NewPassword struct field - Add c.Validate() calls in both handlers - Fix off-by-one error in bcrypt_password validator (use <= 72 not < 72) Password requirements: min 8 chars, max 72 bytes (bcrypt limit) --- pkg/routes/api/v1/user_password_reset.go | 5 +++++ pkg/routes/api/v1/user_update_password.go | 7 ++++++- pkg/user/user_password_reset.go | 2 +- pkg/user/validator.go | 2 +- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/pkg/routes/api/v1/user_password_reset.go b/pkg/routes/api/v1/user_password_reset.go index 09a2dbc53..b91a28a7a 100644 --- a/pkg/routes/api/v1/user_password_reset.go +++ b/pkg/routes/api/v1/user_password_reset.go @@ -44,6 +44,11 @@ func UserResetPassword(c *echo.Context) error { return echo.NewHTTPError(http.StatusBadRequest, "No password provided.").Wrap(err) } + // Validate the password + if err := c.Validate(pwReset); err != nil { + return err + } + s := db.NewSession() defer s.Close() diff --git a/pkg/routes/api/v1/user_update_password.go b/pkg/routes/api/v1/user_update_password.go index 32a34de11..0172a21ec 100644 --- a/pkg/routes/api/v1/user_update_password.go +++ b/pkg/routes/api/v1/user_update_password.go @@ -29,7 +29,7 @@ import ( // UserPassword holds a user password. Used to update it. type UserPassword struct { OldPassword string `json:"old_password"` - NewPassword string `json:"new_password"` + NewPassword string `json:"new_password" valid:"bcrypt_password" minLength:"8" maxLength:"72"` } // UserChangePassword is the handler to change a users password @@ -58,6 +58,11 @@ func UserChangePassword(c *echo.Context) error { return echo.NewHTTPError(http.StatusBadRequest, "No password provided.").Wrap(err) } + // Validate the new password + if err := c.Validate(newPW); err != nil { + return err + } + if newPW.OldPassword == "" { return user.ErrEmptyOldPassword{} } diff --git a/pkg/user/user_password_reset.go b/pkg/user/user_password_reset.go index 27d8cedfc..d2a534392 100644 --- a/pkg/user/user_password_reset.go +++ b/pkg/user/user_password_reset.go @@ -27,7 +27,7 @@ type PasswordReset struct { // The previously issued reset token. Token string `json:"token"` // The new password for this user. - NewPassword string `json:"new_password"` + NewPassword string `json:"new_password" valid:"bcrypt_password" minLength:"8" maxLength:"72"` } // ResetPassword resets a users password. It returns the ID of the user whose diff --git a/pkg/user/validator.go b/pkg/user/validator.go index 67aaedae5..0f66f9aa2 100644 --- a/pkg/user/validator.go +++ b/pkg/user/validator.go @@ -54,7 +54,7 @@ func init() { return false } - return len([]byte(str)) < 72 + return len([]byte(str)) <= 72 } govalidator.TagMap["language"] = i18n.HasLanguage