package email
import (
"context"
"net/smtp"
"strings"
"testing"
)
func TestSMTPTransportSendBuildsHeadersForValidInput(t *testing.T) {
transport := newSMTPTransport("smtp.example.com", "587", "user", "pass")
var captured string
called := false
prev := smtpSendMail
smtpSendMail = func(addr string, auth smtp.Auth, from string, to []string, msg []byte) error {
called = true
if addr != "smtp.example.com:587" {
t.Fatalf("addr=%q", addr)
}
if from != "sender@example.com" {
t.Fatalf("from=%q", from)
}
if len(to) != 1 || to[0] != "recipient@example.com" {
t.Fatalf("to=%v", to)
}
captured = string(msg)
return nil
}
t.Cleanup(func() { smtpSendMail = prev })
err := transport.Send(context.Background(), FromIdentity{
Email: "sender@example.com",
Name: "Descrybe Team",
ReplyTo: "reply@example.com",
}, Outbound{
To: "recipient@example.com",
Subject: "Hello there",
Text: "plain body",
HTML: "
html body
",
Headers: map[string]string{"List-Unsubscribe": ""},
})
if err != nil {
t.Fatal(err)
}
if !called {
t.Fatal("expected smtpSendMail to be called")
}
for _, want := range []string{
"From: Descrybe Team ",
"To: recipient@example.com",
"Subject: Hello there",
"Reply-To: reply@example.com",
"List-Unsubscribe: ",
} {
if !strings.Contains(captured, want) {
t.Fatalf("message missing %q:\n%s", want, captured)
}
}
}
func TestSMTPTransportSendRejectsHeaderInjection(t *testing.T) {
cases := []Outbound{
{To: "recipient@example.com", Subject: "ok\r\nBcc:evil@example.com", Text: "body"},
{To: "recipient@example.com", Subject: "ok", Text: "body", Headers: map[string]string{"X-Test\r\nBcc": "1"}},
{To: "recipient@example.com", Subject: "ok", Text: "body", Headers: map[string]string{"X-Test": "1\r\nBcc:evil@example.com"}},
}
for _, tc := range cases {
transport := newSMTPTransport("smtp.example.com", "587", "", "")
called := false
prev := smtpSendMail
smtpSendMail = func(addr string, auth smtp.Auth, from string, to []string, msg []byte) error {
called = true
return nil
}
err := transport.Send(context.Background(), FromIdentity{
Email: "sender@example.com",
ReplyTo: "reply@example.com",
}, tc)
smtpSendMail = prev
if err == nil {
t.Fatalf("expected error for %#v", tc)
}
if called {
t.Fatalf("smtpSendMail should not be called for %#v", tc)
}
}
}
func TestSMTPTransportSendRejectsReplyToInjection(t *testing.T) {
transport := newSMTPTransport("smtp.example.com", "587", "", "")
called := false
prev := smtpSendMail
smtpSendMail = func(addr string, auth smtp.Auth, from string, to []string, msg []byte) error {
called = true
return nil
}
t.Cleanup(func() { smtpSendMail = prev })
err := transport.Send(context.Background(), FromIdentity{
Email: "sender@example.com",
ReplyTo: "reply@example.com\r\nBcc:evil@example.com",
}, Outbound{
To: "recipient@example.com",
Subject: "safe",
Text: "body",
})
if err == nil {
t.Fatal("expected invalid reply-to error")
}
if called {
t.Fatal("smtpSendMail should not be called")
}
}