CVE-2023-0308: 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.10.
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-01-15T22:15Z
Repository: https://github.com/thorsten/phpmyfaq
Language: PHP
Files changed: phpmyfaq/admin/ajax.user.php, phpmyfaq/ajaxservice.php, phpmyfaq/index.php, phpmyfaq/ucp.php
Commit date: 2022-12-19 20:31:35 +0100
Commit message: fix: added missing conversion to HTML entities

Diff: diff --git a/phpmyfaq/admin/ajax.user.php b/phpmyfaq/admin/ajax.user.php
index 639b1449ae..13f2bdf19b 100644
--- a/phpmyfaq/admin/ajax.user.php
+++ b/phpmyfaq/admin/ajax.user.php
@@ -20,10 +20,11 @@ use phpMyFAQ\Category;
 use phpMyFAQ\Core\Exception;
 use phpMyFAQ\Filter;
 use phpMyFAQ\Helper\HttpHelper;
 use phpMyFAQ\Helper\MailHelper;
 use phpMyFAQ\Permission;
+use phpMyFAQ\Strings;
 use phpMyFAQ\User;
 
 if (!defined('IS_VALID_PHPMYFAQ')) {
     http_response_code(400);
     exit();
@@ -61,11 +62,11 @@ if (
         case 'get_user_data':
             $user->getUserById($userId, true);
             $userdata = [];
             $userdata = $user->userdata->get('*');
             $userdata['status'] = $user->getStatus();
-            $userdata['login'] = $user->getLogin();
+            $userdata['login'] = Strings::htmlentities($user->getLogin());
             $userdata['is_superadmin'] = $user->isSuperAdmin();
             $http->sendJsonWithHeaders($userdata);
             break;
 
         case 'get_all_user_data':
diff --git a/phpmyfaq/ajaxservice.php b/phpmyfaq/ajaxservice.php
index 2d0044b8fe..4ad6d05a8a 100644
--- a/phpmyfaq/ajaxservice.php
+++ b/phpmyfaq/ajaxservice.php
@@ -756,11 +756,11 @@ switch ($action) {
             $message = ['error' => $PMF_LANG['ad_msg_noauth']];
             break;
         }
 
         $userId = Filter::filterInput(INPUT_POST, 'userid', FILTER_VALIDATE_INT);
-        $userName = Filter::filterInput(INPUT_POST, 'name', FILTER_UNSAFE_RAW);
+        $userName = Filter::filterInput(INPUT_POST, 'name', FILTER_SANITIZE_SPECIAL_CHARS);
         $email = Filter::filterInput(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
         $isVisible = Filter::filterInput(INPUT_POST, 'is_visible', FILTER_UNSAFE_RAW);
         $password = Filter::filterInput(INPUT_POST, 'password', FILTER_UNSAFE_RAW);
         $confirm = Filter::filterInput(INPUT_POST, 'password_confirm', FILTER_UNSAFE_RAW);
 
diff --git a/phpmyfaq/index.php b/phpmyfaq/index.php
index 36268534aa..4e3c30c2f7 100755
--- a/phpmyfaq/index.php
+++ b/phpmyfaq/index.php
@@ -563,11 +563,11 @@ $tplMainPage = [
         $faqConfig->getVersion(),
     'registerUser' => $faqConfig->get('security.enableRegistration') ? '<a href="?action=register">' .
         $PMF_LANG['msgRegistration'] . '</a>' : '',
     'sendPassword' => '<a href="?action=password">' . $PMF_LANG['lostPassword'] . '</a>',
     'msgFullName' => $PMF_LANG['ad_user_loggedin'] . $user->getLogin(),
-    'msgLoginName' => $user->getUserData('display_name'),
+    'msgLoginName' => Strings::htmlentities($user->getUserData('display_name')),
     'loginHeader' => $PMF_LANG['msgLoginUser'],
     'loginMessage' => $loginMessage,
     'writeLoginPath' => Strings::htmlentities($faqSystem->getSystemUri($faqConfig)) . '?' . Filter::getFilteredQueryString(),
     'faqloginaction' => $action,
     'login' => $PMF_LANG['ad_auth_ok'],
diff --git a/phpmyfaq/ucp.php b/phpmyfaq/ucp.php
index b4e894c75b..d8ba5547ba 100644
--- a/phpmyfaq/ucp.php
+++ b/phpmyfaq/ucp.php
@@ -14,10 +14,11 @@
  * @link https://www.phpmyfaq.de
  * @since 2012-01-12
  */
 
 use phpMyFAQ\Services\Gravatar;
+use phpMyFAQ\Strings;
 
 if (!defined('IS_VALID_PHPMYFAQ')) {
     http_response_code(400);
     exit();
 }
@@ -49,11 +50,11 @@ if ($user->isLoggedIn()) {
             'ucpGravatarImage' => $gravatarImg,
             'userid' => $user->getUserId(),
             'csrf' => $user->getCsrfTokenFromSession(),
             'readonly' => $faqConfig->isLdapActive() ? 'readonly' : '',
             'msgRealName' => $PMF_LANG['ad_user_name'],
-            'realname' => $user->getUserData('display_name'),
+            'realname' => Strings::htmlentities($user->getUserData('display_name')),
             'msgEmail' => $PMF_LANG['msgNewContentMail'],
             'email' => $user->getUserData('email'),
             'msgIsVisible' => $PMF_LANG['ad_user_data_is_visible'],
             'checked' => (int)$user->getUserData('is_visible') === 1 ? 'checked' : '',
             'msgPassword' => $PMF_LANG['ad_auth_passwd'],

Vulnerable code:             $userdata['login'] = $user->getLogin();
        $userName = Filter::filterInput(INPUT_POST, 'name', FILTER_UNSAFE_RAW);
    'msgLoginName' => $user->getUserData('display_name'),
            'realname' => $user->getUserData('display_name'),
Fixed code: use phpMyFAQ\Strings;
            $userdata['login'] = Strings::htmlentities($user->getLogin());
        $userName = Filter::filterInput(INPUT_POST, 'name', FILTER_SANITIZE_SPECIAL_CHARS);
    'msgLoginName' => Strings::htmlentities($user->getUserData('display_name')),
use phpMyFAQ\Strings;
            'realname' => Strings::htmlentities($user->getUserData('display_name')),