From d16e3f356ccd32fd2602d09186b2f80b81977b12 Mon Sep 17 00:00:00 2001 From: Peter Alexandre Date: Wed, 5 Aug 2026 20:14:38 +0200 Subject: [PATCH] fix(mailer): remove freemail Reply-To to prevent rspamd SPOOF_REPLYTO junking PR #471 kept Reply-To: to let staff reply directly, but this triggers SPOOF_REPLYTO(6.00) + FREEMAIL_REPLYTO_NEQ_FROM(2.00) in rspamd (~8 pts total), pushing feedback emails past the Junk threshold and losing them before OTRS ever polls the mailbox. The reporter's address is already rendered in both email body templates, so OTRS agents can see and copy it. Remove Reply-To from sendFeedbackEmail and sendCommentReportedFeedbackEmail; surface reporter email in comment.feedback template body (was only accessible via the now-removed header). Update unit tests to assert Reply-To is absent. --- src/Service/Mailer.php | 7 ++++--- templates/emails/comment.feedback.html.twig | 2 +- tests/Service/MailerTest.php | 8 ++------ 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/Service/Mailer.php b/src/Service/Mailer.php index 7c6818b5bd..5151a5f053 100644 --- a/src/Service/Mailer.php +++ b/src/Service/Mailer.php @@ -93,6 +93,7 @@ public function sendCommentReportedFeedbackEmail(Member $member, $parameters): b { $parameters['sender'] = $member; $parameters['receiver'] = $member; + $parameters['reporterEmail'] = $member->getEmail(); $feedbackCategoryRepository = $this->entityManager->getRepository(FeedbackCategory::class); $feedbackCategory = $feedbackCategoryRepository->findOneBy(['name' => 'Comment_issue']); @@ -101,7 +102,6 @@ public function sendCommentReportedFeedbackEmail(Member $member, $parameters): b new Address($feedbackCategory->getEmailToNotify(), 'Comment Issue'), 'comment.feedback', $parameters, - $member->getEmail(), ); } @@ -149,7 +149,9 @@ public function sendNotificationEmail(Address $sender, Member $receiver, $parame /** * Sends contact/feedback form submissions to the helpdesk queue. - * From: is always noreply@bewelcome.org to pass DMARC; the reporter's address goes in Reply-To. + * From: noreply@bewelcome.org (passes DMARC). No Reply-To — freemail in Reply-To triggers + * SPOOF_REPLYTO+FREEMAIL_REPLYTO_NEQ_FROM in rspamd, scoring ~8 pts and routing to Junk. + * The reporter's address is already rendered in the email body by feedback.html.twig. */ public function sendFeedbackEmail($sender, Address $receiver, $parameters): bool { @@ -161,7 +163,6 @@ public function sendFeedbackEmail($sender, Address $receiver, $parameters): bool $receiver, 'feedback', $parameters, - \is_string($sender) ? $sender : null, ); } diff --git a/templates/emails/comment.feedback.html.twig b/templates/emails/comment.feedback.html.twig index 454f8c35cc..f63478c5e7 100644 --- a/templates/emails/comment.feedback.html.twig +++ b/templates/emails/comment.feedback.html.twig @@ -1,7 +1,7 @@ {% extends 'emails/email.html.twig' %} {% block content%} -

The user {{ comment.toMember.username }} provided feedback for a comment:

+

The user {{ comment.toMember.username }} ({{ reporterEmail }}) provided feedback for a comment:

{{ feedback }}

Please check the comment here: Comment from {{ comment.fromMember.username }}

{% endblock content %} diff --git a/tests/Service/MailerTest.php b/tests/Service/MailerTest.php index 8f6561a2e4..c1997b5840 100644 --- a/tests/Service/MailerTest.php +++ b/tests/Service/MailerTest.php @@ -72,9 +72,7 @@ public function testSendFeedbackEmailUsesNoreplyAsFrom(): void $this->assertCount(1, $from); $this->assertSame('noreply@bewelcome.org', $from[0]->getAddress(), 'From must be noreply to pass DMARC'); - $replyTo = $captured->getReplyTo(); - $this->assertCount(1, $replyTo); - $this->assertSame('reporter@yahoo.fr', $replyTo[0]->getAddress(), 'Reply-To must be the reporter so staff can reply'); + $this->assertEmpty($captured->getReplyTo(), 'No Reply-To: freemail Reply-To triggers SPOOF_REPLYTO in rspamd and routes to Junk'); } public function testSendFeedbackEmailNullFallbackUsesNoreplyWithNoReplyTo(): void @@ -155,8 +153,6 @@ public function testSendCommentReportedFeedbackEmailUsesNoreplyAsFrom(): void $this->assertCount(1, $from); $this->assertSame('noreply@bewelcome.org', $from[0]->getAddress(), 'From must be noreply to pass DMARC'); - $replyTo = $captured->getReplyTo(); - $this->assertCount(1, $replyTo); - $this->assertSame('member@gmail.com', $replyTo[0]->getAddress(), 'Reply-To must be the member so staff can reply'); + $this->assertEmpty($captured->getReplyTo(), 'No Reply-To: freemail Reply-To triggers SPOOF_REPLYTO in rspamd and routes to Junk'); } }