email: validate with net/mail.ParseAddress

Replace the RFC 5322 mega-regexp with net/mail.ParseAddress, which is the
standard library's address parser and far easier to audit. A bare address
is required: inputs with a display name, angle brackets, a comment, or
trailing content are rejected, as is an address list.

Behaviour change: ParseAddress does not require the domain to contain a
dot, so "alice@localhost" now validates. Layer Match or a DNS lookup on
top if a stricter domain is needed.

Also rewrites FuzzEmail, which previously asserted that *every* input is
invalid, into checks that Email never returns an unexpected error type,
never panics, and gives a stable verdict.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit was merged in pull request #8.
This commit is contained in:
2026-09-07 12:53:02 +00:00
committed by aneurin
co-authored by Claude Sonnet 5
parent 82611a5e96
commit a992065d6c
2 changed files with 35 additions and 13 deletions
+10 -6
View File
@@ -1,19 +1,23 @@
package validate
import (
"regexp"
"net/mail"
)
var (
ErrInvalidEmail = NewError("invalid email address")
)
// Based on https://stackoverflow.com/a/201378
var emailRegexp = regexp.MustCompile("^(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\\])$")
// Email validates an email address.
// Email validates an email address using net/mail.ParseAddress.
//
// Only a bare address is accepted (alice@example.com). Anything with a
// display name, angle brackets, a comment, or trailing content is rejected,
// as is a list of addresses. Note that ParseAddress does not require the
// domain to have a dot, so "alice@localhost" is considered valid; layer on
// Match or a DNS check if you need to be stricter.
func Email(value string) error {
if !emailRegexp.MatchString(value) {
addr, err := mail.ParseAddress(value)
if err != nil || addr.Name != "" || addr.Address != value {
return ErrInvalidEmail
}