CVE-2022-31187: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: GLPI stands for Gestionnaire Libre de Parc Informatique and is a Free Asset and IT Management Software package, that provides ITIL Service Desk features, licenses tracking and software auditing. Affected versions were found to not properly neutralize HTML tags in the global search context. Users are advised to upgrade to version 10.0.3 to resolve this issue. Users unable to upgrade should disable global search.
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: 2022-09-14T18:15Z
Repository: https://github.com/glpi-project/glpi
Language: PHP
Files changed: src/Search.php
Commit date: 2022-09-14 13:53:12 +0200
Commit message: Fix handling of global search output

Diff: diff --git a/src/Search.php b/src/Search.php
index f0be357816c9..db1a79ae568f 100644
--- a/src/Search.php
+++ b/src/Search.php
@@ -6305,10 +6305,18 @@ JAVASCRIPT;
             if (!empty($out)) {
                 return $out;
             }
         }
 
+        $html_output = in_array(
+            self::$output_type,
+            [
+                self::HTML_OUTPUT,
+                self::GLOBAL_SEARCH, // For a global search, output will be done in HTML context
+            ]
+        );
+
         if (isset($so["table"])) {
             $table     = $so["table"];
             $field     = $so["field"];
             $linkfield = $so["linkfield"];
 
@@ -6986,11 +6994,11 @@ JAVASCRIPT;
                 case 'glpi_cables.color':
                    //do not display 'real' value (#.....)
                     return "";
 
                 case 'glpi_ticketsatisfactions.satisfaction':
-                    if (self::$output_type == self::HTML_OUTPUT) {
+                    if ($html_output) {
                         return TicketSatisfaction::displaySatisfaction($data[$ID][0]['name']);
                     }
                     break;
 
                 case 'glpi_projects._virtual_planned_duration':
@@ -7007,24 +7015,24 @@ JAVASCRIPT;
 
                 case 'glpi_cartridgeitems._virtual':
                     return Cartridge::getCount(
                         $data["id"],
                         $data[$ID][0]['alarm_threshold'],
-                        self::$output_type != self::HTML_OUTPUT
+                        !$html_output
                     );
 
                 case 'glpi_printers._virtual':
                     return Cartridge::getCountForPrinter(
                         $data["id"],
-                        self::$output_type != self::HTML_OUTPUT
+                        !$html_output
                     );
 
                 case 'glpi_consumableitems._virtual':
                     return Consumable::getCount(
                         $data["id"],
                         $data[$ID][0]['alarm_threshold'],
-                        self::$output_type != self::HTML_OUTPUT
+                        !$html_output
                     );
 
                 case 'glpi_links._virtual':
                      $out = '';
                      $link = new Link();
@@ -7162,14 +7170,13 @@ JAVASCRIPT;
                             if ($count_display) {
                                 $out .= $separate;
                             }
                             $count_display++;
 
+                            $plaintext = RichText::getTextFromHtml($data[$ID][$k]['name'], false, true, $html_output);
 
-                            $plaintext = RichText::getTextFromHtml($data[$ID][$k]['name'], false, true, self::$output_type == self::HTML_OUTPUT);
-
-                            if (self::$output_type == self::HTML_OUTPUT && (Toolbox::strlen($plaintext) > $CFG_GLPI['cut'])) {
+                            if ($html_output && (Toolbox::strlen($plaintext) > $CFG_GLPI['cut'])) {
                                 $rand = mt_rand();
                                 $popup_params = [
                                     'display'       => false,
                                     'awesome-class' => 'fa-comments',
                                     'autoclose'     => false,

Vulnerable code:                     if (self::$output_type == self::HTML_OUTPUT) {
                        self::$output_type != self::HTML_OUTPUT
                        self::$output_type != self::HTML_OUTPUT
                        self::$output_type != self::HTML_OUTPUT
                            $plaintext = RichText::getTextFromHtml($data[$ID][$k]['name'], false, true, self::$output_type == self::HTML_OUTPUT);

                            if (self::$output_type == self::HTML_OUTPUT && (Toolbox::strlen($plaintext) > $CFG_GLPI['cut'])) {
Fixed code:         $html_output = in_array(
            self::$output_type,
            [
                self::HTML_OUTPUT,
                self::GLOBAL_SEARCH, // For a global search, output will be done in HTML context
            ]
        );

                    if ($html_output) {
                        !$html_output
                        !$html_output
                        !$html_output
                            $plaintext = RichText::getTextFromHtml($data[$ID][$k]['name'], false, true, $html_output);
                            if ($html_output && (Toolbox::strlen($plaintext) > $CFG_GLPI['cut'])) {