CVE-2021-25986: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: In Django-wiki, versions 0.0.20 to 0.7.8 are vulnerable to Stored Cross-Site Scripting (XSS) in Notifications Section. An attacker who has access to edit pages can inject JavaScript payload in the title field. When a victim gets a notification regarding the changes made in the application, the payload in the notification panel renders and loads external JavaScript.
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: 5.4
Published: 2021-11-23T20:15Z
Repository: https://github.com/django-wiki/django-wiki
Language: Other
Files changed: docs/release_notes.rst, src/wiki/plugins/notifications/static/wiki/plugins/notifications/js/ui.js
Commit date: 2021-11-15 13:37:40 +0100
Commit message: Security fix (XSS) - Build HTML elements for notifications safely

Diff: diff --git a/docs/release_notes.rst b/docs/release_notes.rst
index abeaf284..cb4018a8 100644
--- a/docs/release_notes.rst
+++ b/docs/release_notes.rst
@@ -11,10 +11,19 @@ Release plan
 * **0.5.x** Remove Django 1.11 support, adds Django 2.2 and 3.x support. Python 3.5+.
 * **0.6.x** Targets Bootstrap v4, if you are interested in this work, please get in touch on Github!
 * **0.7.x** Removes Django 2.1 support, adds Django 3.1, 3.2
 
 
+0.7.9
+-----
+
+Security fixes
+~~~~~~~~~~~~~~
+
+* XSS vulnerability: Unescaped HTML in title propagated to notification (WhiteSource Vulnerability Research Team)
+
+
 0.7.8
 -----
 
 Released on 2021-10-25.
 
diff --git a/src/wiki/plugins/notifications/static/wiki/plugins/notifications/js/ui.js b/src/wiki/plugins/notifications/static/wiki/plugins/notifications/js/ui.js
index dfc3c96d..54df789e 100644
--- a/src/wiki/plugins/notifications/static/wiki/plugins/notifications/js/ui.js
+++ b/src/wiki/plugins/notifications/static/wiki/plugins/notifications/js/ui.js
@@ -17,15 +17,23 @@ function notify_update() {
       }
       for (var i=data.objects.length-1; i >=0 ; i--) {
         n = data.objects[i];
         notify_latest_id = n.pk>notify_latest_id ? n.pk:notify_latest_id;
         notify_oldest_id = (n.pk<notify_oldest_id || notify_oldest_id==0) ? n.pk:notify_oldest_id;
+        var element_outer_div = $("<div />");
+        var element_a = $("<a />", {href: URL_NOTIFY_GOTO + n.pk + "/"});
+        var element_message_div;
+        var element_since_div;
         if (n.occurrences > 1) {
-          element = $('<div><a href="'+URL_NOTIFY_GOTO+n.pk+'/"><div>'+n.message+'</div><div class="since">'+n.occurrences_msg+' - ' + n.since + '</div></a></div>')
+          element_message_div = $("<div />", {text: n.message});
+          element_since_div = $("<div />", {text: n.occurrences_msg+' - ' + n.since, class: "since"});
         } else {
-          element = $('<div><a href="'+URL_NOTIFY_GOTO+n.pk+'/"><div>'+n.message+'</div><div class="since">'+n.since+'</div></a></div>');
+          element_message_div = $("<div />", {text: n.message});
+          element_since_div = $("<div />", {text: n.since, class: "since"});
         }
+        element_a.append(element_message_div).append(element_since_div);
+        element = element_outer_div.append(element_a);
         element.addClass('dropdown-item notification-item');
         element.insertAfter('.notification-before-list');
       }
     }
   });

Vulnerable code:           element = $('<div><a href="'+URL_NOTIFY_GOTO+n.pk+'/"><div>'+n.message+'</div><div class="since">'+n.occurrences_msg+' - ' + n.since + '</div></a></div>')
          element = $('<div><a href="'+URL_NOTIFY_GOTO+n.pk+'/"><div>'+n.message+'</div><div class="since">'+n.since+'</div></a></div>');
Fixed code: 0.7.9
-----

Security fixes
~~~~~~~~~~~~~~

* XSS vulnerability: Unescaped HTML in title propagated to notification (WhiteSource Vulnerability Research Team)


        var element_outer_div = $("<div />");
        var element_a = $("<a />", {href: URL_NOTIFY_GOTO + n.pk + "/"});
        var element_message_div;
        var element_since_div;
          element_message_div = $("<div />", {text: n.message});
          element_since_div = $("<div />", {text: n.occurrences_msg+' - ' + n.since, class: "since"});
          element_message_div = $("<div />", {text: n.message});
          element_since_div = $("<div />", {text: n.since, class: "since"});
        element_a.append(element_message_div).append(element_since_div);
        element = element_outer_div.append(element_a);