Vulnerability: Multiple cross-site scripting (XSS) vulnerabilities in index.php in Shaarli allow remote attackers to inject arbitrary web script or HTML via the URL to the (1) showRSS, (2) showATOM, or (3) showDailyRSS function; a (4) file name to the importFile function; or (5) vectors related to bookmarks.
Vulnerability ID: CVE-2013-7351
CWE: CWE-79
Severity score: 2.9
Project: https://github.com/sebsauvage/Shaarli
Published: 2020-01-02
File: index.php
Commit message: XSS flaw correction

Closes issue https://github.com/sebsauvage/Shaarli/issues/134
Commit author: Sebastien SAUVAGE

Vulnerable version (before fix):
 function showRSS()         echo '<description><![CDATA['.nl2br(keepMultipleSpaces(text2clickable(htmlspecialchars($link['description'])))).$descriptionlink.']]></description>'."\n</item>\n";         $i++;     }     echo '</channel></rss><!-- Cached version of '.pageUrl().' -->';      $cache->cache(ob_get_contents());     ob_end_flush(); function showATOM()     $feed.='<author><name>'.htmlspecialchars($pageaddr).'</name><uri>'.htmlspecialchars($pageaddr).'</uri></author>';     $feed.='<id>'.htmlspecialchars($pageaddr).'</id>'."\n\n"; // Yes, I know I should use a real IRI (RFC3987), but the site URL will do.     $feed.=$entries;     $feed.='</feed><!-- Cached version of '.pageUrl().' -->';     echo $feed;      $cache->cache(ob_get_contents()); function showDailyRSS()         echo '<description><![CDATA['.$html.']]></description>'."\n</item>\n\n";      }     echo '</channel></rss><!-- Cached version of '.pageUrl().' -->';      $cache->cache(ob_get_contents());     ob_end_flush(); function importFile()         }         $LINKSDB->savedb();          echo '<script language="JavaScript">alert("File '.$filename.' ('.$filesize.' bytes) was successfully processed: '.$import_count.' links imported.");document.location=\'?\';</script>';     }     else     {         echo '<script language="JavaScript">alert("File '.$filename.' ('.$filesize.' bytes) has an unknown file format. Nothing was imported.");document.location=\'?\';</script>';     } } 

Fixed version (after fix):
 function showRSS()         echo '<description><![CDATA['.nl2br(keepMultipleSpaces(text2clickable(htmlspecialchars($link['description'])))).$descriptionlink.']]></description>'."\n</item>\n";         $i++;     }     echo '</channel></rss><!-- Cached version of '.htmlspecialchars(pageUrl()).' -->';      $cache->cache(ob_get_contents());     ob_end_flush(); function showATOM()     $feed.='<author><name>'.htmlspecialchars($pageaddr).'</name><uri>'.htmlspecialchars($pageaddr).'</uri></author>';     $feed.='<id>'.htmlspecialchars($pageaddr).'</id>'."\n\n"; // Yes, I know I should use a real IRI (RFC3987), but the site URL will do.     $feed.=$entries;     $feed.='</feed><!-- Cached version of '.htmlspecialchars(pageUrl()).' -->';     echo $feed;      $cache->cache(ob_get_contents()); function showDailyRSS()         echo '<description><![CDATA['.$html.']]></description>'."\n</item>\n\n";      }     echo '</channel></rss><!-- Cached version of '.htmlspecialchars(pageUrl()).' -->';      $cache->cache(ob_get_contents());     ob_end_flush(); function importFile()         }         $LINKSDB->savedb();          echo '<script language="JavaScript">alert("File '.json_encode($filename).' ('.$filesize.' bytes) was successfully processed: '.$import_count.' links imported.");document.location=\'?\';</script>';     }     else     {         echo '<script language="JavaScript">alert("File '.json_encode($filename).' ('.$filesize.' bytes) has an unknown file format. Nothing was imported.");document.location=\'?\';</script>';     } } 

Patch:
patch: @@ -942,7 +942,7 @@ function showRSS()
         echo '<description><![CDATA['.nl2br(keepMultipleSpaces(text2clickable(htmlspecialchars($link['description'])))).$descriptionlink.']]></description>'."\n</item>\n";
         $i++;
     }
-    echo '</channel></rss><!-- Cached version of '.pageUrl().' -->';
+    echo '</channel></rss><!-- Cached version of '.htmlspecialchars(pageUrl()).' -->';
 
     $cache->cache(ob_get_contents());
     ob_end_flush();
@@ -1027,7 +1027,7 @@ function showATOM()
     $feed.='<author><name>'.htmlspecialchars($pageaddr).'</name><uri>'.htmlspecialchars($pageaddr).'</uri></author>';
     $feed.='<id>'.htmlspecialchars($pageaddr).'</id>'."\n\n"; // Yes, I know I should use a real IRI (RFC3987), but the site URL will do.
     $feed.=$entries;
-    $feed.='</feed><!-- Cached version of '.pageUrl().' -->';
+    $feed.='</feed><!-- Cached version of '.htmlspecialchars(pageUrl()).' -->';
     echo $feed;
 
     $cache->cache(ob_get_contents());
@@ -1104,7 +1104,7 @@ function showDailyRSS()
         echo '<description><![CDATA['.$html.']]></description>'."\n</item>\n\n";
 
     }
-    echo '</channel></rss><!-- Cached version of '.pageUrl().' -->';
+    echo '</channel></rss><!-- Cached version of '.htmlspecialchars(pageUrl()).' -->';
 
     $cache->cache(ob_get_contents());
     ob_end_flush();
@@ -1747,11 +1747,11 @@ function importFile()
         }
         $LINKSDB->savedb();
 
-        echo '<script language="JavaScript">alert("File '.$filename.' ('.$filesize.' bytes) was successfully processed: '.$import_count.' links imported.");document.location=\'?\';</script>';
+        echo '<script language="JavaScript">alert("File '.json_encode($filename).' ('.$filesize.' bytes) was successfully processed: '.$import_count.' links imported.");document.location=\'?\';</script>';
     }
     else
     {
-        echo '<script language="JavaScript">alert("File '.$filename.' ('.$filesize.' bytes) has an unknown file format. Nothing was imported.");document.location=\'?\';</script>';
+        echo '<script language="JavaScript">alert("File '.json_encode($filename).' ('.$filesize.' bytes) has an unknown file format. Nothing was imported.");document.location=\'?\';</script>';
     }
 }
path: index.php