CVE-2016-15010: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: ** UNSUPPORTED WHEN ASSIGNED ** A vulnerability classified as problematic was found in University of Cambridge django-ucamlookup up to 1.9.1. Affected by this vulnerability is an unknown functionality of the component Lookup Handler. The manipulation leads to cross site scripting. The attack can be launched remotely. Upgrading to version 1.9.2 is able to address this issue. The identifier of the patch is 5e25e4765637ea4b9e0bf5fcd5e9a922abee7eb3. It is recommended to upgrade the affected component. The identifier VDB-217441 was assigned to this vulnerability. NOTE: This vulnerability only affects products that are no longer supported by the maintainer.
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-01-05T09:15Z
Repository: https://github.com/uisautomation/django-ucamlookup
Language: HTML
Files changed: ucamlookup/templates/admin/auth/lookup_group/add_form.html, ucamlookup/templates/admin/auth/lookup_user/add_form.html, ucamlookup/templates/ucamlookup_groups.html, ucamlookup/templates/ucamlookup_users.html
Commit date: 2016-09-14 15:54:06 +0100
Commit message: Fix lack of escaping (and so XSS vuln.) in select2 calls

The invocation of Jquery select2 to provide searchable dropdowns
didn't sanitise data coming fom lookup, with the result that any
HTML markup it contained, including <script>...</script>, was
interpreted.

The documentation is difficult to follow, but indications are that the
formater functions (at least formatResult and formatSelection), if
overriden have to do their own escaping of data as necessary. They are
however passed the current global 'escapeMarkup' function as their
final parameter.

Diff: diff --git a/ucamlookup/templates/admin/auth/lookup_group/add_form.html b/ucamlookup/templates/admin/auth/lookup_group/add_form.html
index 3db4956..ab9c13e 100644
--- a/ucamlookup/templates/admin/auth/lookup_group/add_form.html
+++ b/ucamlookup/templates/admin/auth/lookup_group/add_form.html
@@ -45,22 +45,20 @@
                     if (data.searchId_g != searchId_g)
                         return;
                     return {results: data.groups};
                 }
             },
-            formatResult: function(group) {
-                return group.title;
+            formatResult: function(group, container, query, escapeMarkup) {
+                return escapeMarkup(group.title);
             },
-            formatSelection: function(group) {
-                return group.title;
+            formatSelection: function(group, container, escapeMarkup) {
+                return escapeMarkup(group.title);
             },
             id: function(group) {
                 return group.groupid;
             },
             dropdownCssClass: "bigdrop", // apply css that makes the dropdown taller
-            escapeMarkup: function (m) { return m; },
-            // we do not want to escape markup since we are displaying html in results
             width: '100%'
         });
     });
 </script>
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/ucamlookup/templates/admin/auth/lookup_user/add_form.html b/ucamlookup/templates/admin/auth/lookup_user/add_form.html
index c4081e4..26d7576 100644
--- a/ucamlookup/templates/admin/auth/lookup_user/add_form.html
+++ b/ucamlookup/templates/admin/auth/lookup_user/add_form.html
@@ -44,22 +44,20 @@
                     if (data.searchId_u != searchId_u)
                         return;
                     return {results: data.persons};
                 }
             },
-            formatResult: function(person) {
-                return person.visibleName+" ("+person.crsid+") ";
+            formatResult: function(person, container, query, escapeMarkup) {
+                return escapeMarkup(person.visibleName+" ("+person.crsid+") ");
             },
-            formatSelection: function(person) {
-                return person.visibleName+" ("+person.crsid+") ";
+            formatSelection: function(person, container, escapeMarkup) {
+                return escapeMarkup(person.visibleName+" ("+person.crsid+") ");
             },
             id: function(person) {
                 return person.crsid;
             },
             dropdownCssClass: "bigdrop", // apply css that makes the dropdown taller
-            escapeMarkup: function (m) { return m; },
-            // we do not want to escape markup since we are displaying html in results
             width: '50%'
         });
     });
 </script>
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/ucamlookup/templates/ucamlookup_groups.html b/ucamlookup/templates/ucamlookup_groups.html
index 6d95f26..5b7dc92 100644
--- a/ucamlookup/templates/ucamlookup_groups.html
+++ b/ucamlookup/templates/ucamlookup_groups.html
@@ -23,22 +23,20 @@
                     if (data.searchId_g != searchId_g)
                         return;
                     return {results: data.groups};
                 }
             },
