mirror of https://github.com/zulip/zulip.git
markdown: Switch to directly URL-escaping CSS URLs.
soupsieve is a heavy-weight dependency, and Tornado pulls it in by way of markdown rendering; since we are only using it for a very simple process, perform that manually. Per CSS spec[^1]: > In quoted <string> url()s, only newlines and the character used to > quote the string need to be escaped. [^1]: https://drafts.csswg.org/css-values/#urls
This commit is contained in:
parent
1424a2e748
commit
693b959656
|
@ -186,9 +186,6 @@ django-cte
|
||||||
# SCIM integration
|
# SCIM integration
|
||||||
django-scim2
|
django-scim2
|
||||||
|
|
||||||
# CSS manipulation
|
|
||||||
soupsieve
|
|
||||||
|
|
||||||
# Circuit-breaking for outgoing services
|
# Circuit-breaking for outgoing services
|
||||||
circuitbreaker
|
circuitbreaker
|
||||||
|
|
||||||
|
|
|
@ -2894,9 +2894,7 @@ sortedcontainers==2.4.0 \
|
||||||
soupsieve==2.5 \
|
soupsieve==2.5 \
|
||||||
--hash=sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690 \
|
--hash=sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690 \
|
||||||
--hash=sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7
|
--hash=sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7
|
||||||
# via
|
# via beautifulsoup4
|
||||||
# -r requirements/common.in
|
|
||||||
# beautifulsoup4
|
|
||||||
sphinx==7.2.6 \
|
sphinx==7.2.6 \
|
||||||
--hash=sha256:1e09160a40b956dc623c910118fa636da93bd3ca0b9876a7b3df90f07d691560 \
|
--hash=sha256:1e09160a40b956dc623c910118fa636da93bd3ca0b9876a7b3df90f07d691560 \
|
||||||
--hash=sha256:9a5160e1ea90688d5963ba09a2dcd8bdd526620edbb65c328728f1b2228d5ab5
|
--hash=sha256:9a5160e1ea90688d5963ba09a2dcd8bdd526620edbb65c328728f1b2228d5ab5
|
||||||
|
|
|
@ -2240,9 +2240,7 @@ social-auth-core[azuread,openidconnect,saml]==4.5.3 \
|
||||||
soupsieve==2.5 \
|
soupsieve==2.5 \
|
||||||
--hash=sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690 \
|
--hash=sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690 \
|
||||||
--hash=sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7
|
--hash=sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7
|
||||||
# via
|
# via beautifulsoup4
|
||||||
# -r requirements/common.in
|
|
||||||
# beautifulsoup4
|
|
||||||
sqlalchemy==1.4.52 \
|
sqlalchemy==1.4.52 \
|
||||||
--hash=sha256:1296f2cdd6db09b98ceb3c93025f0da4835303b8ac46c15c2136e27ee4d18d94 \
|
--hash=sha256:1296f2cdd6db09b98ceb3c93025f0da4835303b8ac46c15c2136e27ee4d18d94 \
|
||||||
--hash=sha256:1e135fff2e84103bc15c07edd8569612ce317d64bdb391f49ce57124a73f45c5 \
|
--hash=sha256:1e135fff2e84103bc15c07edd8569612ce317d64bdb391f49ce57124a73f45c5 \
|
||||||
|
|
|
@ -47,7 +47,6 @@ import uri_template
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from markdown.blockparser import BlockParser
|
from markdown.blockparser import BlockParser
|
||||||
from markdown.extensions import codehilite, nl2br, sane_lists, tables
|
from markdown.extensions import codehilite, nl2br, sane_lists, tables
|
||||||
from soupsieve import escape as css_escape
|
|
||||||
from tlds import tld_set
|
from tlds import tld_set
|
||||||
from typing_extensions import Self, TypeAlias, override
|
from typing_extensions import Self, TypeAlias, override
|
||||||
|
|
||||||
|
@ -690,7 +689,12 @@ class InlineInterestingLinkProcessor(markdown.treeprocessors.Treeprocessor):
|
||||||
|
|
||||||
img_link = get_camo_url(extracted_data.image)
|
img_link = get_camo_url(extracted_data.image)
|
||||||
img = SubElement(container, "a")
|
img = SubElement(container, "a")
|
||||||
img.set("style", "background-image: url(" + css_escape(img_link) + ")")
|
img.set(
|
||||||
|
"style",
|
||||||
|
'background-image: url("'
|
||||||
|
+ img_link.replace("\\", "\\\\").replace('"', '\\"').replace("\n", "\\a ")
|
||||||
|
+ '")',
|
||||||
|
)
|
||||||
img.set("href", link)
|
img.set("href", link)
|
||||||
img.set("class", "message_embed_image")
|
img.set("class", "message_embed_image")
|
||||||
|
|
||||||
|
|
|
@ -534,7 +534,7 @@ class PreviewTestCase(ZulipTestCase):
|
||||||
|
|
||||||
@override_settings(CAMO_URI="")
|
@override_settings(CAMO_URI="")
|
||||||
def test_inline_url_embed_preview(self) -> None:
|
def test_inline_url_embed_preview(self) -> None:
|
||||||
with_preview = '<p><a href="http://test.org/">http://test.org/</a></p>\n<div class="message_embed"><a class="message_embed_image" href="http://test.org/" style="background-image: url(http\\:\\/\\/ia\\.media-imdb\\.com\\/images\\/rock\\.jpg)"></a><div class="data-container"><div class="message_embed_title"><a href="http://test.org/" title="The Rock">The Rock</a></div><div class="message_embed_description">Description text</div></div></div>'
|
with_preview = '<p><a href="http://test.org/">http://test.org/</a></p>\n<div class="message_embed"><a class="message_embed_image" href="http://test.org/" style="background-image: url("http://ia.media-imdb.com/images/rock.jpg")"></a><div class="data-container"><div class="message_embed_title"><a href="http://test.org/" title="The Rock">The Rock</a></div><div class="message_embed_description">Description text</div></div></div>'
|
||||||
without_preview = '<p><a href="http://test.org/">http://test.org/</a></p>'
|
without_preview = '<p><a href="http://test.org/">http://test.org/</a></p>'
|
||||||
msg = self._send_message_with_test_org_url(sender=self.example_user("hamlet"))
|
msg = self._send_message_with_test_org_url(sender=self.example_user("hamlet"))
|
||||||
self.assertEqual(msg.rendered_content, with_preview)
|
self.assertEqual(msg.rendered_content, with_preview)
|
||||||
|
@ -549,13 +549,11 @@ class PreviewTestCase(ZulipTestCase):
|
||||||
self.assertEqual(msg.rendered_content, without_preview)
|
self.assertEqual(msg.rendered_content, without_preview)
|
||||||
|
|
||||||
def test_inline_url_embed_preview_with_camo(self) -> None:
|
def test_inline_url_embed_preview_with_camo(self) -> None:
|
||||||
camo_url = re.sub(
|
camo_url = get_camo_url("http://ia.media-imdb.com/images/rock.jpg")
|
||||||
r"([^\w-])", r"\\\1", get_camo_url("http://ia.media-imdb.com/images/rock.jpg")
|
|
||||||
)
|
|
||||||
with_preview = (
|
with_preview = (
|
||||||
'<p><a href="http://test.org/">http://test.org/</a></p>\n<div class="message_embed"><a class="message_embed_image" href="http://test.org/" style="background-image: url('
|
'<p><a href="http://test.org/">http://test.org/</a></p>\n<div class="message_embed"><a class="message_embed_image" href="http://test.org/" style="background-image: url("'
|
||||||
+ camo_url
|
+ camo_url
|
||||||
+ ')"></a><div class="data-container"><div class="message_embed_title"><a href="http://test.org/" title="The Rock">The Rock</a></div><div class="message_embed_description">Description text</div></div></div>'
|
+ '")"></a><div class="data-container"><div class="message_embed_title"><a href="http://test.org/" title="The Rock">The Rock</a></div><div class="message_embed_description">Description text</div></div></div>'
|
||||||
)
|
)
|
||||||
msg = self._send_message_with_test_org_url(sender=self.example_user("hamlet"))
|
msg = self._send_message_with_test_org_url(sender=self.example_user("hamlet"))
|
||||||
self.assertEqual(msg.rendered_content, with_preview)
|
self.assertEqual(msg.rendered_content, with_preview)
|
||||||
|
@ -575,7 +573,7 @@ class PreviewTestCase(ZulipTestCase):
|
||||||
event = patched.call_args[0][1]
|
event = patched.call_args[0][1]
|
||||||
|
|
||||||
# Swap the URL out for one with characters that need CSS escaping
|
# Swap the URL out for one with characters that need CSS escaping
|
||||||
html = re.sub(r"rock\.jpg", "rock).jpg", self.open_graph_html)
|
html = re.sub(r"rock\.jpg", r"rock.jpg\\", self.open_graph_html)
|
||||||
self.create_mock_response(url, body=html)
|
self.create_mock_response(url, body=html)
|
||||||
with self.settings(TEST_SUITE=False):
|
with self.settings(TEST_SUITE=False):
|
||||||
with self.assertLogs(level="INFO") as info_logs:
|
with self.assertLogs(level="INFO") as info_logs:
|
||||||
|
@ -590,7 +588,7 @@ class PreviewTestCase(ZulipTestCase):
|
||||||
'<p><a href="http://test.org/">http://test.org/</a></p>\n'
|
'<p><a href="http://test.org/">http://test.org/</a></p>\n'
|
||||||
'<div class="message_embed"><a class="message_embed_image" href="http://test.org/"'
|
'<div class="message_embed"><a class="message_embed_image" href="http://test.org/"'
|
||||||
' style="background-image:'
|
' style="background-image:'
|
||||||
' url(http\\:\\/\\/ia\\.media-imdb\\.com\\/images\\/rock\\)\\.jpg)"></a><div'
|
' url("http://ia.media-imdb.com/images/rock.jpg\\\\")"></a><div'
|
||||||
' class="data-container"><div class="message_embed_title"><a href="http://test.org/"'
|
' class="data-container"><div class="message_embed_title"><a href="http://test.org/"'
|
||||||
' title="The Rock">The Rock</a></div><div class="message_embed_description">Description'
|
' title="The Rock">The Rock</a></div><div class="message_embed_description">Description'
|
||||||
" text</div></div></div>"
|
" text</div></div></div>"
|
||||||
|
@ -614,7 +612,7 @@ class PreviewTestCase(ZulipTestCase):
|
||||||
|
|
||||||
@override_settings(CAMO_URI="")
|
@override_settings(CAMO_URI="")
|
||||||
def test_inline_url_embed_preview_with_relative_image_url(self) -> None:
|
def test_inline_url_embed_preview_with_relative_image_url(self) -> None:
|
||||||
with_preview_relative = '<p><a href="http://test.org/">http://test.org/</a></p>\n<div class="message_embed"><a class="message_embed_image" href="http://test.org/" style="background-image: url(http\\:\\/\\/test\\.org\\/images\\/rock\\.jpg)"></a><div class="data-container"><div class="message_embed_title"><a href="http://test.org/" title="The Rock">The Rock</a></div><div class="message_embed_description">Description text</div></div></div>'
|
with_preview_relative = '<p><a href="http://test.org/">http://test.org/</a></p>\n<div class="message_embed"><a class="message_embed_image" href="http://test.org/" style="background-image: url("http://test.org/images/rock.jpg")"></a><div class="data-container"><div class="message_embed_title"><a href="http://test.org/" title="The Rock">The Rock</a></div><div class="message_embed_description">Description text</div></div></div>'
|
||||||
# Try case where the Open Graph image is a relative URL.
|
# Try case where the Open Graph image is a relative URL.
|
||||||
msg = self._send_message_with_test_org_url(
|
msg = self._send_message_with_test_org_url(
|
||||||
sender=self.example_user("prospero"), relative_url=True
|
sender=self.example_user("prospero"), relative_url=True
|
||||||
|
@ -832,7 +830,7 @@ class PreviewTestCase(ZulipTestCase):
|
||||||
assert msg.rendered_content is not None
|
assert msg.rendered_content is not None
|
||||||
self.assertIn(cached_data.title, msg.rendered_content)
|
self.assertIn(cached_data.title, msg.rendered_content)
|
||||||
assert cached_data.image is not None
|
assert cached_data.image is not None
|
||||||
self.assertIn(re.sub(r"([^\w-])", r"\\\1", cached_data.image), msg.rendered_content)
|
self.assertIn(cached_data.image, msg.rendered_content)
|
||||||
|
|
||||||
@responses.activate
|
@responses.activate
|
||||||
@override_settings(INLINE_URL_EMBED_PREVIEW=True)
|
@override_settings(INLINE_URL_EMBED_PREVIEW=True)
|
||||||
|
|
Loading…
Reference in New Issue