CVE-2023-3469: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: Cross-site Scripting (XSS) - Reflected in GitHub repository thorsten/phpmyfaq prior to 3.2.0-beta.2.
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: 4.8
Published: 2023-06-30T01:15Z
Repository: https://github.com/thorsten/phpmyfaq
Language: PHP
Files changed: phpmyfaq/admin/backup.import.php, phpmyfaq/src/phpMyFAQ/Core/Error.php
Commit date: 2023-05-12 10:27:21 +0200
Commit message: fix: added missing conversion of HTML entities in error exception

Diff: diff --git a/phpmyfaq/admin/backup.import.php b/phpmyfaq/admin/backup.import.php
index 7d458660aae..3421e0d1d89 100644
--- a/phpmyfaq/admin/backup.import.php
+++ b/phpmyfaq/admin/backup.import.php
@@ -59,20 +59,21 @@ if ($user->perm->hasPermission($user->getUserId(), 'restore') && $csrfCheck) {
         }
 
         $handle = fopen($_FILES['userfile']['tmp_name'], 'r');
         $backupData = fgets($handle, 65536);
         $versionFound = Strings::substr($backupData, 0, 9);
-        $versionExpected = '-- pmf' . substr((string) $faqConfig->getVersion(), 0, 3);
+        $versionExpected = '-- pmf' . substr($faqConfig->getVersion(), 0, 3);
         $queries = [];
 
         $fileName = $_FILES['userfile']['name'];
 
         try {
             $verification = $backup->verifyBackup(file_get_contents($_FILES['userfile']['tmp_name']), $fileName);
             if ($verification) {
                 $ok = 1;
             } else {
+                echo 'This file is not a verified backup file.<br>';
                 $ok = 0;
             }
         } catch (SodiumException) {
             echo 'This file cannot be verified.<br>';
             $ok = 0;
@@ -84,22 +85,24 @@ if ($user->perm->hasPermission($user->getUserId(), 'restore') && $csrfCheck) {
                 Translation::get('ad_csv_no'),
                 $versionFound,
                 $versionExpected
             );
             $ok = 0;
-        } else {
+        }
+
+        if ($ok === 1) {
             // @todo: Start transaction for better recovery if something really bad happens
             $backupData = trim(Strings::substr($backupData, 11));
             $tables = explode(' ', $backupData);
             $numTables = count($tables);
             for ($h = 0; $h < $numTables; ++$h) {
                 $queries[] = sprintf('DELETE FROM %s', $tables[$h]);
             }
             $ok = 1;
         }
 
-        if ($ok == 1) {
+        if ($ok === 1) {
             $tablePrefix = '';
             printf("<p>%s</p>\n", Translation::get('ad_csv_prepare'));
             while ($backupData = fgets($handle, 65536)) {
                 $backupData = trim($backupData);
                 $backupPrefixPattern = '-- pmftableprefix:';
@@ -145,10 +148,12 @@ if ($user->perm->hasPermission($user->getUserId(), 'restore') && $csrfCheck) {
                 $g,
                 Translation::get('ad_csv_of'),
                 $numTables,
                 Translation::get('ad_csv_suc')
             );
+        } else {
+            echo Alert::danger('ad_csv_no', 'Import not possible.');
         }
     } else {
         $errorMessage = match ($_FILES['userfile']['error']) {
             1 => 'The uploaded file exceeds the upload_max_filesize directive in php.ini.',
             2 => 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the ' . 'HTML form.',
diff --git a/phpmyfaq/src/phpMyFAQ/Core/Error.php b/phpmyfaq/src/phpMyFAQ/Core/Error.php
index ff21d13e152..5e89c111f20 100644
--- a/phpmyfaq/src/phpMyFAQ/Core/Error.php
+++ b/phpmyfaq/src/phpMyFAQ/Core/Error.php
@@ -16,10 +16,11 @@
  */
 
 namespace phpMyFAQ\Core;
 
 use ErrorException;
+use phpMyFAQ\Strings;
 
 /**
  * Class Error
  *
  * @package phpMyFAQ
@@ -52,11 +53,11 @@ class Error
             $code = 500;
         }
         http_response_code($code);
         echo "<h1>phpMyFAQ Fatal error</h1>";
         echo "<p>Uncaught exception: '" . $exception::class . "'</p>";
-        echo "<p>Message: '" . $exception->getMessage() . "'</p>";
+        echo "<p>Message: '" . Strings::htmlentities($exception->getMessage()) . "'</p>";
         echo "<p>Stack trace:<pre>" . $exception->getTraceAsString() . "</pre></p>";
         echo "<p>Thrown in '" . $exception->getFile() . "' on line " . $exception->getLine() . "</p>";
         if (ini_get('log_errors')) {
             error_log(
                 sprintf(

Vulnerable code:         $versionExpected = '-- pmf' . substr((string) $faqConfig->getVersion(), 0, 3);
        } else {
        if ($ok == 1) {
        echo "<p>Message: '" . $exception->getMessage() . "'</p>";
Fixed code:         $versionExpected = '-- pmf' . substr($faqConfig->getVersion(), 0, 3);
                echo 'This file is not a verified backup file.<br>';
        }

        if ($ok === 1) {
        if ($ok === 1) {
        } else {
            echo Alert::danger('ad_csv_no', 'Import not possible.');
use phpMyFAQ\Strings;
        echo "<p>Message: '" . Strings::htmlentities($exception->getMessage()) . "'</p>";