-            formatResult: function(group) {
-                return group.title;
+            formatResult: function(group, container, query, escapeMarkup) {
+                return escapeMarkup(group.title);
             },
-            formatSelection: function(group) {
-                return group.title;
+            formatSelection: function(group, container, escapeMarkup) {
+                return escapeMarkup(group.title);
             },
             id: function(group) {
                 return group.groupid;
             },
             dropdownCssClass: "bigdrop", // apply css that makes the dropdown taller
-            escapeMarkup: function (m) { return m; }
-            // we do not want to escape markup since we are displaying html in results
         });
 
         $("#{{ input_tag_id }}").select2("data", [
             {% for authorised_group in lookup_lists|get_item:group_list %}
                 {
@@ -46,6 +44,6 @@
                     title: "{{ authorised_group.name }}"
                 },
             {% endfor %}
         ]);
     });
-</script>
\ No newline at end of file
+</script>
diff --git a/ucamlookup/templates/ucamlookup_users.html b/ucamlookup/templates/ucamlookup_users.html
index 94317ce..6c8b44e 100644
--- a/ucamlookup/templates/ucamlookup_users.html
+++ b/ucamlookup/templates/ucamlookup_users.html
@@ -23,22 +23,20 @@
                     if (data.searchId_u != searchId_u)
                         return;
                     return {results: data.persons};
                 }
             },
-            formatResult: function(person) {
-                return person.visibleName+" ("+person.crsid+") ";
+            formatResult: function(person, container, wuery, escapeMarkup) {
+                return escapeMarkup(person.visibleName+" ("+person.crsid+") ");
             },
-            formatSelection: function(person) {
-                return person.visibleName+" ("+person.crsid+") ";
+            formatSelection: function(person, container, escapeMarkup) {
+                return escapeMarkup(person.visibleName+" ("+person.crsid+") ");
             },
             id: function(person) {
                 return person.crsid;
             },
             dropdownCssClass: "bigdrop", // apply css that makes the dropdown taller
-            escapeMarkup: function (m) { return m; }
-            // we do not want to escape markup since we are displaying html in results
         });
 
         $("#{{ input_tag_id }}").select2("data", [
             {% for authorised_user in lookup_lists|get_item:user_list %}
                 {
@@ -49,6 +47,6 @@
                     {% endif %}
                 },
             {% endfor %}
         ]);
     });
-</script>
\ No newline at end of file
+</script>

Vulnerable code:             formatResult: function(group) {
                return group.title;
            formatSelection: function(group) {
                return group.title;
            escapeMarkup: function (m) { return m; },
            // we do not want to escape markup since we are displaying html in results
{% endblock %}
            formatResult: function(person) {
                return person.visibleName+" ("+person.crsid+") ";
            formatSelection: function(person) {
                return person.visibleName+" ("+person.crsid+") ";
            escapeMarkup: function (m) { return m; },
            // we do not want to escape markup since we are displaying html in results
{% endblock %}
            formatResult: function(group) {
                return group.title;
            formatSelection: function(group) {
                return group.title;
            escapeMarkup: function (m) { return m; }
            // we do not want to escape markup since we are displaying html in results
</script>
            formatResult: function(person) {
                return person.visibleName+" ("+person.crsid+") ";
            formatSelection: function(person) {
                return person.visibleName+" ("+person.crsid+") ";
            escapeMarkup: function (m) { return m; }
            // we do not want to escape markup since we are displaying html in results
</script>
Fixed code:             formatResult: function(group, container, query, escapeMarkup) {
                return escapeMarkup(group.title);
            formatSelection: function(group, container, escapeMarkup) {
                return escapeMarkup(group.title);
{% endblock %}
            formatResult: function(person, container, query, escapeMarkup) {
                return escapeMarkup(person.visibleName+" ("+person.crsid+") ");
            formatSelection: function(person, container, escapeMarkup) {
                return escapeMarkup(person.visibleName+" ("+person.crsid+") ");
{% endblock %}
            formatResult: function(group, container, query, escapeMarkup) {
                return escapeMarkup(group.title);
            formatSelection: function(group, container, escapeMarkup) {
                return escapeMarkup(group.title);
</script>
            formatResult: function(person, container, wuery, escapeMarkup) {
                return escapeMarkup(person.visibleName+" ("+person.crsid+") ");
            formatSelection: function(person, container, escapeMarkup) {
                return escapeMarkup(person.visibleName+" ("+person.crsid+") ");
</script>