diff --git a/pyproject.toml b/pyproject.toml index ad359256d7..7a5202657f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -90,6 +90,7 @@ module = [ "social_core.*", "social_django.*", "sourcemap.*", + "soupsieve.*", "sphinx_rtd_theme.*", "talon_core.*", "tlds.*", diff --git a/requirements/common.in b/requirements/common.in index 1a35918e7f..9558e77831 100644 --- a/requirements/common.in +++ b/requirements/common.in @@ -191,3 +191,6 @@ django-cte # SCIM integration django-scim2 + +# CSS manipulation +soupsieve diff --git a/requirements/dev.txt b/requirements/dev.txt index 9699f947f6..d43dda59bb 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -1621,7 +1621,9 @@ social-auth-core[azuread,openidconnect,saml]==4.1.0 \ soupsieve==2.2.1 \ --hash=sha256:052774848f448cf19c7e959adf5566904d525f33a3f8b6ba6f6f8f26ec7de0cc \ --hash=sha256:c2c1c2d44f158cdbddab7824a9af8c4f83c76b1e23e049479aa432feb6c4c23b - # via beautifulsoup4 + # via + # -r requirements/common.in + # beautifulsoup4 sourcemap==0.2.1 \ --hash=sha256:be00a90185e7a16b87bbe62a68ffd5e38bc438ef4700806d9b90e44d8027787c \ --hash=sha256:c448a8c48f9482e522e4582106b0c641a83b5dbc7f13927b178848e3ea20967b diff --git a/requirements/prod.txt b/requirements/prod.txt index 4b0011ed5b..a53bfd607a 100644 --- a/requirements/prod.txt +++ b/requirements/prod.txt @@ -1118,7 +1118,9 @@ social-auth-core[azuread,openidconnect,saml]==4.1.0 \ soupsieve==2.2.1 \ --hash=sha256:052774848f448cf19c7e959adf5566904d525f33a3f8b6ba6f6f8f26ec7de0cc \ --hash=sha256:c2c1c2d44f158cdbddab7824a9af8c4f83c76b1e23e049479aa432feb6c4c23b - # via beautifulsoup4 + # via + # -r requirements/common.in + # beautifulsoup4 sourcemap==0.2.1 \ --hash=sha256:be00a90185e7a16b87bbe62a68ffd5e38bc438ef4700806d9b90e44d8027787c \ --hash=sha256:c448a8c48f9482e522e4582106b0c641a83b5dbc7f13927b178848e3ea20967b diff --git a/zerver/lib/markdown/__init__.py b/zerver/lib/markdown/__init__.py index 675470764a..5a5ca00513 100644 --- a/zerver/lib/markdown/__init__.py +++ b/zerver/lib/markdown/__init__.py @@ -43,6 +43,7 @@ import requests from django.conf import settings from markdown.blockparser import BlockParser from markdown.extensions import codehilite, nl2br, sane_lists, tables +from soupsieve import escape as css_escape from tlds import tld_set from typing_extensions import TypedDict @@ -714,7 +715,7 @@ class InlineInterestingLinkProcessor(markdown.treeprocessors.Treeprocessor): img_link = get_camo_url(img_link) img = SubElement(container, "a") - img.set("style", "background-image: url(" + img_link + ")") + img.set("style", "background-image: url(" + css_escape(img_link) + ")") img.set("href", link) img.set("class", "message_embed_image") diff --git a/zerver/tests/test_link_embed.py b/zerver/tests/test_link_embed.py index dc437c8aa3..32deb8d54b 100644 --- a/zerver/tests/test_link_embed.py +++ b/zerver/tests/test_link_embed.py @@ -1,3 +1,4 @@ +import re from collections import OrderedDict from typing import Any, Optional, Union from unittest import mock @@ -524,7 +525,7 @@ class PreviewTestCase(ZulipTestCase): @override_settings(CAMO_URI="") def test_inline_url_embed_preview(self) -> None: - with_preview = '
\n ' + with_preview = '\n ' without_preview = '' msg = self._send_message_with_test_org_url(sender=self.example_user("hamlet")) self.assertEqual(msg.rendered_content, with_preview) @@ -539,7 +540,9 @@ class PreviewTestCase(ZulipTestCase): self.assertEqual(msg.rendered_content, without_preview) def test_inline_url_embed_preview_with_camo(self) -> None: - camo_url = get_camo_url("http://ia.media-imdb.com/images/rock.jpg") + camo_url = re.sub( + r"([^\w-])", r"\\\1", get_camo_url("http://ia.media-imdb.com/images/rock.jpg") + ) with_preview = ( '\n