diff --git a/authentik/stages/email/tests/test_sending.py b/authentik/stages/email/tests/test_sending.py index 631983ab67..fc36cc7db6 100644 --- a/authentik/stages/email/tests/test_sending.py +++ b/authentik/stages/email/tests/test_sending.py @@ -8,7 +8,7 @@ from django.core.mail.backends.locmem import EmailBackend from django.urls import reverse from authentik.core.models import User -from authentik.core.tests.utils import create_test_admin_user, create_test_flow +from authentik.core.tests.utils import create_test_admin_user, create_test_flow, create_test_user from authentik.events.models import Event, EventAction from authentik.flows.markers import StageMarker from authentik.flows.models import FlowDesignation, FlowStageBinding @@ -67,6 +67,36 @@ class TestEmailStageSending(FlowTestCase): self.assertEqual(event.context["to_email"], [f"{self.user.name} <{self.user.email}>"]) self.assertEqual(event.context["from_email"], "system@authentik.local") + def test_newlines_long_name(self): + """Test with pending user""" + plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()]) + long_user = create_test_user() + long_user.name = "Test User\r\n Many Words\r\n" + long_user.save() + plan.context[PLAN_CONTEXT_PENDING_USER] = long_user + session = self.client.session + session[SESSION_KEY_PLAN] = plan + session.save() + Event.objects.filter(action=EventAction.EMAIL_SENT).delete() + + url = reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}) + with patch( + "authentik.stages.email.models.EmailStage.backend_class", + PropertyMock(return_value=EmailBackend), + ): + response = self.client.post(url) + self.assertEqual(response.status_code, 200) + self.assertStageResponse( + response, + self.flow, + response_errors={ + "non_field_errors": [{"string": "email-sent", "code": "email-sent"}] + }, + ) + self.assertEqual(len(mail.outbox), 1) + self.assertEqual(mail.outbox[0].subject, "authentik") + self.assertEqual(mail.outbox[0].to, [f"Test User Many Words <{long_user.email}>"]) + def test_pending_fake_user(self): """Test with pending (fake) user""" self.flow.designation = FlowDesignation.RECOVERY diff --git a/authentik/stages/email/utils.py b/authentik/stages/email/utils.py index 22beb1294c..f19d9c1ea6 100644 --- a/authentik/stages/email/utils.py +++ b/authentik/stages/email/utils.py @@ -32,7 +32,14 @@ class TemplateEmailMessage(EmailMultiAlternatives): sanitized_to = [] # Ensure that all recipients are valid for recipient_name, recipient_email in to: - sanitized_to.append(sanitize_address((recipient_name, recipient_email), "utf-8")) + # Remove any newline characters from name and email before sanitizing + clean_name = ( + recipient_name.replace("\n", " ").replace("\r", " ") if recipient_name else "" + ) + clean_email = ( + recipient_email.replace("\n", "").replace("\r", "") if recipient_email else "" + ) + sanitized_to.append(sanitize_address((clean_name, clean_email), "utf-8")) super().__init__(to=sanitized_to, **kwargs) if not template_name: return