mirror of
https://github.com/axllent/mailpit.git
synced 2026-06-27 22:46:09 +00:00
Merge branch 'feature/GHSA-54wq-72mp-cq7c' into develop
This commit is contained in:
@@ -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,33 @@ 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] != "" {
|
||||
a, err := mail.ParseAddress(match[1])
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
parts := strings.SplitN(a.Address, "@", 2)
|
||||
|
||||
if len(parts) != 2 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// https://datatracker.ietf.org/doc/html/rfc5321#section-4.5.3.1
|
||||
if len(parts[0]) > 64 || len(parts[1]) > 255 || len(a.Address) > 256 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return match
|
||||
}
|
||||
|
||||
@@ -104,6 +104,20 @@ func TestCmdEHLO(t *testing.T) {
|
||||
// See RFC 2821 section 4.1.4 for more detail.
|
||||
cmdCode(t, conn, "MAIL FROM:<sender@example.com>", "250")
|
||||
cmdCode(t, conn, "RCPT TO:<recipient@example.com>", "250")
|
||||
|
||||
// test invalid addresses & header injection
|
||||
cmdCode(t, conn, "RCPT TO: <recipientrecipientrecipientrecipientrecipientrecipientrecipientrecipientrecipientrecipientrecipientrecipient@exampleexampleexampleexampleexampleexampleexample.com>", "501") // too long
|
||||
cmdCode(t, conn, "RCPT TO: <recipientrecipientrecipientreciprecipientrecipientrecipientrecipt@exaample.com>", "501")
|
||||
cmdCode(t, conn, "RCPT TO: <recipient@examplexampleexampleexampleexampleexampleexampleexamplexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexamplexampleexampleexampleexampleexampleexampleexamplexampleexampleexampleexampleexampleexampleexample.com>", "501")
|
||||
cmdCode(t, conn, "RCPT TO: <r@examplexampleexampleexampleexampleexampleexampleexamplexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexamplexampleexampleexampleexampleexampleexampleexamplexampleexampleexampleexampleexampleexampleexample.com>", "250") // valid
|
||||
cmdCode(t, conn, "RCPT TO:<recipientexample.com>", "501")
|
||||
cmdCode(t, conn, "RCPT TO: <recipientexample.com>", "501")
|
||||
cmdCode(t, conn, "RCPT TO: <recipientexample.com>", "501")
|
||||
cmdCode(t, conn, "RCPT TO:<recipient\rexample.com>", "501")
|
||||
cmdCode(t, conn, "RCPT TO: <recipient\rexample.com>", "501")
|
||||
cmdCode(t, conn, "RCPT TO: <recipient\rexample.com>", "501")
|
||||
cmdCode(t, conn, "RCPT TO: <>", "501") // empty address not allowed here
|
||||
|
||||
cmdCode(t, conn, "EHLO host.example.com", "250")
|
||||
cmdCode(t, conn, "DATA", "503")
|
||||
|
||||
@@ -145,6 +159,21 @@ func TestCmdMAIL(t *testing.T) {
|
||||
// MAIL with seemingly valid but noncompliant FROM arg (double space after the colon) should return 501 syntax error
|
||||
cmdCode(t, conn, "MAIL FROM: <sender@example.com>", "501")
|
||||
|
||||
// test invalid addresses & header injection
|
||||
cmdCode(t, conn, "MAIL FROM: <sendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersendersender@exampleexampleexampleexampleexampleexampleexample.com>", "501") // too long
|
||||
cmdCode(t, conn, "MAIL FROM: <sendersendersendersendersendersendersendersendersendersendersender@exaample.com>", "501")
|
||||
cmdCode(t, conn, "MAIL FROM: <sender@examplexampleexampleexampleexampleexampleexampleexamplexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexamplexampleexampleexampleexampleexampleexampleexamplexampleexampleexampleexampleexampleexampleexample.com>", "501")
|
||||
cmdCode(t, conn, "MAIL FROM: <s@examplexampleexampleexampleexampleexampleexampleexamplexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexampleexamplexampleexampleexampleexampleexampleexampleexamplexampleexampleexampleexampleexampleexampleexample.com>", "250") // valid
|
||||
cmdCode(t, conn, "MAIL FROM:<sender\rexample.com>", "501")
|
||||
cmdCode(t, conn, "MAIL FROM: <sender\rexample.com>", "501")
|
||||
cmdCode(t, conn, "MAIL FROM: <sender\rexample.com>", "501")
|
||||
cmdCode(t, conn, "MAIL FROM:<senderexample.com>", "501")
|
||||
cmdCode(t, conn, "MAIL FROM: <senderexample.com>", "501")
|
||||
cmdCode(t, conn, "MAIL FROM: <senderexample.com>", "501")
|
||||
cmdCode(t, conn, "MAIL FROM: < sender@example.com >", "501")
|
||||
cmdCode(t, conn, "MAIL FROM: < sender@example.com>", "501")
|
||||
cmdCode(t, conn, "MAIL FROM: <sender@example.com >", "501")
|
||||
|
||||
// MAIL with valid SIZE parameter should return 250 Ok
|
||||
cmdCode(t, conn, "MAIL FROM:<sender@example.com> SIZE=1000", "250")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user