From 64aa009bb957309a6938724b2e493ff69354c49c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C4=8Ciha=C5=99?= Date: Tue, 15 Oct 2024 14:17:01 +0200 Subject: [PATCH] feat: quote audit log values to avoid auto linking Some e-mail services such as Gmail automatically convert things that look like a link into links even in HTML mails. This could be problematic for audit log entries as want to display the information as is without turning that into links. Adding seems to help in this according to https://stackoverflow.com/a/23404042/225718 --- weblate/accounts/models.py | 9 ++++++--- weblate/utils/html.py | 24 ++++++++++++++++++++++++ weblate/utils/tests/test_html.py | 27 ++++++++++++++++++++++++++- 3 files changed, 56 insertions(+), 4 deletions(-) diff --git a/weblate/accounts/models.py b/weblate/accounts/models.py index 5a8a77512f48..b2f9107ff381 100644 --- a/weblate/accounts/models.py +++ b/weblate/accounts/models.py @@ -46,6 +46,7 @@ from weblate.utils import messages from weblate.utils.decorators import disable_for_loaddata from weblate.utils.fields import EmailField +from weblate.utils.html import mail_quote_value from weblate.utils.render import validate_editor from weblate.utils.request import get_ip_address, get_user_agent from weblate.utils.token import get_token @@ -351,10 +352,12 @@ def get_params(self): "site_title": settings.SITE_TITLE, } for name, value in self.params.items(): - if name in {"old", "new", "name", "email", "username"}: - value = format_html("{}", value) + if value is None: + value = format_html("{}", value) + elif name in {"old", "new", "name", "email", "username"}: + value = format_html("{}", mail_quote_value(value)) elif name in {"device", "project", "site_title", "method"}: - value = format_html("{}", value) + value = format_html("{}", mail_quote_value(value)) result[name] = value diff --git a/weblate/utils/html.py b/weblate/utils/html.py index 4f4eae3e8eeb..9c7b0ebd547f 100644 --- a/weblate/utils/html.py +++ b/weblate/utils/html.py @@ -9,10 +9,12 @@ from typing import TYPE_CHECKING import nh3 +from django.utils.html import format_html, format_html_join from html2text import HTML2Text as _HTML2Text from lxml.etree import HTMLParser if TYPE_CHECKING: + from django.utils.safestring import SafeString from lxml.etree import ParserTarget from weblate.checks.flags import Flags @@ -164,3 +166,25 @@ def handle_tag(self, tag: str, attrs: dict[str, str | None], start: bool) -> Non self.o(WEBLATE_TAGS[tag][not start]) return super().handle_tag(tag, attrs, start) + + +def mail_quote_char(text: str) -> str | SafeString: + if text in {":", "."}: + return format_html("{}", text) + return text + + +def mail_quote_value(text: str) -> str | SafeString: + """ + Quote value to be used in e-mail notifications. + + This tries to avoid automatic conversion to links by Gmail + and similar services. + + Solution based on https://stackoverflow.com/a/23404042/225718 + """ + return format_html_join( + "", + "{}", + ((mail_quote_char(part),) for part in re.split("([.:])", text)), + ) diff --git a/weblate/utils/tests/test_html.py b/weblate/utils/tests/test_html.py index 8dd276ea9a7e..69deed6020e1 100644 --- a/weblate/utils/tests/test_html.py +++ b/weblate/utils/tests/test_html.py @@ -7,7 +7,12 @@ from django.test import SimpleTestCase from weblate.checks.flags import Flags -from weblate.utils.html import HTML2Text, HTMLSanitizer, extract_html_tags +from weblate.utils.html import ( + HTML2Text, + HTMLSanitizer, + extract_html_tags, + mail_quote_value, +) class HTMLSanitizerTestCase(SimpleTestCase): @@ -104,3 +109,23 @@ def test_html2text_diff(self) -> None: html2text.handle("text "), "text{+ +}\n\n", ) + + +class MailQuoteTestCase(SimpleTestCase): + def test_plain(self): + self.assertEqual( + mail_quote_value("text"), + "text", + ) + + def test_dot(self): + self.assertEqual( + mail_quote_value("example.com"), + "example.com", + ) + + def test_url(self): + self.assertEqual( + mail_quote_value("https://test.example.com"), + "https://test.example.com", + )