CVE-2023-33186: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: Zulip is an open-source team collaboration tool with unique topic-based threading that combines the best of email and chat to make remote work productive and delightful. The main development branch of Zulip Server from May 2, 2023 and later, including beta versions 7.0-beta1 and 7.0-beta2, is vulnerable to a cross-site scripting vulnerability in tooltips on the message feed. An attacker who can send messages could maliciously craft a topic for the message, such that a victim who hovers the tooltip for that topic in their message feed triggers execution of JavaScript code controlled by the attacker.
CWE description: The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
CVSS v3 base score: 6.1
Published: 2023-05-30T06:16Z
Repository: https://github.com/zulip/zulip
Language: Other
Files changed: web/src/tippyjs.js, web/templates/recipient_row.hbs, web/templates/tooltip_templates.hbs
Commit date: 2023-05-02 13:50:46 -0700
Commit message: tooltips: Add `S` hotkey hint to `narrow-to` tippy tooltips.

Added hotkey hint to Narrow to stream/topic/DM tooltips by creating
new tippy for `tippy-narrow-tooltip` with LONG_HOVER_DELAY which
appends `S` hotkey to the existing tippy content set by
data-tippy-content attribute on the element.

Using this approach instead of a <template> with
data-tooltip-template-id avoids issues with context, where
{display_recipient}/{topic}/{display_reply_to} inside <template>
would always show the same stream/topic name regardless of the
actual stream/topic being hovered over.

Diff: diff --git a/web/src/tippyjs.js b/web/src/tippyjs.js
index f2c1bd7dfc4..a06e44bca4a 100644
--- a/web/src/tippyjs.js
+++ b/web/src/tippyjs.js
@@ -183,10 +183,20 @@ export function initialize() {
                 },
             ],
         },
     });
 
+    delegate("body", {
+        target: ".tippy-narrow-tooltip",
+        delay: LONG_HOVER_DELAY,
+        appendTo: () => document.body,
+        onCreate(instance) {
+            const content = instance.props.content + $("#narrow-hotkey-tooltip-template").html();
+            instance.setContent(parse_html(content));
+        },
+    });
+
     delegate("body", {
         target: ".toggle-subscription-tooltip",
         delay: EXTRA_LONG_HOVER_DELAY,
         appendTo: () => document.body,
         placement: "bottom",
diff --git a/web/templates/recipient_row.hbs b/web/templates/recipient_row.hbs
index 85b9e6441b7..768ca2e3bde 100644
--- a/web/templates/recipient_row.hbs
+++ b/web/templates/recipient_row.hbs
@@ -1,10 +1,10 @@
 {{#if is_stream}}
 <div class="message_header message_header_stream right_part" data-stream-id="{{stream_id}}">
     <div class="message-header-contents" style="background: {{recipient_bar_color}};">
         {{! stream link }}
-        <a class="message_label_clickable narrows_by_recipient stream_label tippy-zulip-delayed-tooltip"
+        <a class="message_label_clickable narrows_by_recipient stream_label tippy-narrow-tooltip"
           href="{{stream_url}}"
           data-tippy-content="{{t 'Narrow to stream "{display_recipient}"' }}">
             <span class="stream-privacy-modified-color-{{stream_id}} stream-privacy filter-icon"  style="color: {{stream_privacy_icon_color}}">
                 {{> stream_privacy}}
             </span>
@@ -18,11 +18,11 @@
         <span class="copy-paste-text">&gt;</span>
 
         {{! topic stuff }}
         <span class="stream_topic">
             {{! topic link }}
-            <a class="message_label_clickable narrows_by_topic tippy-zulip-delayed-tooltip"
+            <a class="message_label_clickable narrows_by_topic tippy-narrow-tooltip"
               href="{{topic_url}}"
               data-tippy-content="{{t 'Narrow to stream "{display_recipient}", topic "{topic}"' }}">
                 {{#if use_match_properties}}
                     {{{match_topic}}}
                 {{else}}
@@ -78,11 +78,11 @@
     </div>
 </div>
 {{else}}
 <div class="message_header message_header_private_message">
     <div class="message-header-contents">
-        <a class="message_label_clickable narrows_by_recipient stream_label tippy-zulip-delayed-tooltip"
+        <a class="message_label_clickable narrows_by_recipient stream_label tippy-narrow-tooltip"
           href="{{pm_with_url}}"
           data-tippy-content="{{t 'Narrow to your direct messages with {display_reply_to}' }}">
         <span class="private_message_header_icon"><i class="zulip-icon zulip-icon-user"></i></span>
         {{t "You and {display_reply_to}" }}
         </a>
diff --git a/web/templates/tooltip_templates.hbs b/web/templates/tooltip_templates.hbs
index 6d65c8a2a84..1299baad768 100644
--- a/web/templates/tooltip_templates.hbs
+++ b/web/templates/tooltip_templates.hbs
@@ -1,4 +1,7 @@
 <template id="view-user-card-tooltip-template">
     {{t 'View user card' }}
     {{tooltip_hotkey_hints "U"}}
 </template>
+<template id="narrow-hotkey-tooltip-template">
+    {{tooltip_hotkey_hints "S"}}
+</template>

Vulnerable code:         <a class="message_label_clickable narrows_by_recipient stream_label tippy-zulip-delayed-tooltip"
            <a class="message_label_clickable narrows_by_topic tippy-zulip-delayed-tooltip"
        <a class="message_label_clickable narrows_by_recipient stream_label tippy-zulip-delayed-tooltip"
Fixed code:     delegate("body", {
        target: ".tippy-narrow-tooltip",
        delay: LONG_HOVER_DELAY,
        appendTo: () => document.body,
        onCreate(instance) {
            const content = instance.props.content + $("#narrow-hotkey-tooltip-template").html();
            instance.setContent(parse_html(content));
        },
    });

        <a class="message_label_clickable narrows_by_recipient stream_label tippy-narrow-tooltip"
            <a class="message_label_clickable narrows_by_topic tippy-narrow-tooltip"
        <a class="message_label_clickable narrows_by_recipient stream_label tippy-narrow-tooltip"
<template id="narrow-hotkey-tooltip-template">
    {{tooltip_hotkey_hints "S"}}
</template>