From 7691b6bcbb881ce1e2b383536dc60828a6ca9190 Mon Sep 17 00:00:00 2001 From: Ziemek Borowski Date: Sun, 3 May 2026 11:52:55 +0200 Subject: [PATCH] Potential fix for code scanning alert no. 2: Email content injection Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- internal/protocols/smtp/sendmail.go | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/internal/protocols/smtp/sendmail.go b/internal/protocols/smtp/sendmail.go index a945bc8..82ae161 100644 --- a/internal/protocols/smtp/sendmail.go +++ b/internal/protocols/smtp/sendmail.go @@ -269,7 +269,7 @@ func writeSMTPCSVRow(csvLogger logger.Logger, row []string) error { // buildEmailMessage constructs an RFC 5322 email message. // Defense-in-Depth: Email headers (From, To, Subject) are sanitized to remove // CRLF sequences that could be used for header injection attacks. The message -// body is not sanitized as it legitimately may contain newlines. +// body is sanitized to normalize newlines and strip unsafe control characters. func buildEmailMessage(from string, to []string, subject, body string) []byte { messageID := generateMessageID("") date := time.Now().Format(time.RFC1123Z) @@ -281,6 +281,7 @@ func buildEmailMessage(from string, to []string, subject, body string) []byte { for i, addr := range to { sanitizedTo[i] = sanitizeEmailHeader(addr) } + body = sanitizeEmailBody(body) message := fmt.Sprintf("Message-ID: <%s>\r\n", messageID) message += fmt.Sprintf("Date: %s\r\n", date) @@ -304,6 +305,22 @@ func sanitizeEmailHeader(header string) string { return header } +// sanitizeEmailBody normalizes line endings and removes unsafe control +// characters from body content while preserving regular text formatting. +func sanitizeEmailBody(body string) string { + body = strings.ReplaceAll(body, "\r\n", "\n") + body = strings.ReplaceAll(body, "\r", "\n") + + var b strings.Builder + b.Grow(len(body)) + for _, r := range body { + if r == '\n' || r == '\t' || r >= 0x20 { + b.WriteRune(r) + } + } + return b.String() +} + // generateMessageID creates a unique message ID. func generateMessageID(host string) string { timestamp := time.Now().UnixNano()