VYPR
High severityNVD Advisory· Published Sep 29, 2025· Updated Sep 30, 2025

go-mail has insufficient address encoding when passing mail addresses to the SMTP client

CVE-2025-59937

Description

go-mail is a comprehensive library for sending mails with Go. In versions 0.7.0 and below, due to incorrect handling of the mail.Address values when a sender- or recipient address is passed to the corresponding MAIL FROM or RCPT TO commands of the SMTP client, there is a possibility of wrong address routing or even ESMTP parameter smuggling. For successful exploitation, it is required that the user's code allows for arbitrary mail address input (i. e. through a web form or similar). If only static mail addresses are used (i. e. in a config file) and the mail addresses in use do not consist of quoted local parts, this should not affect users. This issue is fixed in version 0.7.1

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

go-mail library v0.7.0 and below has an address parsing flaw enabling ESMTP parameter smuggling and misrouting.

Vulnerability

Description CVE-2025-59937 is an address parsing vulnerability in the go-mail library (versions 0.7.0 and earlier) that occurs when user-supplied mail addresses are passed to the SMTP client's MAIL FROM or RCPT TO commands. The library incorrectly handles quoted local-parts containing '@', allowing an attacker to craft an email address that causes the parser to extract and route to an unintended domain instead of the RFC-compliant target [1][3].

Exploitation

Successful exploitation requires that the application allows arbitrary mail address input (e.g., from a web form). If only static addresses from a configuration file are used and these addresses do not contain quoted local parts, the vulnerability is not exploitable [1]. An example payload such as "foo@psres.net> ORCPT=admin@admin.com"@test.com demonstrates how the parser misinterprets the address, leading to delivery to the wrong domain (foo@psres.net) instead of test.com [3]. This can also be leveraged for ESMTP parameter smuggling, injecting additional SMTP commands or parameters.

Impact

An attacker can cause email messages to be misrouted to unintended recipients or inject arbitrary ESMTP parameters, potentially enabling further SMTP-level attacks. The impact includes confidentiality violations (sensitive data sent to wrong address) and integrity issues (injection of SMTP commands).

Mitigation

The issue is fixed in version 0.7.1 of the library [1][4]. Users are advised to upgrade immediately. For those unable to upgrade, they should ensure that mail addresses are not taken from untrusted sources and that any dynamic addresses are validated according to RFC 5322.

AI Insight generated on May 19, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/wneessen/go-mailGo
< 0.7.10.7.1

Affected products

2
  • Mail/Mailllm-fuzzy
    Range: <=0.7.0
  • wneessen/go-mailv5
    Range: < 0.7.1

Patches

1
42e92cfe027b

Merge pull request #496 from wneessen/bugfix/495_mail-address-parsing

