CVE-2025-52559: Generate HTML for digest new channels safely.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
Anders Kaseorg
2025-06-23 16:28:17 -07:00
committed by Tim Abbott
parent a274170293
commit 08a17ef22d
2 changed files with 13 additions and 8 deletions

View File

@@ -11,6 +11,7 @@ from django.db import transaction
from django.db.models import Exists, OuterRef, QuerySet
from django.utils.timezone import now as timezone_now
from django.utils.translation import gettext as _
from markupsafe import Markup
from confirmation.models import one_click_unsubscribe_link
from zerver.context_processors import common_context
@@ -254,7 +255,7 @@ def gather_new_streams(
realm: Realm,
recently_created_streams: list[Stream], # streams only need id and name
can_access_public: bool,
) -> tuple[int, dict[str, list[str]]]:
) -> tuple[int, dict[str, list[Markup] | list[str]]]:
if can_access_public:
new_streams = [stream for stream in recently_created_streams if not stream.invite_only]
else:
@@ -265,7 +266,9 @@ def gather_new_streams(
for stream in new_streams:
narrow_url = stream_narrow_url(realm, stream)
channel_link = f"<a href='{narrow_url}'>{stream.name}</a>"
channel_link = Markup("<a href='{narrow_url}'>{stream_name}</a>").format(
narrow_url=narrow_url, stream_name=stream.name
)
channels_html.append(channel_link)
channels_plain.append(stream.name)

View File

@@ -13,6 +13,7 @@ from django.contrib.staticfiles.storage import staticfiles_storage
from django.template import Library, engines
from django.template.backends.jinja2 import Jinja2
from django.utils.safestring import mark_safe
from markupsafe import Markup
import zerver.lib.markdown.api_arguments_table_generator
import zerver.lib.markdown.api_return_values_table_generator
@@ -35,7 +36,7 @@ from zerver.openapi.merge_api_changelogs import (
register = Library()
def and_n_others(values: list[str], limit: int) -> str:
def and_n_others(values: list[str | Markup], limit: int) -> str | Markup:
# A helper for the commonly appended "and N other(s)" string, with
# the appropriate pluralization.
return " and {} other{}".format(
@@ -54,7 +55,7 @@ def get_updated_changelog() -> str | None:
@register.filter(name="display_list", is_safe=True)
def display_list(values: list[str], display_limit: int) -> str:
def display_list(values: list[str | Markup], display_limit: int) -> str | Markup:
"""
Given a list of values, return a string nicely formatting those values,
summarizing when you have more than `display_limit`. Eg, for a
@@ -66,16 +67,17 @@ def display_list(values: list[str], display_limit: int) -> str:
Jessica, Waseem, Tim, and 1 other
Jessica, Waseem, Tim, and 2 others
"""
sep = Markup(", ") if any(isinstance(value, Markup) for value in values) else ", "
if len(values) == 1:
# One value, show it.
display_string = f"{values[0]}"
display_string = values[0]
elif len(values) <= display_limit:
# Fewer than `display_limit` values, show all of them.
display_string = ", ".join(f"{value}" for value in values[:-1])
display_string += f" and {values[-1]}"
display_string = sep.join(value for value in values[:-1])
display_string += " and " + values[-1]
else:
# More than `display_limit` values, only mention a few.
display_string = ", ".join(f"{value}" for value in values[:display_limit])
display_string = sep.join(value for value in values[:display_limit])
display_string += and_n_others(values, display_limit)
return display_string