From df19c60b17a4c1c10a974e528fd9daf5c8b2ad54 Mon Sep 17 00:00:00 2001 From: Lyra Thorpe Date: Wed, 17 Jun 2026 17:19:13 +0100 Subject: [PATCH] fix: relay raw SMTP bytes without decoding send_message decoded the message body with utf-8/errors="replace", corrupting 8-bit content before forwarding. Pass the raw bytes straight to smtp.sendmail so the message is relayed unchanged. Fixes #4 Co-Authored-By: Claude Opus 4.8 (1M context) --- proxy_server.py | 3 +-- tests/test_proxy_server.py | 7 +++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/proxy_server.py b/proxy_server.py index 7a4a583..e37b23a 100644 --- a/proxy_server.py +++ b/proxy_server.py @@ -416,7 +416,6 @@ class SMTPProxyHandler: def send_message(self, sender, recipients, data): """Forward a complete SMTP message to the backend SMTP server.""" - message = data.decode("utf-8", errors="replace") if Settings.BACKEND_SMTP_USE_SSL: smtp = smtplib.SMTP_SSL(Settings.BACKEND_SMTP_HOST, Settings.BACKEND_SMTP_PORT, timeout=30) else: @@ -426,7 +425,7 @@ class SMTPProxyHandler: try: if Settings.BACKEND_SMTP_USER and Settings.BACKEND_SMTP_PASS: smtp.login(Settings.BACKEND_SMTP_USER, Settings.BACKEND_SMTP_PASS) - smtp.sendmail(sender, recipients, message) + smtp.sendmail(sender, recipients, data) finally: smtp.quit() diff --git a/tests/test_proxy_server.py b/tests/test_proxy_server.py index d01ec91..c904ac9 100644 --- a/tests/test_proxy_server.py +++ b/tests/test_proxy_server.py @@ -126,14 +126,17 @@ def test_smtp_proxy_handler_forwards_message_over_ssl(monkeypatch): Settings.BACKEND_SMTP_USER = "smtp-user" Settings.BACKEND_SMTP_PASS = "smtp-pass" + # Include an 8-bit byte that would be mangled by a utf-8 decode. + raw = b"Subject: Test\r\n\r\nBody \x80\r\n" handler = SMTPProxyHandler() - handler.send_message("from@example.com", ["to@example.com"], b"Subject: Test\r\n\r\nBody\r\n") + handler.send_message("from@example.com", ["to@example.com"], raw) assert captured["instance"].host == Settings.BACKEND_SMTP_HOST assert captured["instance"].logged_in is True assert captured["instance"].sent[0] == "from@example.com" assert captured["instance"].sent[1] == ["to@example.com"] - assert "Subject: Test" in captured["instance"].sent[2] + # Raw bytes must be forwarded unchanged, not decoded. + assert captured["instance"].sent[2] == raw Settings.BACKEND_SMTP_USE_SSL = previous_ssl Settings.BACKEND_SMTP_USER = previous_user