From 6942aef40504f80a1011fd8976d754f649ffa5a2 Mon Sep 17 00:00:00 2001 From: Fabrizio Fallico Date: Tue, 4 Feb 2025 18:25:57 +0100 Subject: [PATCH 1/3] fix: improve email validation using net/mail package --- types/email.go | 5 +++-- types/email_test.go | 5 +++++ types/regexes.go | 11 ----------- 3 files changed, 8 insertions(+), 13 deletions(-) delete mode 100644 types/regexes.go diff --git a/types/email.go b/types/email.go index e4a1cbda..16d78eae 100644 --- a/types/email.go +++ b/types/email.go @@ -3,6 +3,7 @@ package types import ( "encoding/json" "errors" + "net/mail" ) // ErrValidationEmail is the sentinel error returned when an email fails validation @@ -14,7 +15,7 @@ var ErrValidationEmail = errors.New("email: failed to pass regex validation") type Email string func (e Email) MarshalJSON() ([]byte, error) { - if !emailRegex.MatchString(string(e)) { + if _, err := mail.ParseAddress(string(e)); err != nil { return nil, ErrValidationEmail } @@ -32,7 +33,7 @@ func (e *Email) UnmarshalJSON(data []byte) error { } *e = Email(s) - if !emailRegex.MatchString(s) { + if _, err := mail.ParseAddress(s); err != nil { return ErrValidationEmail } diff --git a/types/email_test.go b/types/email_test.go index 736056b1..f89787cd 100644 --- a/types/email_test.go +++ b/types/email_test.go @@ -22,6 +22,11 @@ func TestEmail_MarshalJSON_Validation(t *testing.T) { expectedJSON: []byte(`{"email":"validemail@openapicodegen.com"}`), expectedError: nil, }, + "it should succeed marshalling a valid email and return valid JSON populated with the email with valid separators": { + email: Email("validemail+with_valid-separator{like}~these*ones@openapicodegen.com"), + expectedJSON: []byte(`{"email":"validemail+with_valid-separator{like}~these*ones@openapicodegen.com"}`), + expectedError: nil, + }, "it should fail marshalling an invalid email and return a validation error": { email: Email("invalidemail"), expectedJSON: nil, diff --git a/types/regexes.go b/types/regexes.go deleted file mode 100644 index 94f17df5..00000000 --- a/types/regexes.go +++ /dev/null @@ -1,11 +0,0 @@ -package types - -import "regexp" - -const ( - emailRegexString = "^(?:(?:(?:(?:[a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(?:\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|(?:(?:\\x22)(?:(?:(?:(?:\\x20|\\x09)*(?:\\x0d\\x0a))?(?:\\x20|\\x09)+)?(?:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(?:(?:(?:\\x20|\\x09)*(?:\\x0d\\x0a))?(\\x20|\\x09)+)?(?:\\x22))))@(?:(?:(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])(?:[a-zA-Z]|\\d|-|\\.|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(?:(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])(?:[a-zA-Z]|\\d|-|\\.|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$" -) - -var ( - emailRegex = regexp.MustCompile(emailRegexString) -) From bb4dd9279e1216414e246821f5f966bd3926b825 Mon Sep 17 00:00:00 2001 From: Fabrizio Fallico Date: Wed, 5 Feb 2025 09:24:55 +0100 Subject: [PATCH 2/3] fix: improve email validation using net/mail package --- types/email.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/types/email.go b/types/email.go index 16d78eae..234e7ccf 100644 --- a/types/email.go +++ b/types/email.go @@ -15,11 +15,12 @@ var ErrValidationEmail = errors.New("email: failed to pass regex validation") type Email string func (e Email) MarshalJSON() ([]byte, error) { - if _, err := mail.ParseAddress(string(e)); err != nil { + m, err := mail.ParseAddress(string(e)) + if err != nil { return nil, ErrValidationEmail } - return json.Marshal(string(e)) + return json.Marshal(m.Address) } func (e *Email) UnmarshalJSON(data []byte) error { @@ -32,10 +33,11 @@ func (e *Email) UnmarshalJSON(data []byte) error { return err } - *e = Email(s) - if _, err := mail.ParseAddress(s); err != nil { + m, err := mail.ParseAddress(s) + if err != nil { return ErrValidationEmail } + *e = Email(m.Address) return nil } From 77d96ee302c247ab848da7f77509f8d53faeaa9b Mon Sep 17 00:00:00 2001 From: Marcin Romaszewicz Date: Mon, 23 Feb 2026 23:10:51 -0800 Subject: [PATCH 3/3] fix: update test expectations for email validation behavior change Invalid emails no longer persist their value after failed UnmarshalJSON validation, since the new net/mail-based validation sets the value only after successful parsing. Co-Authored-By: Claude Opus 4.6 --- types/email_test.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/types/email_test.go b/types/email_test.go index f89787cd..ef31119d 100644 --- a/types/email_test.go +++ b/types/email_test.go @@ -76,10 +76,7 @@ func TestEmail_UnmarshalJSON_RequiredEmail_Validation(t *testing.T) { "it should fail validating an invalid email": { jsonStr: `{"email":"not-an-email"}`, expectedError: ErrValidationEmail, - expectedEmail: func() Email { - e := Email("not-an-email") - return e - }(), + expectedEmail: Email(""), }, "it should fail validating an empty email": { jsonStr: `{"email":""}`, @@ -139,7 +136,7 @@ func TestEmail_UnmarshalJSON_NullableEmail_Validation(t *testing.T) { jsonStr: `{"email":"not-an-email"}`, expectedError: ErrValidationEmail, expectedEmail: func() *Email { - e := Email("not-an-email") + e := Email("") return &e }(), },