Vulnerability: Cross Site Scripting (XSS) exists in Piwigo before 2.8.3 via a crafted search expression to include/functions_search.inc.php.
Vulnerability ID: CVE-2016-10513
CWE: CWE-79
Severity score: 2.9
Project: https://github.com/Piwigo/Piwigo
Published: 2017-10-10
File: include/functions_search.inc.php
Commit message: fixes #548, escape HTML chars from search expression

before sending them to HTML comment on frontend
Commit author: plegall

Vulnerable version (before fix):
 function get_quick_search_results_no_cache($q, $options)    $ids = qsearch_eval($expression, $qsr, $tmp, $search_results['qs']['unmatched_terms']);    $debug[] = "<!--\nparsed: ".$expression;   $debug[] = count($expression->stokens).' tokens';   for ($i=0; $i<count($expression->stokens); $i++)   {     $debug[] = $expression->stokens[$i].': '.count($qsr->tag_ids[$i]).' tags, '.count($qsr->tag_iids[$i]).' tiids, '.count($qsr->images_iids[$i]).' iiids, '.count($qsr->iids[$i]).' iids'       .' modifier:'.dechex($expression->stoken_modifiers[$i])       .( !empty($expression->stokens[$i]->variants) ? ' variants: '.implode(', ',$expression->stokens[$i]->variants): '');   }   $debug[] = 'before perms '.count($ids); 

Fixed version (after fix):
 function get_quick_search_results_no_cache($q, $options)    $ids = qsearch_eval($expression, $qsr, $tmp, $search_results['qs']['unmatched_terms']);    $debug[] = "<!--\nparsed: ".htmlspecialchars($expression);   $debug[] = count($expression->stokens).' tokens';   for ($i=0; $i<count($expression->stokens); $i++)   {     $debug[] = htmlspecialchars($expression->stokens[$i]).': '.count($qsr->tag_ids[$i]).' tags, '.count($qsr->tag_iids[$i]).' tiids, '.count($qsr->images_iids[$i]).' iiids, '.count($qsr->iids[$i]).' iids'       .' modifier:'.dechex($expression->stoken_modifiers[$i])       .( !empty($expression->stokens[$i]->variants) ? ' variants: '.htmlspecialchars(implode(', ',$expression->stokens[$i]->variants)): '');   }   $debug[] = 'before perms '.count($ids); 

Patch:
patch: @@ -1284,13 +1284,13 @@ function get_quick_search_results_no_cache($q, $options)
 
   $ids = qsearch_eval($expression, $qsr, $tmp, $search_results['qs']['unmatched_terms']);
 
-  $debug[] = "<!--\nparsed: ".$expression;
+  $debug[] = "<!--\nparsed: ".htmlspecialchars($expression);
   $debug[] = count($expression->stokens).' tokens';
   for ($i=0; $i<count($expression->stokens); $i++)
   {
-    $debug[] = $expression->stokens[$i].': '.count($qsr->tag_ids[$i]).' tags, '.count($qsr->tag_iids[$i]).' tiids, '.count($qsr->images_iids[$i]).' iiids, '.count($qsr->iids[$i]).' iids'
+    $debug[] = htmlspecialchars($expression->stokens[$i]).': '.count($qsr->tag_ids[$i]).' tags, '.count($qsr->tag_iids[$i]).' tiids, '.count($qsr->images_iids[$i]).' iiids, '.count($qsr->iids[$i]).' iids'
       .' modifier:'.dechex($expression->stoken_modifiers[$i])
-      .( !empty($expression->stokens[$i]->variants) ? ' variants: '.implode(', ',$expression->stokens[$i]->variants): '');
+      .( !empty($expression->stokens[$i]->variants) ? ' variants: '.htmlspecialchars(implode(', ',$expression->stokens[$i]->variants)): '');
   }
   $debug[] = 'before perms '.count($ids);
path: include/functions_search.inc.php