mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-04 05:53:43 +00:00 
			
		
		
		
	CVE-2025-52559: Generate HTML for digest new channels safely.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
		
				
					committed by
					
						
						Tim Abbott
					
				
			
			
				
	
			
			
			
						parent
						
							1a8429e338
						
					
				
				
					commit
					175ec1f365
				
			@@ -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
 | 
			
		||||
@@ -250,7 +251,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:
 | 
			
		||||
@@ -261,7 +262,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)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,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
 | 
			
		||||
@@ -29,7 +30,7 @@ from zerver.lib.cache import dict_to_items_tuple, ignore_unhashable_lru_cache, i
 | 
			
		||||
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(
 | 
			
		||||
@@ -39,7 +40,7 @@ def and_n_others(values: list[str], limit: int) -> str:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@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
 | 
			
		||||
@@ -51,16 +52,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
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user