CVE-2023-48715: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: Tuleap is an open source suite to improve management of software developments and collaboration. Prior to version 15.2.99.103 of Tuleap Community Edition and prior to versions 15.2-4 and 15.1-8 of Tuleap Enterprise Edition, the name of the releases are not properly escaped on the edition page of a release. A malicious user with the ability to create a FRS release could force a victim having write permissions in the FRS to execute uncontrolled code. Tuleap Community Edition 15.2.99.103, Tuleap Enterprise Edition 15.2-4, and Tuleap Enterprise Edition 15.1-8 contain a fix for this issue.
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: 2023-12-11T19:15Z
Repository: https://github.com/Enalean/tuleap
Language: PHP
Files changed: src/www/file/file_utils.php
Commit date: 2023-11-23 12:02:10 +0100
Commit message: Fixes request #35143: XSS on the edition page of a release

Change-Id: I351c201117301e8a28ba56d8330c468d026d3050

Diff: diff --git a/src/www/file/file_utils.php b/src/www/file/file_utils.php
index 7e52811a0411..6b96ba8fb136 100644
--- a/src/www/file/file_utils.php
+++ b/src/www/file/file_utils.php
@@ -124,33 +124,10 @@ function frs_show_processor_popup($group_id, $name = 'processor_id', $checked_va
 
     pop-up box of packages:releases for this group
 
 */
 
-
-function frs_show_release_popup($group_id, $name = 'release_id', $checked_val = "xzxz")
-{
-    /*
-        return a pop-up select box of releases for the project
-    */
-    global $FRS_RELEASE_ID_RES,$FRS_RELEASE_NAME_RES,$Language;
-    $release_factory = new FRSReleaseFactory();
-    if (! $group_id) {
-        return $Language->getText('file_file_utils', 'g_id_err');
-    } else {
-        if (! isset($FRS_RELEASE_ID_RES)) {
-            $res                  = $release_factory->getFRSReleasesInfoListFromDb($group_id);
-            $FRS_RELEASE_ID_RES   = [];
-            $FRS_RELEASE_NAME_RES = [];
-            foreach ($res as $release) {
-                $FRS_RELEASE_ID_RES[]   = $release['release_id'];
-                $FRS_RELEASE_NAME_RES[] = $release['package_name'] . ':' . $release['release_name'];
-            }
-        }
-        return html_build_select_box_from_arrays($FRS_RELEASE_ID_RES, $FRS_RELEASE_NAME_RES, $name, $checked_val, false);
-    }
-}
 function frs_show_release_popup2($group_id, $name = 'release_id', $checked_val = "xzxz")
 {
     /*
         return a pop-up select box of releases for the project
     */
@@ -163,15 +140,15 @@ function frs_show_release_popup2($group_id, $name = 'release_id', $checked_val =
         $p   = [];
         foreach ($res as $release) {
             $p[$release['package_name']][$release['release_id']] = $release['release_name'];
         }
 
-        $select = '<select name="' . $name . '">';
+        $select = '<select name="' . $hp->purify($name) . '">';
         foreach ($p as $package_name => $releases) {
-            $select .= '<optgroup label="' . $package_name . '">';
+            $select .= '<optgroup label="' . $hp->purify($package_name) . '">';
             foreach ($releases as $id => $name) {
-                $select .= '<option value="' . $id . '" ' . ($id == $checked_val ? 'selected="selected"' : '') . '>' . $hp->purify($name) . '</option>';
+                $select .= '<option value="' . $hp->purify($id) . '" ' . ($id == $checked_val ? 'selected="selected"' : '') . '>' . $hp->purify($name) . '</option>';
             }
             $select .= '</optgroup>';
         }
         $select .= '</select>';
         return $select;

Vulnerable code: 
function frs_show_release_popup($group_id, $name = 'release_id', $checked_val = "xzxz")
{
    /*
        return a pop-up select box of releases for the project
    */
    global $FRS_RELEASE_ID_RES,$FRS_RELEASE_NAME_RES,$Language;
    $release_factory = new FRSReleaseFactory();
    if (! $group_id) {
        return $Language->getText('file_file_utils', 'g_id_err');
    } else {
        if (! isset($FRS_RELEASE_ID_RES)) {
            $res                  = $release_factory->getFRSReleasesInfoListFromDb($group_id);
            $FRS_RELEASE_ID_RES   = [];
            $FRS_RELEASE_NAME_RES = [];
            foreach ($res as $release) {
                $FRS_RELEASE_ID_RES[]   = $release['release_id'];
                $FRS_RELEASE_NAME_RES[] = $release['package_name'] . ':' . $release['release_name'];
            }
        }
        return html_build_select_box_from_arrays($FRS_RELEASE_ID_RES, $FRS_RELEASE_NAME_RES, $name, $checked_val, false);
    }
}
        $select = '<select name="' . $name . '">';
            $select .= '<optgroup label="' . $package_name . '">';
                $select .= '<option value="' . $id . '" ' . ($id == $checked_val ? 'selected="selected"' : '') . '>' . $hp->purify($name) . '</option>';
Fixed code:         $select = '<select name="' . $hp->purify($name) . '">';
            $select .= '<optgroup label="' . $hp->purify($package_name) . '">';
                $select .= '<option value="' . $hp->purify($id) . '" ' . ($id == $checked_val ? 'selected="selected"' : '') . '>' . $hp->purify($name) . '</option>';