CVE-2023-1757: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: Cross-site Scripting (XSS) - Stored in GitHub repository thorsten/phpmyfaq prior to 3.1.12.
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-04-05T16:15Z
Repository: https://github.com/thorsten/phpmyfaq
Language: PHP
Files changed: phpmyfaq/admin/news.php, phpmyfaq/src/phpMyFAQ/News.php
Commit date: 2023-02-14 19:03:58 +0100
Commit message: fix: added missing validation of URLs and conversion to HTML entities

Diff: diff --git a/phpmyfaq/admin/news.php b/phpmyfaq/admin/news.php
index 3cae41841c..0c76cc8e86 100644
--- a/phpmyfaq/admin/news.php
+++ b/phpmyfaq/admin/news.php
@@ -45,11 +45,12 @@ if ('add-news' == $action && $user->perm->hasPermission($user->getUserId(), 'add
           </h1>
         </div>
 
         <div class="row">
             <div class="col-12">
-                <form id="faqEditor" name="faqEditor" action="?action=save-news" method="post" novalidate>
+                <form id="faqEditor" name="faqEditor" action="?action=save-news" method="post" class="needs-validation"
+                      novalidate>
 
                     <div class="form-group row">
                         <label class="col-3 col-form-label" for="newsheader">
                             <?= $PMF_LANG['ad_news_header'] ?>
                         </label>
@@ -110,11 +111,11 @@ if ('add-news' == $action && $user->perm->hasPermission($user->getUserId(), 'add
                     </div>
 
                     <div class="form-group row">
                         <label class="col-3 col-form-label" for="link"><?= $PMF_LANG['ad_news_link_url'] ?></label>
                         <div class="col-9">
-                            <input class="form-control" type="text" name="link" id="link"
+                            <input class="form-control" type="url" name="link" id="link"
                             placeholder="http://www.example.com/">
                         </div>
                     </div>
 
                     <div class="form-group row">
@@ -254,11 +255,12 @@ if ('add-news' == $action && $user->perm->hasPermission($user->getUserId(), 'add
           </h1>
         </div>
 
         <div class="row">
             <div class="col-12">
-                <form  action="?action=update-news" method="post" accept-charset="utf-8">
+                <form action="?action=update-news" method="post" accept-charset="utf-8" class="needs-validation"
+                      novalidate>
                     <input type="hidden" name="id" value="<?= $newsData['id'] ?>">
 
                     <div class="form-group row">
                         <label class="col-3 col-form-label" for="newsheader"><?= $PMF_LANG['ad_news_header'] ?></label>
                         <div class="col-9">
@@ -326,11 +328,11 @@ if ('add-news' == $action && $user->perm->hasPermission($user->getUserId(), 'add
                     </div>
 
                     <div class="form-group row">
                         <label class="col-3 col-form-label" for="link"><?= $PMF_LANG['ad_news_link_url'] ?></label>
                         <div class="col-9">
-                            <input type="text" id="link" name="link"
+                            <input type="url" id="link" name="link"
                             value="<?= Strings::htmlentities($newsData['link']) ?>" class="form-control">
                         </div>
                     </div>
 
                     <div class="form-group row">
@@ -447,12 +449,12 @@ if ('add-news' == $action && $user->perm->hasPermission($user->getUserId(), 'add
     $content = Filter::filterInput(INPUT_POST, 'news', FILTER_SANITIZE_SPECIAL_CHARS);
     $author = Filter::filterInput(INPUT_POST, 'authorName', FILTER_UNSAFE_RAW);
     $email = Filter::filterInput(INPUT_POST, 'authorEmail', FILTER_VALIDATE_EMAIL);
     $active = Filter::filterInput(INPUT_POST, 'active', FILTER_UNSAFE_RAW);
     $comment = Filter::filterInput(INPUT_POST, 'comment', FILTER_UNSAFE_RAW);
-    $link = Filter::filterInput(INPUT_POST, 'link', FILTER_SANITIZE_SPECIAL_CHARS);
-    $linkTitle = Filter::filterInput(INPUT_POST, 'linkTitle', FILTER_SANITIZE_SPECIAL_CHARS);
+    $link = Filter::filterInput(INPUT_POST, 'link', FILTER_VALIDATE_URL);
+    $linkTitle = Filter::filterInput(INPUT_POST, 'linkTitle', FILTER_SANITIZE_ENCODED);
     $newsLang = Filter::filterInput(INPUT_POST, 'langTo', FILTER_UNSAFE_RAW);
     $target = Filter::filterInput(INPUT_POST, 'target', FILTER_UNSAFE_RAW);
 
     $newsData = array(
         'lang' => $newsLang,
@@ -498,12 +500,12 @@ if ('add-news' == $action && $user->perm->hasPermission($user->getUserId(), 'add
     $content = Filter::filterInput(INPUT_POST, 'news', FILTER_SANITIZE_SPECIAL_CHARS);
     $author = Filter::filterInput(INPUT_POST, 'authorName', FILTER_UNSAFE_RAW);
     $email = Filter::filterInput(INPUT_POST, 'authorEmail', FILTER_VALIDATE_EMAIL);
     $active = Filter::filterInput(INPUT_POST, 'active', FILTER_UNSAFE_RAW);
     $comment = Filter::filterInput(INPUT_POST, 'comment', FILTER_UNSAFE_RAW);
-    $link = Filter::filterInput(INPUT_POST, 'link', FILTER_UNSAFE_RAW);
-    $linkTitle = Filter::filterInput(INPUT_POST, 'linkTitle', FILTER_UNSAFE_RAW);
+    $link = Filter::filterInput(INPUT_POST, 'link', FILTER_VALIDATE_URL);
+    $linkTitle = Filter::filterInput(INPUT_POST, 'linkTitle', FILTER_SANITIZE_ENCODED);
     $newsLang = Filter::filterInput(INPUT_POST, 'langTo', FILTER_UNSAFE_RAW);
     $target = Filter::filterInput(INPUT_POST, 'target', FILTER_UNSAFE_RAW);
 
     $newsData = [
         'lang' => $newsLang,
diff --git a/phpmyfaq/src/phpMyFAQ/News.php b/phpmyfaq/src/phpMyFAQ/News.php
index b90cc1e825..e40643562d 100644
--- a/phpmyfaq/src/phpMyFAQ/News.php
+++ b/phpmyfaq/src/phpMyFAQ/News.php
@@ -93,11 +93,11 @@ class News
                 $output .= sprintf(
                     '<br>%s <a href="%s" target="_%s">%s</a>',
                     $this->pmfLang['msgInfo'],
                     Strings::htmlentities($item['link']),
                     $item['target'],
-                    $item['linkTitle']
+                    Strings::htmlentities($item['linkTitle'])
                 );
             }
 
             $output .= sprintf('<small class="text-muted">%s</small>', $date->format($item['date']));
         }

Vulnerable code:                 <form id="faqEditor" name="faqEditor" action="?action=save-news" method="post" novalidate>
                            <input class="form-control" type="text" name="link" id="link"
                <form  action="?action=update-news" method="post" accept-charset="utf-8">
                            <input type="text" id="link" name="link"
    $link = Filter::filterInput(INPUT_POST, 'link', FILTER_SANITIZE_SPECIAL_CHARS);
    $linkTitle = Filter::filterInput(INPUT_POST, 'linkTitle', FILTER_SANITIZE_SPECIAL_CHARS);
    $link = Filter::filterInput(INPUT_POST, 'link', FILTER_UNSAFE_RAW);
    $linkTitle = Filter::filterInput(INPUT_POST, 'linkTitle', FILTER_UNSAFE_RAW);
                    $item['linkTitle']
Fixed code:                 <form id="faqEditor" name="faqEditor" action="?action=save-news" method="post" class="needs-validation"
                      novalidate>
                            <input class="form-control" type="url" name="link" id="link"
                <form action="?action=update-news" method="post" accept-charset="utf-8" class="needs-validation"
                      novalidate>
                            <input type="url" id="link" name="link"
    $link = Filter::filterInput(INPUT_POST, 'link', FILTER_VALIDATE_URL);
    $linkTitle = Filter::filterInput(INPUT_POST, 'linkTitle', FILTER_SANITIZE_ENCODED);
    $link = Filter::filterInput(INPUT_POST, 'link', FILTER_VALIDATE_URL);
    $linkTitle = Filter::filterInput(INPUT_POST, 'linkTitle', FILTER_SANITIZE_ENCODED);
                    Strings::htmlentities($item['linkTitle'])