From f48a3f30857f477f8be2f03899172b6d3940a2cf Mon Sep 17 00:00:00 2001 From: Taylor Osler Date: Thu, 22 Aug 2024 11:27:07 -0700 Subject: [PATCH] fix(email): use a custom engine for plaintext emails --- src/sentry/utils/email/message_builder.py | 10 +++++++++- src/sentry/web/helpers.py | 11 +++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/sentry/utils/email/message_builder.py b/src/sentry/utils/email/message_builder.py index a39916542dc17b..c961fd0e0f7eb5 100644 --- a/src/sentry/utils/email/message_builder.py +++ b/src/sentry/utils/email/message_builder.py @@ -11,7 +11,9 @@ import lxml.html import toronado +from django.conf import settings from django.core.mail import EmailMultiAlternatives +from django.template.engine import Engine from django.utils.encoding import force_str from sentry import options @@ -132,8 +134,14 @@ def __render_html_body(self) -> str | None: return inline_css(html_body) def __render_text_body(self) -> str: + # Create a clone of the default Django templater with autoescape disabled. + # TODO: cache this? + params = settings.TEMPLATES[0].copy() + params["OPTIONS"]["autoescape"] = False + engine = Engine(params["DIRS"], params["APP_DIRS"], **params["OPTIONS"]) + if self.template: - body: str = render_to_string(self.template, self.context) + body: str = render_to_string(self.template, self.context, engine=engine) return body return self._txt_body diff --git a/src/sentry/web/helpers.py b/src/sentry/web/helpers.py index 3f0e311958381b..5a77afb4dbff92 100644 --- a/src/sentry/web/helpers.py +++ b/src/sentry/web/helpers.py @@ -5,7 +5,8 @@ from typing import Any from django.http import HttpRequest, HttpResponse -from django.template import loader +from django.template import Context, loader +from django.template.engine import Engine from django.utils import timezone from sentry.utils.dates import AVAILABLE_TIMEZONES @@ -17,6 +18,7 @@ def render_to_string( template: Sequence[str] | str, context: Mapping[str, Any] | None = None, request: HttpRequest | None = None, + engine: Engine | None = None, ) -> str: if context is None: context = dict() @@ -26,7 +28,12 @@ def render_to_string( if "timezone" in context and context["timezone"] in AVAILABLE_TIMEZONES: timezone.activate(context["timezone"]) - rendered = loader.render_to_string(template, context=context, request=request) + if engine is None: + rendered = loader.render_to_string(template, context=context, request=request) + else: + template_compiled = engine.get_template(template) + rendered = template_compiled.render(Context(context, autoescape=engine.autoescape)) + timezone.deactivate() return rendered