Release / update-version (push) Has been cancelled
Release / build-frontend (push) Has been cancelled
Release / release (push) Has been cancelled
Release / sync-version-file (push) Has been cancelled
CI / shell (push) Canceled after 0s
CI / test (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
Security Scan / backend-security (push) Canceled after 0s
Security Scan / frontend-security (push) Canceled after 0s
118 lines
4.1 KiB
Go
118 lines
4.1 KiB
Go
//go:build unit
|
|
|
|
package service
|
|
|
|
import (
|
|
"bytes"
|
|
"io"
|
|
"mime"
|
|
"mime/quotedprintable"
|
|
"net/mail"
|
|
"regexp"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestBuildSMTPMessageProducesStandardsCompliantMIME(t *testing.T) {
|
|
config := &SMTPConfig{
|
|
Host: "smtp.example.com",
|
|
From: "reply@example.com",
|
|
FromName: "Sub2API 通知",
|
|
}
|
|
body := "<html>\n<body>验证码:123456 & ready</body>\n</html>"
|
|
|
|
message, err := buildSMTPMessage(config, "User <user@example.net>", "邮箱验证码", body)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "reply@example.com", message.envelopeFrom)
|
|
require.Equal(t, "user@example.net", message.envelopeTo)
|
|
|
|
parsed, err := mail.ReadMessage(bytes.NewReader(message.data))
|
|
require.NoError(t, err)
|
|
|
|
from, err := mail.ParseAddress(parsed.Header.Get("From"))
|
|
require.NoError(t, err)
|
|
require.Equal(t, "Sub2API 通知", from.Name)
|
|
require.Equal(t, "reply@example.com", from.Address)
|
|
|
|
recipient, err := mail.ParseAddress(parsed.Header.Get("To"))
|
|
require.NoError(t, err)
|
|
require.Equal(t, "User", recipient.Name)
|
|
require.Equal(t, "user@example.net", recipient.Address)
|
|
|
|
decodedSubject, err := new(mime.WordDecoder).DecodeHeader(parsed.Header.Get("Subject"))
|
|
require.NoError(t, err)
|
|
require.Equal(t, "邮箱验证码", decodedSubject)
|
|
require.NotEmpty(t, parsed.Header.Get("Date"))
|
|
_, err = mail.ParseDate(parsed.Header.Get("Date"))
|
|
require.NoError(t, err)
|
|
require.Regexp(t, regexp.MustCompile(`^<[0-9a-f]{32}@example\.com>$`), parsed.Header.Get("Message-ID"))
|
|
require.Equal(t, "1.0", parsed.Header.Get("MIME-Version"))
|
|
require.Equal(t, "quoted-printable", parsed.Header.Get("Content-Transfer-Encoding"))
|
|
|
|
mediaType, params, err := mime.ParseMediaType(parsed.Header.Get("Content-Type"))
|
|
require.NoError(t, err)
|
|
require.Equal(t, "text/html", mediaType)
|
|
require.Equal(t, "UTF-8", params["charset"])
|
|
|
|
decodedBody, err := io.ReadAll(quotedprintable.NewReader(parsed.Body))
|
|
require.NoError(t, err)
|
|
require.Equal(t, strings.ReplaceAll(body, "\n", "\r\n"), string(decodedBody))
|
|
}
|
|
|
|
func TestBuildSMTPMessagePreventsHeaderInjection(t *testing.T) {
|
|
config := &SMTPConfig{
|
|
Host: "smtp.example.com",
|
|
From: "reply@example.com",
|
|
FromName: "Sender\r\nBcc: hidden@example.com",
|
|
}
|
|
|
|
message, err := buildSMTPMessage(config, "user@example.net", "Subject\r\nCc: hidden@example.com", "body")
|
|
require.NoError(t, err)
|
|
|
|
parsed, err := mail.ReadMessage(bytes.NewReader(message.data))
|
|
require.NoError(t, err)
|
|
require.Empty(t, parsed.Header.Get("Bcc"))
|
|
require.Empty(t, parsed.Header.Get("Cc"))
|
|
|
|
decodedSubject, err := new(mime.WordDecoder).DecodeHeader(parsed.Header.Get("Subject"))
|
|
require.NoError(t, err)
|
|
require.Equal(t, "SubjectCc: hidden@example.com", decodedSubject)
|
|
}
|
|
|
|
func TestBuildSMTPMessageRejectsInvalidConfiguration(t *testing.T) {
|
|
_, err := buildSMTPMessage(nil, "user@example.net", "subject", "body")
|
|
require.ErrorContains(t, err, "missing SMTP configuration")
|
|
|
|
_, err = buildSMTPMessage(&SMTPConfig{Host: "smtp.example.com"}, "user@example.net", "subject", "body")
|
|
require.ErrorContains(t, err, "invalid SMTP from address")
|
|
|
|
_, err = buildSMTPMessage(&SMTPConfig{
|
|
Host: "smtp.example.com",
|
|
From: "reply@example.com",
|
|
}, "invalid recipient <>", "subject", "body")
|
|
require.ErrorContains(t, err, "invalid SMTP recipient address")
|
|
|
|
_, err = buildSMTPMessage(&SMTPConfig{
|
|
Host: "smtp.example.com",
|
|
From: "reply@example.com",
|
|
}, "user@example.net\r\nBcc: hidden@example.net", "subject", "body")
|
|
require.ErrorContains(t, err, "invalid SMTP recipient address")
|
|
}
|
|
|
|
func TestBuildSMTPMessageUsesUniqueMessageIDs(t *testing.T) {
|
|
config := &SMTPConfig{Host: "smtp.example.com", From: "reply@example.com"}
|
|
|
|
first, err := buildSMTPMessage(config, "user@example.net", "subject", "body")
|
|
require.NoError(t, err)
|
|
second, err := buildSMTPMessage(config, "user@example.net", "subject", "body")
|
|
require.NoError(t, err)
|
|
|
|
firstParsed, err := mail.ReadMessage(bytes.NewReader(first.data))
|
|
require.NoError(t, err)
|
|
secondParsed, err := mail.ReadMessage(bytes.NewReader(second.data))
|
|
require.NoError(t, err)
|
|
require.NotEqual(t, firstParsed.Header.Get("Message-ID"), secondParsed.Header.Get("Message-ID"))
|
|
}
|