From f9ddfa869e13d83d05c82d76b3ad90c867e734e4 Mon Sep 17 00:00:00 2001 From: Christoph Haas Date: Mon, 23 Mar 2026 00:13:35 +0100 Subject: [PATCH] Validate email local part when creating users - Check length (1-64 characters) - Allow only valid email characters (RFC 5321) - Return ValidationError with descriptive message --- backend/internal/api/handlers/users.go | 28 ++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/backend/internal/api/handlers/users.go b/backend/internal/api/handlers/users.go index 3571c3e..ea2d86a 100644 --- a/backend/internal/api/handlers/users.go +++ b/backend/internal/api/handlers/users.go @@ -3,7 +3,9 @@ package handlers import ( "log" "net/http" + "regexp" "strconv" + "strings" "github.com/gin-gonic/gin" "github.com/imc-vibe/backend/internal/db" @@ -129,6 +131,12 @@ func (h *UserHandler) Create(c *gin.Context) { return } + // Validate email local part (before @) + if err := validateEmailLocalPart(req.Email); err != nil { + Error(c, http.StatusBadRequest, err.Error()) + return + } + existing, _ := h.db.GetUserByEmail(req.Email) if existing != nil { Error(c, http.StatusConflict, "user already exists") @@ -254,3 +262,23 @@ func (h *UserHandler) ListAll(c *gin.Context) { Success(c, users) } + +var emailLocalPartRegex = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+\\-/=?^_`{|}~-]+$") + +func validateEmailLocalPart(email string) error { + parts := strings.Split(email, "@") + if len(parts) != 2 { + return &ValidationError{Message: "invalid email format"} + } + + localPart := parts[0] + if len(localPart) < 1 || len(localPart) > 64 { + return &ValidationError{Message: "username must be between 1 and 64 characters"} + } + + if !emailLocalPartRegex.MatchString(localPart) { + return &ValidationError{Message: "username can only contain letters, numbers, and common special characters"} + } + + return nil +}