CVE-2023-5317: 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.18.
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-09-30T01:15Z
Repository: https://github.com/thorsten/phpmyfaq
Language: PHP
Files changed: phpmyfaq/admin/ajax.config.php, phpmyfaq/admin/instances.php
Commit date: 2023-09-01 17:57:46 +0200
Commit message: fix: allow only valid URLs for instances

Diff: diff --git a/phpmyfaq/admin/ajax.config.php b/phpmyfaq/admin/ajax.config.php
index 7c72fba29a3..86b65f0ab24 100644
--- a/phpmyfaq/admin/ajax.config.php
+++ b/phpmyfaq/admin/ajax.config.php
@@ -62,10 +62,17 @@ switch ($ajaxAction) {
             $http->setStatus(400);
             $http->sendJsonWithHeaders(['error' => 'Cannot create instance.']);
             exit(1);
         }
 
+        $url = 'https://' . $url . '.' . $_SERVER['SERVER_NAME'];
+        if (!Filter::filterVar($url, FILTER_VALIDATE_URL)) {
+            $http->setStatus(400);
+            $http->sendJsonWithHeaders(['error' => 'Cannot create instance: wrong URL']);
+            exit(1);
+        }
+
         $data = [
             'url' => 'https://' . $url . '.' . $_SERVER['SERVER_NAME'],
             'instance' => $instance,
             'comment' => $comment,
         ];
diff --git a/phpmyfaq/admin/instances.php b/phpmyfaq/admin/instances.php
index a2374dfa574..05496994b01 100644
--- a/phpmyfaq/admin/instances.php
+++ b/phpmyfaq/admin/instances.php
@@ -68,42 +68,51 @@ if ($user->perm->hasPermission($user->getUserId(), 'editinstances')) {
         $updatedClient = new Client($faqConfig);
         $moveInstance = false;
 
         // Collect updated data for database
         $updatedData = [];
-        $updatedData['url'] = Filter::filterInput(INPUT_POST, 'url', FILTER_UNSAFE_RAW);
+        $updatedData['url'] = Filter::filterInput(INPUT_POST, 'url', FILTER_VALIDATE_URL);
         $updatedData['instance'] = Filter::filterInput(INPUT_POST, 'instance', FILTER_UNSAFE_RAW);
         $updatedData['comment'] = Filter::filterInput(INPUT_POST, 'comment', FILTER_UNSAFE_RAW);
 
         // Original data
         $originalData = $originalClient->getInstanceById($instanceId);
 
         if ($originalData->url !== $updatedData['url']) {
             $moveInstance = true;
         }
 
-        if ($updatedClient->updateInstance($instanceId, $updatedData)) {
+        if (is_null($updatedData['url'])) {
+            printf(
+                '<p class="alert alert-danger">%s%s<br/>%s</p>',
+                '<a class="close" data-dismiss="alert" href="#">&times;</a>',
+                $PMF_LANG['ad_entryins_fail'],
+                 $faqConfig->getDb()->error()
+            );
+        } else {
+          if ($updatedClient->updateInstance($instanceId, $updatedData)) {
             if ($moveInstance) {
-                try {
-                    $updatedClient->moveClientFolder($originalData->url, $updatedData['url']);
-                    $updatedClient->deleteClientFolder($originalData->url);
-                } catch (Exception $e) {
-                    // handle exception
-                }
+              try {
+                $updatedClient->moveClientFolder($originalData->url, $updatedData['url']);
+                $updatedClient->deleteClientFolder($originalData->url);
+              } catch (Exception $e) {
+                // handle exception
+              }
             }
             printf(
                 '<p class="alert alert-success">%s%s</p>',
                 '<a class="close" data-dismiss="alert" href="#">&times;</a>',
                 $PMF_LANG['ad_config_saved']
             );
-        } else {
+          } else {
             printf(
                 '<p class="alert alert-danger">%s%s<br/>%s</p>',
                 '<a class="close" data-dismiss="alert" href="#">&times;</a>',
                 $PMF_LANG['ad_entryins_fail'],
                 $faqConfig->getDb()->error()
             );
+          }
         }
     }
     ?>
   <table class="table">
     <thead>

Vulnerable code:         $updatedData['url'] = Filter::filterInput(INPUT_POST, 'url', FILTER_UNSAFE_RAW);
        if ($updatedClient->updateInstance($instanceId, $updatedData)) {
                try {
                    $updatedClient->moveClientFolder($originalData->url, $updatedData['url']);
                    $updatedClient->deleteClientFolder($originalData->url);
                } catch (Exception $e) {
                    // handle exception
                }
        } else {
Fixed code:         $url = 'https://' . $url . '.' . $_SERVER['SERVER_NAME'];
        if (!Filter::filterVar($url, FILTER_VALIDATE_URL)) {
            $http->setStatus(400);
            $http->sendJsonWithHeaders(['error' => 'Cannot create instance: wrong URL']);
            exit(1);
        }

        $updatedData['url'] = Filter::filterInput(INPUT_POST, 'url', FILTER_VALIDATE_URL);
        if (is_null($updatedData['url'])) {
            printf(
                '<p class="alert alert-danger">%s%s<br/>%s</p>',
                '<a class="close" data-dismiss="alert" href="#">&times;</a>',
                $PMF_LANG['ad_entryins_fail'],
                 $faqConfig->getDb()->error()
            );
        } else {
          if ($updatedClient->updateInstance($instanceId, $updatedData)) {
              try {
                $updatedClient->moveClientFolder($originalData->url, $updatedData['url']);
                $updatedClient->deleteClientFolder($originalData->url);
              } catch (Exception $e) {
                // handle exception
              }
          } else {
          }