https://github.com/wneessen/go-mailWinni NeessenSep 26, 2025via ghsa
8 files changed · +356 62
  • b64linebreaker.go+7 7 modified
    @@ -42,7 +42,7 @@ type base64LineBreaker struct {
     func (l *base64LineBreaker) Write(data []byte) (numBytes int, err error) {
     	if l.out == nil {
     		err = errors.New("no io.Writer set for base64LineBreaker")
    -		return
    +		return numBytes, err
     	}
     	if l.used+len(data) < MaxBodyLength {
     		copy(l.line[l.used:], data)
    @@ -52,25 +52,25 @@ func (l *base64LineBreaker) Write(data []byte) (numBytes int, err error) {
     
     	_, err = l.out.Write(l.line[0:l.used])
     	if err != nil {
    -		return
    +		return numBytes, err
     	}
     	excess := MaxBodyLength - l.used
     	l.used = 0
     
     	numBytes, err = l.out.Write(data[0:excess])
     	if err != nil {
    -		return
    +		return numBytes, err
     	}
     
     	_, err = l.out.Write(newlineBytes)
     	if err != nil {
    -		return
    +		return numBytes, err
     	}
     
     	var n int
     	n, err = l.Write(data[excess:]) // recurse
     	numBytes += n
    -	return
    +	return numBytes, err
     }
     
     // Close finalizes the base64LineBreaker, writing any remaining buffered data and appending a newline.
    @@ -85,10 +85,10 @@ func (l *base64LineBreaker) Close() (err error) {
     	if l.used > 0 {
     		_, err = l.out.Write(l.line[0:l.used])
     		if err != nil {
    -			return
    +			return err
     		}
     		_, err = l.out.Write(newlineBytes)
     	}
     
    -	return
    +	return err
     }
    
  • client.go+2 2 modified
    @@ -1261,7 +1261,7 @@ func (c *Client) SendWithSMTPClient(client *smtp.Client, messages ...*Msg) (retu
     			Reason: ErrConnCheck, errlist: []error{err}, isTemp: isTempError(err),
     			errcode: errorCode(err), enhancedStatusCode: enhancedStatusCode(err, escSupport),
     		}
    -		return
    +		return returnErr
     	}
     
     	var errs []error
    @@ -1279,7 +1279,7 @@ func (c *Client) SendWithSMTPClient(client *smtp.Client, messages ...*Msg) (retu
     		}
     	}
     
    -	return
    +	return returnErr
     }
     
     // auth attempts to authenticate the client using SMTP AUTH mechanisms. It checks the connection,
    
  • doc.go+1 1 modified
    @@ -11,4 +11,4 @@ package mail
     
     // VERSION indicates the current version of the package. It is also attached to the default user
     // agent string.
    -const VERSION = "0.7.0"
    +const VERSION = "0.7.1"
    
  • eml.go+2 2 modified
    @@ -538,7 +538,7 @@ func parseMultiPartHeader(multiPartHeader string) (header string, optional map[s
     	headerSplit := strings.Split(multiPartHeader, ";")
     	header = headerSplit[0]
     	if len(headerSplit) == 1 {
    -		return
    +		return header, optional
     	}
     	for _, opt := range headerSplit[1:] {
     		optString := strings.TrimLeft(opt, " ")
    @@ -547,7 +547,7 @@ func parseMultiPartHeader(multiPartHeader string) (header string, optional map[s
     			optional[optSplit[0]] = optSplit[1]
     		}
     	}
    -	return
    +	return header, optional
     }
     
     // parseEMLAttachmentEmbed parses a multipart that is an attachment or embed.
    
  • msg.go+7 4 modified
    @@ -1495,10 +1495,12 @@ func (m *Msg) GetSender(useFullAddr bool) (string, error) {
     			return "", ErrNoFromAddress
     		}
     	}
    -	if useFullAddr {
    -		return from[0].String(), nil
    +
    +	addr := from[0]
    +	if !useFullAddr {
    +		addr.Name = ""
     	}
    -	return from[0].Address, nil
    +	return addr.String(), nil
     }
     
     // GetRecipients returns a list of the currently set "TO", "CC", and "BCC" addresses for the Msg.
    @@ -1522,7 +1524,8 @@ func (m *Msg) GetRecipients() ([]string, error) {
     			continue
     		}
     		for _, r := range addresses {
    -			rcpts = append(rcpts, r.Address)
    +			r.Name = ""
    +			rcpts = append(rcpts, r.String())
     		}
     	}
     	if len(rcpts) <= 0 {
    
  • msg_test.go+272 30 modified
    @@ -2539,8 +2539,8 @@ func TestMsg_GetSender(t *testing.T) {
     		if err != nil {
     			t.Errorf("failed to get sender: %s", err)
     		}
    -		if !strings.EqualFold(sender, "toni.tester@example.com") {
    -			t.Errorf("expected sender not returned. Want: %s, got: %s", "toni.tester@example.com", sender)
    +		if !strings.EqualFold(sender, "<toni.tester@example.com>") {
    +			t.Errorf("expected sender not returned. Want: %s, got: %s", "<toni.tester@example.com>", sender)
     		}
     	})
     	t.Run("GetSender with envelope from only (full address)", func(t *testing.T) {
    @@ -2571,8 +2571,8 @@ func TestMsg_GetSender(t *testing.T) {
     		if err != nil {
     			t.Errorf("failed to get sender: %s", err)
     		}
    -		if !strings.EqualFold(sender, "toni.tester@example.com") {
    -			t.Errorf("expected sender not returned. Want: %s, got: %s", "toni.tester@example.com", sender)
    +		if !strings.EqualFold(sender, "<toni.tester@example.com>") {
    +			t.Errorf("expected sender not returned. Want: %s, got: %s", "<toni.tester@example.com>", sender)
     		}
     	})
     	t.Run("GetSender with from only (full address)", func(t *testing.T) {
    @@ -2606,8 +2606,8 @@ func TestMsg_GetSender(t *testing.T) {
     		if err != nil {
     			t.Errorf("failed to get sender: %s", err)
     		}
    -		if !strings.EqualFold(sender, "toni.tester@example.com") {
    -			t.Errorf("expected sender not returned. Want: %s, got: %s", "toni.tester@example.com", sender)
    +		if !strings.EqualFold(sender, "<toni.tester@example.com>") {
    +			t.Errorf("expected sender not returned. Want: %s, got: %s", "<toni.tester@example.com>", sender)
     		}
     	})
     	t.Run("GetSender with envelope from and from (full address)", func(t *testing.T) {
    @@ -2661,9 +2661,67 @@ func TestMsg_GetRecipients(t *testing.T) {
     		if len(rcpts) != 1 {
     			t.Fatalf("expected 1 recipient, got: %d", len(rcpts))
     		}
    -		if !strings.EqualFold(rcpts[0], "toni.tester@example.com") {
    +		if !strings.EqualFold(rcpts[0], "<toni.tester@example.com>") {
    +			t.Errorf("expected recipient not returned. Want: %s, got: %s",
    +				"<toni.tester@example.com>", rcpts[0])
    +		}
    +	})
    +	t.Run("GetRecipients with quoted local-part (issue #495)", func(t *testing.T) {
    +		message := NewMsg()
    +		if message == nil {
    +			t.Fatal("message is nil")
    +		}
    +		addr := `"toni.tester@example.com> ORCPT=admin@admin.com"@example.com`
    +		if err := message.To(addr); err != nil {
    +			t.Fatalf("failed to set to address: %s", err)
    +		}
    +		rcpts, err := message.GetRecipients()
    +		if err != nil {
    +			t.Errorf("failed to get recipients: %s", err)
    +		}
    +		if len(rcpts) != 1 {
    +			t.Fatalf("expected 1 recipient, got: %d", len(rcpts))
    +		}
    +		if !strings.EqualFold(rcpts[0], `<"toni.tester@example.com> ORCPT=admin@admin.com"@example.com>`) {
    +			t.Errorf("expected recipient not returned. Want: %s, got: %s",
    +				`<"toni.tester@example.com> ORCPT=admin@admin.com"@example.com>`, rcpts[0])
    +		}
    +	})
    +	t.Run("GetRecipients with quoted local-part in to,cc and bcc (issue #495)", func(t *testing.T) {
    +		message := NewMsg()
    +		if message == nil {
    +			t.Fatal("message is nil")
    +		}
    +		addr := `"toni.tester@example.com> ORCPT=admin@admin.com"@example.com`
    +		if err := message.To(addr); err != nil {
    +			t.Fatalf("failed to set to address: %s", err)
    +		}
    +		addr = `"tina.tester@example.com> ORCPT=admin@admin.com"@example.com`
    +		if err := message.Cc(addr); err != nil {
    +			t.Fatalf("failed to set to address: %s", err)
    +		}
    +		addr = `"troy.tester@example.com> ORCPT=admin@admin.com"@example.com`
    +		if err := message.Bcc(addr); err != nil {
    +			t.Fatalf("failed to set to address: %s", err)
    +		}
    +		rcpts, err := message.GetRecipients()
    +		if err != nil {
    +			t.Errorf("failed to get recipients: %s", err)
    +		}
    +		if len(rcpts) != 3 {
    +			t.Fatalf("expected 3 recipient, got: %d", len(rcpts))
    +		}
    +		if !strings.EqualFold(rcpts[0], `<"toni.tester@example.com> ORCPT=admin@admin.com"@example.com>`) {
    +			t.Errorf("expected recipient not returned. Want: %s, got: %s",
    +				`<"toni.tester@example.com> ORCPT=admin@admin.com"@example.com>`, rcpts[0])
    +		}
    +		if !strings.EqualFold(rcpts[1], `<"tina.tester@example.com> ORCPT=admin@admin.com"@example.com>`) {
     			t.Errorf("expected recipient not returned. Want: %s, got: %s",
    -				"toni.tester@example.com", rcpts[0])
    +				`<"tina.tester@example.com> ORCPT=admin@admin.com"@example.com>`, rcpts[1])
    +		}
    +		if !strings.EqualFold(rcpts[2], `<"troy.tester@example.com> ORCPT=admin@admin.com"@example.com>`) {
    +			t.Errorf("expected recipient not returned. Want: %s, got: %s",
    +				`<"troy.tester@example.com> ORCPT=admin@admin.com"@example.com>`, rcpts[2])
     		}
     	})
     	t.Run("GetRecipients with only cc", func(t *testing.T) {
    @@ -2681,9 +2739,9 @@ func TestMsg_GetRecipients(t *testing.T) {
     		if len(rcpts) != 1 {
     			t.Fatalf("expected 1 recipient, got: %d", len(rcpts))
     		}
    -		if !strings.EqualFold(rcpts[0], "toni.tester@example.com") {
    +		if !strings.EqualFold(rcpts[0], "<toni.tester@example.com>") {
     			t.Errorf("expected recipient not returned. Want: %s, got: %s",
    -				"toni.tester@example.com", rcpts[0])
    +				"<toni.tester@example.com>", rcpts[0])
     		}
     	})
     	t.Run("GetRecipients with only bcc", func(t *testing.T) {
    @@ -2701,9 +2759,9 @@ func TestMsg_GetRecipients(t *testing.T) {
     		if len(rcpts) != 1 {
     			t.Fatalf("expected 1 recipient, got: %d", len(rcpts))
     		}
    -		if !strings.EqualFold(rcpts[0], "toni.tester@example.com") {
    +		if !strings.EqualFold(rcpts[0], "<toni.tester@example.com>") {
     			t.Errorf("expected recipient not returned. Want: %s, got: %s",
    -				"toni.tester@example.com", rcpts[0])
    +				"<toni.tester@example.com>", rcpts[0])
     		}
     	})
     	t.Run("GetRecipients with to and cc", func(t *testing.T) {
    @@ -2724,13 +2782,13 @@ func TestMsg_GetRecipients(t *testing.T) {
     		if len(rcpts) != 2 {
     			t.Fatalf("expected 2 recipient, got: %d", len(rcpts))
     		}
    -		if !strings.EqualFold(rcpts[0], "toni.tester@example.com") {
    +		if !strings.EqualFold(rcpts[0], "<toni.tester@example.com>") {
     			t.Errorf("expected recipient not returned. Want: %s, got: %s",
    -				"toni.tester@example.com", rcpts[0])
    +				"<toni.tester@example.com>", rcpts[0])
     		}
    -		if !strings.EqualFold(rcpts[1], "tina.tester@example.com") {
    +		if !strings.EqualFold(rcpts[1], "<tina.tester@example.com>") {
     			t.Errorf("expected recipient not returned. Want: %s, got: %s",
    -				"tina.tester@example.com", rcpts[1])
    +				"<tina.tester@example.com>", rcpts[1])
     		}
     	})
     	t.Run("GetRecipients with to and bcc", func(t *testing.T) {
    @@ -2751,13 +2809,13 @@ func TestMsg_GetRecipients(t *testing.T) {
     		if len(rcpts) != 2 {
     			t.Fatalf("expected 2 recipient, got: %d", len(rcpts))
     		}
    -		if !strings.EqualFold(rcpts[0], "toni.tester@example.com") {
    +		if !strings.EqualFold(rcpts[0], "<toni.tester@example.com>") {
     			t.Errorf("expected recipient not returned. Want: %s, got: %s",
    -				"toni.tester@example.com", rcpts[0])
    +				"<toni.tester@example.com>", rcpts[0])
     		}
    -		if !strings.EqualFold(rcpts[1], "tina.tester@example.com") {
    +		if !strings.EqualFold(rcpts[1], "<tina.tester@example.com>") {
     			t.Errorf("expected recipient not returned. Want: %s, got: %s",
    -				"tina.tester@example.com", rcpts[1])
    +				"<tina.tester@example.com>", rcpts[1])
     		}
     	})
     	t.Run("GetRecipients with cc and bcc", func(t *testing.T) {
    @@ -2778,13 +2836,13 @@ func TestMsg_GetRecipients(t *testing.T) {
     		if len(rcpts) != 2 {
     			t.Fatalf("expected 2 recipient, got: %d", len(rcpts))
     		}
    -		if !strings.EqualFold(rcpts[0], "toni.tester@example.com") {
    +		if !strings.EqualFold(rcpts[0], "<toni.tester@example.com>") {
     			t.Errorf("expected recipient not returned. Want: %s, got: %s",
    -				"toni.tester@example.com", rcpts[0])
    +				"<toni.tester@example.com>", rcpts[0])
     		}
    -		if !strings.EqualFold(rcpts[1], "tina.tester@example.com") {
    +		if !strings.EqualFold(rcpts[1], "<tina.tester@example.com>") {
     			t.Errorf("expected recipient not returned. Want: %s, got: %s",
    -				"tina.tester@example.com", rcpts[1])
    +				"<tina.tester@example.com>", rcpts[1])
     		}
     	})
     	t.Run("GetRecipients with to, cc and bcc", func(t *testing.T) {
    @@ -2808,17 +2866,17 @@ func TestMsg_GetRecipients(t *testing.T) {
     		if len(rcpts) != 3 {
     			t.Fatalf("expected 3 recipient, got: %d", len(rcpts))
     		}
    -		if !strings.EqualFold(rcpts[0], "toni.tester@example.com") {
    +		if !strings.EqualFold(rcpts[0], "<toni.tester@example.com>") {
     			t.Errorf("expected recipient not returned. Want: %s, got: %s",
    -				"toni.tester@example.com", rcpts[0])
    +				"<toni.tester@example.com>", rcpts[0])
     		}
    -		if !strings.EqualFold(rcpts[1], "tina.tester@example.com") {
    +		if !strings.EqualFold(rcpts[1], "<tina.tester@example.com>") {
     			t.Errorf("expected recipient not returned. Want: %s, got: %s",
    -				"tina.tester@example.com", rcpts[1])
    +				"<tina.tester@example.com>", rcpts[1])
     		}
    -		if !strings.EqualFold(rcpts[2], "tom.tester@example.com") {
    +		if !strings.EqualFold(rcpts[2], "<tom.tester@example.com>") {
     			t.Errorf("expected recipient not returned. Want: %s, got: %s",
    -				"tina.tester@example.com", rcpts[2])
    +				"<tina.tester@example.com>", rcpts[2])
     		}
     	})
     	t.Run("GetRecipients with no recipients", func(t *testing.T) {
    @@ -2836,6 +2894,190 @@ func TestMsg_GetRecipients(t *testing.T) {
     	})
     }
     
    +func TestMsg_addressCmdInjectsions(t *testing.T) {
    +	tests := []struct {
    +		name       string
    +		payload    string
    +		shouldFail bool
    +	}{
    +		// Basic argument-injection (expected to fail)
    +		{"ORCPT-arg", `"toni.tester@example.com> ORCPT=admin@example.com"@example.com`, false},
    +		{"SIZE-arg", `"toni.tester@example.com> SIZE=99999"@example.com`, false},
    +		{"AUTH-arg", `"toni.tester@example.com> AUTH=PLAIN"@example.com`, false},
    +
    +		// whitespace / separator variants
    +		{"double-space", `"toni.tester@example.com>  ORCPT=admin@example.com"@example.com`, false},
    +		{"tab-separator", `"toni.tester@example.com>\tORCPT=admin@example.com"@example.com`, false},
    +
    +		// quoted/escape tricks
    +		{"quoted-close-space", `"toni.tester@example.com\"> ORCPT=admin@example.com"@example.com`, false},
    +		{"escaped-backslash", `"toni.tester@example.com\\"> ORCPT=admin@example.com"@example.com`, true},
    +
    +		// missing / mis-balanced angle brackets
    +		{"unbalanced-close", `toni.tester@example.com> ORCPT=admin@example.com@example.com`, true},
    +		{"leading-bracket", `<toni.tester@example.com> ORCPT=admin@example.com@example.com`, true},
    +		{"no-brackets", `toni.tester@example.com ORCPT=admin@example.com@example.com`, true},
    +
    +		// comments / RFC-style
    +		{"comment-insert", `(comment)toni.tester@example.com> ORCPT=admin@example.com"@example.com`, true},
    +		{"comment-inside-quote", `"toni.tester@example.com (orcd) > ORCPT=admin@example.com"@example.com`, false},
    +
    +		// percent-encoded attempts
    +		{"percent-encoded-sep", `"toni.tester@example.com%3E%20ORCPT=admin@example.com"@example.com`, false},
    +		{"percent-encoded-crlf", `"toni.tester@example.com%0d%0aRCPT TO:<other@example.com>"@example.com`, false},
    +
    +		// null / control byte insertion
    +		{"null-byte", `"toni.tester@example.com\000> ORCPT=admin@example.com"@example.com`, false},
    +
    +		// explicit CRLF-injection attempts
    +		{"crlf-rcpt", `"toni.tester@example.com">\r\nRCPT TO:<attacker@example.com>"@example.com`, true},
    +		{"crlf-mailfrom", `"toni.tester@example.com">\r\nMAIL FROM:<attacker@example.com>"@example.com`, true},
    +
    +		// folding whitespace / long-wrapping
    +		{"folding-ws", `"toni.tester@example.com> \r\n\tORCPT=admin@example.com"@example.com`, false},
    +
    +		// unicode / homoglyphs
    +		{"unicode-fullwidth", `"toni.tester@example.com> ORCPT=admin@example.com"@example.com`, false},
    +		{"unicode-hidden", `"toni.tester@example.com> ORCPT=admin@exam` + "\u200c" + `ple.com"@example.com`, false},
    +
    +		// multiple @ / nested-at attempts
    +		{"nested-at", `"toni.tester@example.com> ORCPT=admin@sub@example.com"@example.com`, false},
    +		{"double-at", `"toni.tester@example.com> ORCPT=admin@@example.com"@example.com`, false},
    +
    +		// long / overflow-ish
    +		{"long-localpart", `"toni.tester@example.comAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA> ORCPT=admin@example.com"@example.com`, false},
    +
    +		// safe detection-only marker
    +		{"detection-marker", `"toni.tester@example.com> X-INJECT-TEST=smuggle-detect@example.com"@example.com`, false},
    +	}
    +
    +	t.Run("address command injects in recipients", func(t *testing.T) {
    +		for _, tc := range tests {
    +			t.Run(tc.name, func(t *testing.T) {
    +				message := NewMsg()
    +				if message == nil {
    +					t.Fatal("message is nil")
    +				}
    +				err := message.To(tc.payload)
    +				if err != nil && !tc.shouldFail {
    +					t.Errorf("failed to set to address: %s", err)
    +				}
    +				if err == nil && tc.shouldFail {
    +					t.Errorf("setting address was supposed to fail but didn't")
    +				}
    +				if tc.shouldFail {
    +					return
    +				}
    +
    +				addr, err := mail.ParseAddress(tc.payload)
    +				if err != nil {
    +					t.Errorf("failed to parse address payload: %s", err)
    +				}
    +				rcpts, err := message.GetRecipients()
    +				if err != nil {
    +					t.Errorf("failed to get recipient: %s", err)
    +				}
    +				if len(rcpts) != 1 {
    +					t.Fatalf("expected 1 recipient, got: %d", len(rcpts))
    +				}
    +				if !strings.EqualFold(rcpts[0], addr.String()) {
    +					t.Errorf("recipients don't match, expected: %s, got: %s", addr.String(), rcpts[0])
    +				}
    +			})
    +		}
    +	})
    +	t.Run("address command injects in sender (from)", func(t *testing.T) {
    +		for _, tc := range tests {
    +			t.Run(tc.name, func(t *testing.T) {
    +				message := NewMsg()
    +				if message == nil {
    +					t.Fatal("message is nil")
    +				}
    +				err := message.From(tc.payload)
    +				if err != nil && !tc.shouldFail {
    +					t.Errorf("failed to set to address: %s", err)
    +				}
    +				if err == nil && tc.shouldFail {
    +					t.Errorf("setting address was supposed to fail but didn't")
    +				}
    +				if tc.shouldFail {
    +					return
    +				}
    +
    +				addr, err := mail.ParseAddress(tc.payload)
    +				if err != nil {
    +					t.Errorf("failed to parse address payload: %s", err)
    +				}
    +				from, err := message.GetSender(false)
    +				if err != nil {
    +					t.Errorf("failed to get recipient: %s", err)
    +				}
    +				if from == "" {
    +					t.Fatal("expected sender, got empty string")
    +				}
    +				if !strings.EqualFold(from, addr.String()) {
    +					t.Errorf("sender don't match, expected: %s, got: %s", addr.String(), from)
    +				}
    +				from, err = message.GetSender(true)
    +				if err != nil {
    +					t.Errorf("failed to get recipient: %s", err)
    +				}
    +				if from == "" {
    +					t.Fatal("expected sender, got empty string")
    +				}
    +				if !strings.EqualFold(from, addr.String()) {
    +					t.Errorf("sender don't match, expected: %s, got: %s", addr.String(), from)
    +				}
    +			})
    +		}
    +	})
    +	t.Run("address command injects in sender (envelope-from)", func(t *testing.T) {
    +		for _, tc := range tests {
    +			t.Run(tc.name, func(t *testing.T) {
    +				message := NewMsg()
    +				if message == nil {
    +					t.Fatal("message is nil")
    +				}
    +				err := message.EnvelopeFrom(tc.payload)
    +				if err != nil && !tc.shouldFail {
    +					t.Errorf("failed to set to address: %s", err)
    +				}
    +				if err == nil && tc.shouldFail {
    +					t.Errorf("setting address was supposed to fail but didn't")
    +				}
    +				if tc.shouldFail {
    +					return
    +				}
    +
    +				addr, err := mail.ParseAddress(tc.payload)
    +				if err != nil {
    +					t.Errorf("failed to parse address payload: %s", err)
    +				}
    +				from, err := message.GetSender(false)
    +				if err != nil {
    +					t.Errorf("failed to get recipient: %s", err)
    +				}
    +				if from == "" {
    +					t.Fatal("expected sender, got empty string")
    +				}
    +				if !strings.EqualFold(from, addr.String()) {
    +					t.Errorf("sender don't match, expected: %s, got: %s", addr.String(), from)
    +				}
    +				from, err = message.GetSender(true)
    +				if err != nil {
    +					t.Errorf("failed to get recipient: %s", err)
    +				}
    +				if from == "" {
    +					t.Fatal("expected sender, got empty string")
    +				}
    +				if !strings.EqualFold(from, addr.String()) {
    +					t.Errorf("sender don't match, expected: %s, got: %s", addr.String(), from)
    +				}
    +			})
    +		}
    +	})
    +}
    +
     func TestMsg_GetAddrHeader(t *testing.T) {
     	t.Run("GetAddrHeader with valid address (from)", func(t *testing.T) {
     		message := NewMsg()
    
  • smtp/smtp.go+6 6 modified
    @@ -266,10 +266,10 @@ func (c *Client) TLSConnectionState() (state tls.ConnectionState, ok bool) {
     
     	tc, ok := c.conn.(*tls.Conn)
     	if !ok {
    -		return
    +		return state, ok
     	}
     	state, ok = tc.ConnectionState(), true
    -	return
    +	return state, ok
     }
     
     // Verify checks the validity of an email address on the server.
    @@ -368,7 +368,7 @@ func (c *Client) Mail(from string) error {
     	if err := c.hello(); err != nil {
     		return err
     	}
    -	cmdStr := "MAIL FROM:<%s>"
    +	cmdStr := "MAIL FROM:%s"
     
     	c.mutex.RLock()
     	if c.ext != nil {
    @@ -402,10 +402,10 @@ func (c *Client) Rcpt(to string) error {
     	c.mutex.RUnlock()
     
     	if ok && c.dsnrntype != "" {
    -		_, _, err := c.cmd(25, "RCPT TO:<%s> NOTIFY=%s", to, c.dsnrntype)
    +		_, _, err := c.cmd(25, "RCPT TO:%s NOTIFY=%s", to, c.dsnrntype)
     		return err
     	}
    -	_, _, err := c.cmd(25, "RCPT TO:<%s>", to)
    +	_, _, err := c.cmd(25, "RCPT TO:%s", to)
     	return err
     }
     
    @@ -432,7 +432,7 @@ func (d *DataCloser) Write(p []byte) (n int, err error) {
     	d.c.mutex.Lock()
     	n, err = d.WriteCloser.Write(p)
     	d.c.mutex.Unlock()
    -	return
    +	return n, err
     }
     
     // ServerResponse returns the response that was returned by the server after the DataCloser has
    
  • smtp/smtp_test.go+59 10 modified
    @@ -30,6 +30,7 @@ import (
     	"hash"
     	"io"
     	"net"
    +	netmail "net/mail"
     	"os"
     	"strconv"
     	"strings"
    @@ -2261,7 +2262,11 @@ func TestClient_Mail(t *testing.T) {
     				t.Errorf("failed to close client: %s", err)
     			}
     		})
    -		if err = client.Mail("valid-from@domain.tld"); err != nil {
    +		fromAddr, err := netmail.ParseAddress("valid-from@domain.tld")
    +		if err != nil {
    +			t.Fatalf("failed to parse from address: %s", err)
    +		}
    +		if err = client.Mail(fromAddr.String()); err != nil {
     			t.Errorf("failed to set mail from address: %s", err)
     		}
     	})
    @@ -2358,7 +2363,11 @@ func TestClient_Mail(t *testing.T) {
     				t.Errorf("failed to close client: %s", err)
     			}
     		})
    -		if err = client.Mail("valid-from@domain.tld"); err != nil {
    +		fromAddr, err := netmail.ParseAddress("valid-from@domain.tld")
    +		if err != nil {
    +			t.Fatalf("failed to parse from address: %s", err)
    +		}
    +		if err = client.Mail(fromAddr.String()); err != nil {
     			t.Errorf("failed to set mail from address: %s", err)
     		}
     		expected := "MAIL FROM:<valid-from@domain.tld> BODY=8BITMIME"
    @@ -2398,7 +2407,11 @@ func TestClient_Mail(t *testing.T) {
     				t.Errorf("failed to close client: %s", err)
     			}
     		})
    -		if err = client.Mail("valid-from@domain.tld"); err != nil {
    +		fromAddr, err := netmail.ParseAddress("valid-from@domain.tld")
    +		if err != nil {
    +			t.Fatalf("failed to parse from address: %s", err)
    +		}
    +		if err = client.Mail(fromAddr.String()); err != nil {
     			t.Errorf("failed to set mail from address: %s", err)
     		}
     		expected := "MAIL FROM:<valid-from@domain.tld> SMTPUTF8"
    @@ -2438,7 +2451,11 @@ func TestClient_Mail(t *testing.T) {
     				t.Errorf("failed to close client: %s", err)
     			}
     		})
    -		if err = client.Mail("valid-from+📧@domain.tld"); err != nil {
    +		fromAddr, err := netmail.ParseAddress("valid-from+📧@domain.tld")
    +		if err != nil {
    +			t.Fatalf("failed to parse from address: %s", err)
    +		}
    +		if err = client.Mail(fromAddr.String()); err != nil {
     			t.Errorf("failed to set mail from address: %s", err)
     		}
     		expected := "MAIL FROM:<valid-from+📧@domain.tld> SMTPUTF8"
    @@ -2479,7 +2496,11 @@ func TestClient_Mail(t *testing.T) {
     			}
     		})
     		client.dsnmrtype = "FULL"
    -		if err = client.Mail("valid-from@domain.tld"); err != nil {
    +		fromAddr, err := netmail.ParseAddress("valid-from@domain.tld")
    +		if err != nil {
    +			t.Fatalf("failed to parse from address: %s", err)
    +		}
    +		if err = client.Mail(fromAddr.String()); err != nil {
     			t.Errorf("failed to set mail from address: %s", err)
     		}
     		expected := "MAIL FROM:<valid-from@domain.tld> RET=FULL"
    @@ -2520,7 +2541,11 @@ func TestClient_Mail(t *testing.T) {
     			}
     		})
     		client.dsnmrtype = "FULL"
    -		if err = client.Mail("valid-from@domain.tld"); err != nil {
    +		fromAddr, err := netmail.ParseAddress("valid-from@domain.tld")
    +		if err != nil {
    +			t.Fatalf("failed to parse from address: %s", err)
    +		}
    +		if err = client.Mail(fromAddr.String()); err != nil {
     			t.Errorf("failed to set mail from address: %s", err)
     		}
     		expected := "MAIL FROM:<valid-from@domain.tld> BODY=8BITMIME SMTPUTF8 RET=FULL"
    @@ -2560,7 +2585,11 @@ func TestClient_Rcpt(t *testing.T) {
     				t.Errorf("failed to close client: %s", err)
     			}
     		})
    -		if err = client.Rcpt("valid-to@domain.tld"); err != nil {
    +		addr, err := netmail.ParseAddress("valid-to@domain.tld")
    +		if err != nil {
    +			t.Fatalf("failed to parse recipient address: %s", err)
    +		}
    +		if err = client.Rcpt(addr.String()); err != nil {
     			t.Errorf("failed to set recipient address: %s", err)
     		}
     	})
    @@ -2626,7 +2655,11 @@ func TestClient_Rcpt(t *testing.T) {
     			t.Fatalf("failed to send hello to test server: %s", err)
     		}
     		client.dsnrntype = "SUCCESS"
    -		if err = client.Rcpt("valid-to@domain.tld"); err == nil {
    +		addr, err := netmail.ParseAddress("valid-to@domain.tld")
    +		if err != nil {
    +			t.Fatalf("failed to parse recipient address: %s", err)
    +		}
    +		if err = client.Rcpt(addr.String()); err == nil {
     			t.Error("recpient address with newlines should fail")
     		}
     		expected := "RCPT TO:<valid-to@domain.tld> NOTIFY=SUCCESS"
    @@ -3006,8 +3039,16 @@ func TestSendMail(t *testing.T) {
     			config.RootCAs = testConfig.RootCAs
     			config.Certificates = testConfig.Certificates
     		}
    +		fromAddr, err := netmail.ParseAddress("valid-from@domain.tld")
    +		if err != nil {
    +			t.Fatalf("failed to parse from address: %s", err)
    +		}
     		auth := LoginAuth("username", "password", TestServerAddr, false)
    -		if err := SendMail(addr, auth, "valid-from@domain.tld", []string{"valid-to@domain.tld"},
    +		toAddr, err := netmail.ParseAddress("valid-to@domain.tld")
    +		if err != nil {
    +			t.Fatalf("failed to parse recipient address: %s", err)
    +		}
    +		if err := SendMail(addr, auth, fromAddr.String(), []string{toAddr.String()},
     			[]byte("test message")); err != nil {
     			t.Fatalf("failed to send mail: %s", err)
     		}
    @@ -3090,8 +3131,16 @@ Subject: Hooray for Go
     Line 1
     .Leading dot line .
     Goodbye.`)
    +		fromAddr, err := netmail.ParseAddress("valid-from@domain.tld")
    +		if err != nil {
    +			t.Fatalf("failed to parse from address: %s", err)
    +		}
    +		toAddr, err := netmail.ParseAddress("valid-to@domain.tld")
    +		if err != nil {
    +			t.Fatalf("failed to parse recipient address: %s", err)
    +		}
     		auth := LoginAuth("username", "password", TestServerAddr, false)
    -		if err := SendMail(addr, auth, "valid-from@domain.tld", []string{"valid-to@domain.tld"}, message); err != nil {
    +		if err = SendMail(addr, auth, fromAddr.String(), []string{toAddr.String()}, message); err != nil {
     			t.Fatalf("failed to send mail: %s", err)
     		}
     		props.BufferMutex.RLock()
    

Vulnerability mechanics

Synthesis attempt was rejected by the grounding validator. Re-run pending.

References

7

News mentions

0

No linked articles in our index yet.