From 76b05e0d6495c8a81f9c1ead8b3fd94f3555e6c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Casaj=C3=BAs?= Date: Thu, 22 Jun 2023 10:40:32 +0200 Subject: [PATCH] Preserve original sender and authentication results if the original email is preserved in the alias (#1780) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Adrià Casajús --- app/email/headers.py | 1 + app/models.py | 3 +- email_handler.py | 31 +++++---- .../replacement_on_forward_phase.eml | 64 +++++++++++++++++++ tests/handler/test_preserved_headers.py | 34 ++++++++++ 5 files changed, 115 insertions(+), 18 deletions(-) create mode 100644 tests/example_emls/replacement_on_forward_phase.eml create mode 100644 tests/handler/test_preserved_headers.py diff --git a/app/email/headers.py b/app/email/headers.py index 800a5a2b..416ea2df 100644 --- a/app/email/headers.py +++ b/app/email/headers.py @@ -20,6 +20,7 @@ X_SPAM_STATUS = "X-Spam-Status" LIST_UNSUBSCRIBE = "List-Unsubscribe" LIST_UNSUBSCRIBE_POST = "List-Unsubscribe-Post" RETURN_PATH = "Return-Path" +AUTHENTICATION_RESULTS = "Authentication-Results" # headers used to DKIM sign in order of preference DKIM_HEADERS = [ diff --git a/app/models.py b/app/models.py index 0c252f7f..7c143d49 100644 --- a/app/models.py +++ b/app/models.py @@ -514,9 +514,8 @@ class User(Base, ModelMixin, UserMixin, PasswordOracle): server_default=BlockBehaviourEnum.return_2xx.name, ) - # to keep existing behavior, the server default is TRUE whereas for new user, the default value is FALSE include_header_email_header = sa.Column( - sa.Boolean, default=False, nullable=False, server_default="1" + sa.Boolean, default=True, nullable=False, server_default="1" ) # bitwise flags. Allow for future expansion diff --git a/email_handler.py b/email_handler.py index 470d4d25..27313f14 100644 --- a/email_handler.py +++ b/email_handler.py @@ -846,22 +846,21 @@ def forward_email_to_mailbox( f"""Email sent to {alias.email} from an invalid address and cannot be replied""", ) - delete_all_headers_except( - msg, - [ - headers.FROM, - headers.TO, - headers.CC, - headers.SUBJECT, - headers.DATE, - # do not delete original message id - headers.MESSAGE_ID, - # References and In-Reply-To are used for keeping the email thread - headers.REFERENCES, - headers.IN_REPLY_TO, - ] - + headers.MIME_HEADERS, - ) + headers_to_keep = [ + headers.FROM, + headers.TO, + headers.CC, + headers.SUBJECT, + headers.DATE, + # do not delete original message id + headers.MESSAGE_ID, + # References and In-Reply-To are used for keeping the email thread + headers.REFERENCES, + headers.IN_REPLY_TO, + ] + headers.MIME_HEADERS + if user.include_header_email_header: + headers_to_keep.append(headers.AUTHENTICATION_RESULTS) + delete_all_headers_except(msg, headers_to_keep) # create PGP email if needed if mailbox.pgp_enabled() and user.is_premium() and not alias.disable_pgp: diff --git a/tests/example_emls/replacement_on_forward_phase.eml b/tests/example_emls/replacement_on_forward_phase.eml new file mode 100644 index 00000000..655a1974 --- /dev/null +++ b/tests/example_emls/replacement_on_forward_phase.eml @@ -0,0 +1,64 @@ +Received: by mail-ed1-f49.google.com with SMTP id ej4so13657316edb.7 + for ; Mon, 27 Jun 2022 08:48:15 -0700 (PDT) +X-Gm-Message-State: AJIora8exR9DGeRFoKAtjzwLtUpH5hqx6Zt3tm8n4gUQQivGQ3fELjUV + yT7RQIfeW9Kv2atuOcgtmGYVU4iQ8VBeLmK1xvOYL4XpXfrT7ZrJNQ== +Authentication-Results: mx.google.com; + dkim=pass header.i=@matera.eu header.s=fnt header.b=XahYMey7; + dkim=pass header.i=@sendgrid.info header.s=smtpapi header.b="QOCS/yjt"; + spf=pass (google.com: domain of bounces+14445963-ab4e-csyndic.quartz=gmail.com@front-mail.matera.eu designates 168.245.4.42 as permitted sender) smtp.mailfrom="bounces+14445963-ab4e-csyndic.quartz=gmail.com@front-mail.matera.eu"; + dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=matera.eu +Received: from out.frontapp.com (unknown) + by geopod-ismtpd-3-0 (SG) + with ESMTP id d2gM2N7PT7W8d2-UEC4ESA + for ; + Mon, 27 Jun 2022 15:48:11.014 +0000 (UTC) +Content-Type: multipart/alternative; + boundary="----sinikael-?=_1-16563448907660.10629093370416887" +In-Reply-To: + +References: + + + + + +From: {{ sender_address }} +To: {{ recipient_address }} +Subject: Something +Message-ID: +X-Mailer: Front (1.0; +https://frontapp.com; + +msgid=af07e94a66ece6564ae30a2aaac7a34c@frontapp.com) +X-Feedback-ID: 14445963:SG +X-SG-EID: + =?us-ascii?Q?XtlxQDg5i3HqMzQY2Upg19JPZBVl1RybInUUL2yta9uBoIU4KU1FMJ5DjWrz6g?= + =?us-ascii?Q?fJUK5Qmneg2uc46gwp5BdHdp6Foaq5gg3xJriv3?= + =?us-ascii?Q?9OA=2FWRifeylU9O+ngdNbOKXoeJAkROmp2mCgw9x?= + =?us-ascii?Q?uud+EclOT9mYVtbZsydOLLm6Y2PPswQl8lnmiku?= + =?us-ascii?Q?DAhkG15HTz2FbWGWNDFb7VrSsN5ddjAscr6sIHw?= + =?us-ascii?Q?S48R5fnXmfhPbmlCgqFjr0FGphfuBdNAt6z6w8a?= + =?us-ascii?Q?o9u1EYDIX7zWHZ+Tr3eyw=3D=3D?= +X-SG-ID: + =?us-ascii?Q?N2C25iY2uzGMFz6rgvQsb8raWjw0ZPf1VmjsCkspi=2FI9PhcvqXQTpKqqyZkvBe?= + =?us-ascii?Q?+2RscnQ4WPkA+BN1vYgz1rezTVIqgp+rlWrKk8o?= + =?us-ascii?Q?HoB5dzpX6HKWtWCVRi10zwlDN1+pJnySoIUrlaT?= + =?us-ascii?Q?PA2aqQKmMQbjTl0CUAFryR8hhHcxdS0cQowZSd7?= + =?us-ascii?Q?XNjJWLvCGF7ODwg=2FKr+4yRE8UvULS2nrdO2wWyQ?= + =?us-ascii?Q?AiFHdPdZsRlgNomEo=3D?= +X-Spamd-Result: default: False [-2.00 / 13.00]; + ARC_ALLOW(-1.00)[google.com:s=arc-20160816:i=1]; + MIME_GOOD(-0.10)[multipart/alternative,text/plain]; + REPLYTO_ADDR_EQ_FROM(0.00)[]; + FORGED_RECIPIENTS_FORWARDING(0.00)[]; + NEURAL_HAM(-0.00)[-0.981]; + FREEMAIL_TO(0.00)[gmail.com]; + RCVD_TLS_LAST(0.00)[]; + FREEMAIL_ENVFROM(0.00)[gmail.com]; + MIME_TRACE(0.00)[0:+,1:+,2:~]; + RWL_MAILSPIKE_POSSIBLE(0.00)[209.85.208.49:from] + +------sinikael-?=_1-16563448907660.10629093370416887 +Content-Type: text/plain; charset=utf-8 +Content-Transfer-Encoding: quoted-printable + +From {{ sender_address }} To {{ recipient_address }} +------sinikael-?=_1-16563448907660.10629093370416887-- diff --git a/tests/handler/test_preserved_headers.py b/tests/handler/test_preserved_headers.py new file mode 100644 index 00000000..e8c39869 --- /dev/null +++ b/tests/handler/test_preserved_headers.py @@ -0,0 +1,34 @@ +from aiosmtpd.smtp import Envelope + +import email_handler +from app.db import Session +from app.email import headers, status +from app.mail_sender import mail_sender +from app.models import Alias +from tests.utils import create_new_user, load_eml_file, random_email + + +@mail_sender.store_emails_test_decorator +def test_original_headers_from_preserved(): + user = create_new_user() + alias = Alias.create_new_random(user) + Session.flush() + assert user.include_header_email_header + original_sender_address = random_email() + msg = load_eml_file( + "replacement_on_forward_phase.eml", + {"sender_address": original_sender_address, "recipient_address": alias.email}, + ) + envelope = Envelope() + envelope.mail_from = f"env.{original_sender_address}" + envelope.rcpt_tos = [alias.email] + result = email_handler.MailHandler()._handle(envelope, msg) + assert result == status.E200 + send_requests = mail_sender.get_stored_emails() + assert len(send_requests) == 1 + request = send_requests[0] + assert request.msg[headers.SL_ENVELOPE_FROM] == envelope.mail_from + assert ( + request.msg[headers.AUTHENTICATION_RESULTS] + == msg[headers.AUTHENTICATION_RESULTS] + )