CVE-2023-2752: 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.2.0-beta.
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-05-17T08:15Z
Repository: https://github.com/thorsten/phpmyfaq
Language: PHP
Files changed: phpmyfaq/faq.php, phpmyfaq/src/phpMyFAQ/Helper/FaqHelper.php, tests/phpMyFAQ/Helper/FaqHelperTest.php
Commit date: 2023-04-11 06:47:28 +0200
Commit message: fix: cleanup content before sending it to the user

Diff: diff --git a/phpmyfaq/faq.php b/phpmyfaq/faq.php
index 578ae55a8eb..bb7cf27b940 100644
--- a/phpmyfaq/faq.php
+++ b/phpmyfaq/faq.php
@@ -103,10 +103,12 @@ if ($faqConfig->get('main.enableMarkdownEditor')) {
     $answer = $markDown->text($faq->faqRecord['content']);
 } else {
     $answer = $faqHelper->renderMarkupContent($faq->faqRecord['content']);
 }
 
+$answer = $faqHelper->cleanUpContent($answer);
+
 // Rewrite URL fragments
 $currentUrl = htmlspecialchars("//{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}", ENT_QUOTES, 'UTF-8');
 $answer = $faqHelper->rewriteUrlFragments($answer, $currentUrl);
 
 // Add Glossary entries for answers only
diff --git a/phpmyfaq/src/phpMyFAQ/Helper/FaqHelper.php b/phpmyfaq/src/phpMyFAQ/Helper/FaqHelper.php
index cd8dc505a87..c482b1a7126 100644
--- a/phpmyfaq/src/phpMyFAQ/Helper/FaqHelper.php
+++ b/phpmyfaq/src/phpMyFAQ/Helper/FaqHelper.php
@@ -15,10 +15,11 @@
  * @since     2010-11-12
  */
 
 namespace phpMyFAQ\Helper;
 
+use DOMDocument;
 use Exception;
 use ParsedownExtra;
 use phpMyFAQ\Category;
 use phpMyFAQ\Configuration;
 use phpMyFAQ\Date;
@@ -224,6 +225,26 @@ class FaqHelper extends Helper
             $categoryId,
             $faqEntity->getId(),
             $faqEntity->getLanguage()
         );
     }
+
+    /**
+     * Remove <script> tags, we don't need them
+     *
+     * @param string $content
+     * @return string
+     */
+    public function cleanUpContent(string $content): string
+    {
+        $document = new DOMDocument();
+        $document->loadHTML($content, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
+
+        $scriptTags = $document->getElementsByTagName('script');
+
+        for ($i = 0; $i < $scriptTags->length; $i++) {
+            $scriptTags->item($i)->parentNode->removeChild($scriptTags->item($i));
+        }
+
+        return preg_replace(['/\r/', '/\n/'], '', $document->saveHTML());
+    }
 }
diff --git a/tests/phpMyFAQ/Helper/FaqHelperTest.php b/tests/phpMyFAQ/Helper/FaqHelperTest.php
index fdce1ead65d..ead622c621d 100644
--- a/tests/phpMyFAQ/Helper/FaqHelperTest.php
+++ b/tests/phpMyFAQ/Helper/FaqHelperTest.php
@@ -57,6 +57,16 @@ class FaqHelperTest extends TestCase
         $this->assertEquals(
             'https://localhost:443/index.php?action=faq&cat=1&id=42&artlang=de',
             $this->faqHelper->createFaqUrl($faqEntity, 1)
         );
     }
+
+    public function testCleanUpContent(): void
+    {
+        $content = '<p>Some text <script>alert("Hello, world!");</script></p>';
+        $expectedOutput = '<p>Some text </p>';
+
+        $actualOutput = $this->faqHelper->cleanUpContent($content);
+
+        $this->assertEquals($expectedOutput, $actualOutput);
+    }
 }

Fixed code: $answer = $faqHelper->cleanUpContent($answer);

use DOMDocument;

    /**
     * Remove <script> tags, we don't need them
     *
     * @param string $content
     * @return string
     */
    public function cleanUpContent(string $content): string
    {
        $document = new DOMDocument();
        $document->loadHTML($content, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);

        $scriptTags = $document->getElementsByTagName('script');

        for ($i = 0; $i < $scriptTags->length; $i++) {
            $scriptTags->item($i)->parentNode->removeChild($scriptTags->item($i));
        }

        return preg_replace(['/\r/', '/\n/'], '', $document->saveHTML());
    }

    public function testCleanUpContent(): void
    {
        $content = '<p>Some text <script>alert("Hello, world!");</script></p>';
        $expectedOutput = '<p>Some text </p>';

        $actualOutput = $this->faqHelper->cleanUpContent($content);

        $this->assertEquals($expectedOutput, $actualOutput);
    }