mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-07 12:54:33 +00:00
fix(clients): reject spaces, '/', '\' and control chars in client email
Client emails containing a slash broke the path-param routes (edit/delete/view returned 404 / "client not found"), leaving stale records that could only be cleared with manual SQLite edits. Validate the email on both the backend (Create + Update, which also covers the bulk paths) and the frontend (Zod) so these characters are rejected at save time with a clear, localized message across all 13 locales. Closes #4695
This commit is contained in:
@@ -408,6 +408,15 @@ type ClientCreatePayload struct {
|
||||
InboundIds []int `json:"inboundIds"`
|
||||
}
|
||||
|
||||
func validateClientEmail(email string) error {
|
||||
for _, r := range email {
|
||||
if r == '/' || r == '\\' || r == ' ' || r < 0x20 || r == 0x7f {
|
||||
return common.NewError("client email contains an invalid character:", email)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ClientService) Create(inboundSvc *InboundService, payload *ClientCreatePayload) (bool, error) {
|
||||
if payload == nil {
|
||||
return false, common.NewError("empty payload")
|
||||
@@ -416,6 +425,9 @@ func (s *ClientService) Create(inboundSvc *InboundService, payload *ClientCreate
|
||||
if strings.TrimSpace(client.Email) == "" {
|
||||
return false, common.NewError("client email is required")
|
||||
}
|
||||
if err := validateClientEmail(client.Email); err != nil {
|
||||
return false, err
|
||||
}
|
||||
if len(payload.InboundIds) == 0 {
|
||||
return false, common.NewError("at least one inbound is required")
|
||||
}
|
||||
@@ -581,6 +593,9 @@ func (s *ClientService) Update(inboundSvc *InboundService, id int, updated model
|
||||
if strings.TrimSpace(updated.Email) == "" {
|
||||
return false, common.NewError("client email is required")
|
||||
}
|
||||
if err := validateClientEmail(updated.Email); err != nil {
|
||||
return false, err
|
||||
}
|
||||
if updated.SubID == "" {
|
||||
updated.SubID = existing.SubID
|
||||
}
|
||||
|
||||
32
web/service/client_email_validation_test.go
Normal file
32
web/service/client_email_validation_test.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package service
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestValidateClientEmail(t *testing.T) {
|
||||
valid := []string{
|
||||
"alice",
|
||||
"alice@example.com",
|
||||
"user-123_test.name",
|
||||
"имя",
|
||||
}
|
||||
for _, email := range valid {
|
||||
if err := validateClientEmail(email); err != nil {
|
||||
t.Errorf("validateClientEmail(%q) = %v, want nil", email, err)
|
||||
}
|
||||
}
|
||||
|
||||
invalid := []string{
|
||||
"i6dui/",
|
||||
"a/b",
|
||||
"client with spaces",
|
||||
"back\\slash",
|
||||
"tab\there",
|
||||
"new\nline",
|
||||
"\x7fdelete",
|
||||
}
|
||||
for _, email := range invalid {
|
||||
if err := validateClientEmail(email); err == nil {
|
||||
t.Errorf("validateClientEmail(%q) = nil, want error", email)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user