From 36cc06c125954dec6673219dafa084e13cc14534 Mon Sep 17 00:00:00 2001 From: Ralph Slooten Date: Thu, 15 Jan 2026 11:59:59 +1300 Subject: [PATCH] Security: Ensure SMTP TO & FROM addresses are RFC 5322 compliant and prevent header injection ([GHSA-54wq-72mp-cq7c](https://github.com/axllent/mailpit/security/advisories/GHSA-54wq-72mp-cq7c)) --- internal/smtpd/smtpd.go | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/internal/smtpd/smtpd.go b/internal/smtpd/smtpd.go index 706e054..032fa5c 100644 --- a/internal/smtpd/smtpd.go +++ b/internal/smtpd/smtpd.go @@ -15,6 +15,7 @@ import ( "io/fs" "log" "net" + "net/mail" "os" "regexp" "strconv" @@ -421,7 +422,7 @@ loop: break } - match := mailFromRE.FindStringSubmatch(args) + match := extractAndValidateAddress(mailFromRE, args) if match == nil { s.writef("501 5.5.4 Syntax error in parameters or arguments (invalid FROM parameter)") } else { @@ -477,7 +478,7 @@ loop: break } - match := rcptToRE.FindStringSubmatch(args) + match := extractAndValidateAddress(rcptToRE, args) if match == nil { s.writef("501 5.5.4 Syntax error in parameters or arguments (invalid TO parameter)") } else { @@ -1014,3 +1015,23 @@ func (s *session) handleAuthCramMD5() (bool, error) { return authenticated, err } + +// Extract and validate email address from a regex match. +// This ensures that only RFC 5322 email addresses are accepted (if set). +func extractAndValidateAddress(re *regexp.Regexp, args string) []string { + match := re.FindStringSubmatch(args) + if match == nil || strings.Contains(match[1], " ") { + return nil + } + + // first argument will be the email address, validate it if not empty + if match[1] != "" { + // fmt.Println("Validating email address:", match[1]) + _, err := mail.ParseAddress(match[1]) + if err != nil { + return nil + } + } + + return match +}