summaryrefslogtreecommitdiff
path: root/blog
diff options
context:
space:
mode:
authorBaptiste Mispelon <bmispelon@gmail.com>2025-05-31 00:32:30 +0200
committerGitHub <noreply@github.com>2025-05-30 23:32:30 +0100
commit253cc2a4499cfe0935c4e4462a3562a4650df317 (patch)
tree8cc6e2c7b0774bec548773c8175b22f17be9f476 /blog
parentc68e183cc0b04ee435d87ff10cec9a1d402291d0 (diff)
Fixed escaping of alt text in ContentFormat.img() (#2036)
* Fixed escaping of alt text in ContentFormat.img() * Simpler approach
Diffstat (limited to 'blog')
-rw-r--r--blog/models.py3
-rw-r--r--blog/tests.py40
2 files changed, 42 insertions, 1 deletions
diff --git a/blog/models.py b/blog/models.py
index 0083dd22..988c607e 100644
--- a/blog/models.py
+++ b/blog/models.py
@@ -8,6 +8,7 @@ from django.test import RequestFactory
from django.utils import timezone
from django.utils.cache import _generate_cache_header_key
from django.utils.formats import date_format
+from django.utils.html import format_html
from django.utils.translation import gettext_lazy as _
from django_hosts.resolvers import get_host, reverse, reverse_host
from docutils.core import publish_parts
@@ -73,7 +74,7 @@ class ContentFormat(models.TextChoices):
CF = type(self)
return {
CF.REST: f".. image:: {url}\n :alt: {alt_text}",
- CF.HTML: f'<img src="{url}" alt="{alt_text}">',
+ CF.HTML: format_html('<img src="{}" alt="{}">', url, alt_text),
CF.MARKDOWN: f"![{alt_text}]({url})",
}[self]
diff --git a/blog/tests.py b/blog/tests.py
index 60a0e511..15c6a4b9 100644
--- a/blog/tests.py
+++ b/blog/tests.py
@@ -297,3 +297,43 @@ class ImageUploadTestCase(TestCase):
r"http://www\.djangoproject\.localhost:8000"
r"/m/blog/images/2005/07/test(_\w+)?\.png",
)
+
+ def test_alt_text_html_escape(self):
+ testdata = [
+ (ContentFormat.HTML, 'te"st', '<img src="." alt="te&quot;st">'),
+ (ContentFormat.HTML, "te<st>", '<img src="." alt="te&lt;st&gt;">'),
+ (ContentFormat.MARKDOWN, 'te"st', '<img src="." alt="te&quot;st">'),
+ (ContentFormat.MARKDOWN, "te[st]", '<img src="." alt="te[st]">'),
+ (ContentFormat.MARKDOWN, "te{st}", '<img src="." alt="te{st}">'),
+ (ContentFormat.MARKDOWN, "te<st>", '<img src="." alt="te&lt;st&gt;">'),
+ (ContentFormat.MARKDOWN, "test*", '<img src="." alt="test*">'),
+ (ContentFormat.MARKDOWN, "test_", '<img src="." alt="test_">'),
+ (ContentFormat.MARKDOWN, "test`", '<img src="." alt="test`">'),
+ (ContentFormat.MARKDOWN, "test+", '<img src="." alt="test+">'),
+ (ContentFormat.MARKDOWN, "test-", '<img src="." alt="test-">'),
+ (ContentFormat.MARKDOWN, "test.", '<img src="." alt="test.">'),
+ (ContentFormat.MARKDOWN, "test!", '<img src="." alt="test!">'),
+ (ContentFormat.MARKDOWN, "te\nst", '<img src="." alt="te\nst">'),
+ (ContentFormat.REST, 'te"st', '<img src="." alt="te&quot;st">'),
+ (ContentFormat.REST, "te[st]", '<img src="." alt="te[st]">'),
+ (ContentFormat.REST, "te{st}", '<img src="." alt="te{st}">'),
+ (ContentFormat.REST, "te<st>", '<img src="." alt="te&lt;st&gt;">'),
+ (ContentFormat.REST, "te:st", '<img src="." alt="te:st">'),
+ (ContentFormat.REST, "test*", '<img src="." alt="test*">'),
+ (ContentFormat.REST, "test_", '<img src="." alt="test_">'),
+ (ContentFormat.REST, "test`", '<img src="." alt="test`">'),
+ (ContentFormat.REST, "test+", '<img src="." alt="test+">'),
+ (ContentFormat.REST, "test-", '<img src="." alt="test-">'),
+ (ContentFormat.REST, "test.", '<img src="." alt="test.">'),
+ (ContentFormat.REST, "test!", '<img src="." alt="test!">'),
+ ]
+ for cf, alt_text, expected in testdata:
+ # RST doesn't like an empty src, so we use . instead
+ img_tag = cf.img(url=".", alt_text=alt_text)
+ if cf is ContentFormat.MARKDOWN:
+ expected = f"<p>{expected}</p>"
+ with self.subTest(cf=cf, alt_text=alt_text):
+ self.assertHTMLEqual(
+ ContentFormat.to_html(cf, img_tag),
+ expected,
+ )