Skip to content

Commit 253cc2a

Browse files
authored
Fixed escaping of alt text in ContentFormat.img() (#2036)
* Fixed escaping of alt text in ContentFormat.img() * Simpler approach
1 parent c68e183 commit 253cc2a

File tree

2 files changed

+42
-1
lines changed

2 files changed

+42
-1
lines changed

blog/models.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from django.utils import timezone
99
from django.utils.cache import _generate_cache_header_key
1010
from django.utils.formats import date_format
11+
from django.utils.html import format_html
1112
from django.utils.translation import gettext_lazy as _
1213
from django_hosts.resolvers import get_host, reverse, reverse_host
1314
from docutils.core import publish_parts
@@ -73,7 +74,7 @@ def img(self, url, alt_text):
7374
CF = type(self)
7475
return {
7576
CF.REST: f".. image:: {url}\n :alt: {alt_text}",
76-
CF.HTML: f'<img src="{url}" alt="{alt_text}">',
77+
CF.HTML: format_html('<img src="{}" alt="{}">', url, alt_text),
7778
CF.MARKDOWN: f"![{alt_text}]({url})",
7879
}[self]
7980

blog/tests.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,3 +297,43 @@ def test_full_url(self):
297297
r"http://www\.djangoproject\.localhost:8000"
298298
r"/m/blog/images/2005/07/test(_\w+)?\.png",
299299
)
300+
301+
def test_alt_text_html_escape(self):
302+
testdata = [
303+
(ContentFormat.HTML, 'te"st', '<img src="." alt="te&quot;st">'),
304+
(ContentFormat.HTML, "te<st>", '<img src="." alt="te&lt;st&gt;">'),
305+
(ContentFormat.MARKDOWN, 'te"st', '<img src="." alt="te&quot;st">'),
306+
(ContentFormat.MARKDOWN, "te[st]", '<img src="." alt="te[st]">'),
307+
(ContentFormat.MARKDOWN, "te{st}", '<img src="." alt="te{st}">'),
308+
(ContentFormat.MARKDOWN, "te<st>", '<img src="." alt="te&lt;st&gt;">'),
309+
(ContentFormat.MARKDOWN, "test*", '<img src="." alt="test*">'),
310+
(ContentFormat.MARKDOWN, "test_", '<img src="." alt="test_">'),
311+
(ContentFormat.MARKDOWN, "test`", '<img src="." alt="test`">'),
312+
(ContentFormat.MARKDOWN, "test+", '<img src="." alt="test+">'),
313+
(ContentFormat.MARKDOWN, "test-", '<img src="." alt="test-">'),
314+
(ContentFormat.MARKDOWN, "test.", '<img src="." alt="test.">'),
315+
(ContentFormat.MARKDOWN, "test!", '<img src="." alt="test!">'),
316+
(ContentFormat.MARKDOWN, "te\nst", '<img src="." alt="te\nst">'),
317+
(ContentFormat.REST, 'te"st', '<img src="." alt="te&quot;st">'),
318+
(ContentFormat.REST, "te[st]", '<img src="." alt="te[st]">'),
319+
(ContentFormat.REST, "te{st}", '<img src="." alt="te{st}">'),
320+
(ContentFormat.REST, "te<st>", '<img src="." alt="te&lt;st&gt;">'),
321+
(ContentFormat.REST, "te:st", '<img src="." alt="te:st">'),
322+
(ContentFormat.REST, "test*", '<img src="." alt="test*">'),
323+
(ContentFormat.REST, "test_", '<img src="." alt="test_">'),
324+
(ContentFormat.REST, "test`", '<img src="." alt="test`">'),
325+
(ContentFormat.REST, "test+", '<img src="." alt="test+">'),
326+
(ContentFormat.REST, "test-", '<img src="." alt="test-">'),
327+
(ContentFormat.REST, "test.", '<img src="." alt="test.">'),
328+
(ContentFormat.REST, "test!", '<img src="." alt="test!">'),
329+
]
330+
for cf, alt_text, expected in testdata:
331+
# RST doesn't like an empty src, so we use . instead
332+
img_tag = cf.img(url=".", alt_text=alt_text)
333+
if cf is ContentFormat.MARKDOWN:
334+
expected = f"<p>{expected}</p>"
335+
with self.subTest(cf=cf, alt_text=alt_text):
336+
self.assertHTMLEqual(
337+
ContentFormat.to_html(cf, img_tag),
338+
expected,
339+
)

0 commit comments

Comments
 (0)