CVE-2024-37156: Insufficient Information (NVD-CWE-noinfo)
value: The SuluFormBundle adds support for creating dynamic forms in Sulu Admin. The TokenController get parameter formName is not sanitized in the returned input field which leads to XSS. This vulnerability is fixed in 2.5.3.
CWE description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
Published: 2024-06-06T16:15Z
Repository: https://github.com/sulu/SuluFormBundle
Language: PHP
Files changed: Controller/FormTokenController.php, Tests/Functional/Controller/FormTokenControllerTest.php
Commit date: 2024-06-06 16:09:01 +0200
Commit message: Merge pull request from GHSA-rrvc-c7xg-7cf3


Diff: diff --git a/Controller/FormTokenController.php b/Controller/FormTokenController.php
index 14a6fe6..630279b 100644
--- a/Controller/FormTokenController.php
+++ b/Controller/FormTokenController.php
@@ -33,10 +33,13 @@ class FormTokenController
         $csrfToken = $this->csrfTokenManager->getToken($formName)->getValue();
 
         $content = $csrfToken;
 
         if ($request->get('html')) {
+            $formName = htmlspecialchars($formName, ENT_QUOTES, 'UTF-8');
+            $csrfToken = htmlspecialchars($csrfToken, ENT_QUOTES, 'UTF-8');
+
             $content = \sprintf(
                 '<input type="hidden" id="%s__token" name="%s[_token]" value="%s" />',
                 $formName,
                 $formName,
                 $csrfToken
diff --git a/Tests/Functional/Controller/FormTokenControllerTest.php b/Tests/Functional/Controller/FormTokenControllerTest.php
new file mode 100644
index 0000000..50c004b
--- /dev/null
+++ b/Tests/Functional/Controller/FormTokenControllerTest.php
@@ -0,0 +1,55 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of Sulu.
+ *
+ * (c) Sulu GmbH
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace Sulu\Bundle\FormBundle\Tests\Functional\Controller;
+
+use Sulu\Bundle\FormBundle\Controller\FormTokenController;
+use Sulu\Bundle\TestBundle\Testing\SuluTestCase;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Security\Csrf\CsrfToken;
+use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
+
+class FormTokenControllerTest extends SuluTestCase
+{
+    /**
+     * @var FormTokenController
+     */
+    private formTokenController $formTokenController;
+
+    protected function setUp(): void
+    {
+        parent::setUp();
+        $csrfTokenManager = $this->createMock(CsrfTokenManagerInterface::class);
+        $csrfToken = $this->createMock(CsrfToken::class);
+        $csrfToken->method('getValue')->willReturn('testToken');
+        $csrfTokenManager->method('getToken')->willReturn($csrfToken);
+        $this->formTokenController = new FormTokenController($csrfTokenManager);
+    }
+
+    public function testTokenAction(): void
+    {
+        $request = new Request([], [], ['form' => 'testForm', 'html' => true]);
+        $response = $this->formTokenController->tokenAction($request);
+        $this->assertSame(200, $response->getStatusCode());
+        $this->assertStringContainsString('testForm', $response->getContent());
+    }
+
+    public function testTokenActionWithScript(): void
+    {
+        $request = new Request([], [], ['form' => '<script>alert(1)</script>', 'html' => true]);
+        $response = $this->formTokenController->tokenAction($request);
+        $this->assertSame(200, $response->getStatusCode());
+        $this->assertStringContainsString('&lt;script&gt;alert(1)&lt;/script&gt;', $response->getContent());
+        $this->assertStringNotContainsString('<script>alert(1)</script>', $response->getContent());
+    }
+}

Fixed code:             $formName = htmlspecialchars($formName, ENT_QUOTES, 'UTF-8');
            $csrfToken = htmlspecialchars($csrfToken, ENT_QUOTES, 'UTF-8');

<?php

declare(strict_types=1);

/*
 * This file is part of Sulu.
 *
 * (c) Sulu GmbH
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace Sulu\Bundle\FormBundle\Tests\Functional\Controller;

use Sulu\Bundle\FormBundle\Controller\FormTokenController;
use Sulu\Bundle\TestBundle\Testing\SuluTestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;

class FormTokenControllerTest extends SuluTestCase
{
    /**
     * @var FormTokenController
     */
    private formTokenController $formTokenController;

    protected function setUp(): void
    {
        parent::setUp();
        $csrfTokenManager = $this->createMock(CsrfTokenManagerInterface::class);
        $csrfToken = $this->createMock(CsrfToken::class);
        $csrfToken->method('getValue')->willReturn('testToken');
        $csrfTokenManager->method('getToken')->willReturn($csrfToken);
        $this->formTokenController = new FormTokenController($csrfTokenManager);
    }

    public function testTokenAction(): void
    {
        $request = new Request([], [], ['form' => 'testForm', 'html' => true]);
        $response = $this->formTokenController->tokenAction($request);
        $this->assertSame(200, $response->getStatusCode());
        $this->assertStringContainsString('testForm', $response->getContent());
    }

    public function testTokenActionWithScript(): void
    {
        $request = new Request([], [], ['form' => '<script>alert(1)</script>', 'html' => true]);
        $response = $this->formTokenController->tokenAction($request);
        $this->assertSame(200, $response->getStatusCode());
        $this->assertStringContainsString('&lt;script&gt;alert(1)&lt;/script&gt;', $response->getContent());
        $this->assertStringNotContainsString('<script>alert(1)</script>', $response->getContent());
    }
}