CVE-2022-36108 (PHP, published 2022-09-13T18:15Z)
Description: value: TYPO3 is an open source PHP based web content management system released under the GNU GPL. It has been discovered that the `f:asset.css` view helper is vulnerable to cross-site scripting when user input is passed as variables to the CSS. Update to TYPO3 version 10.4.32 or 11.5.16 that fix the problem. There are no known workarounds for this issue.
Severity: nan (CVSS v3 base score: 6.1)
Weakness: CWE-79 \u2014 Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
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.
Repository: https://github.com/TYPO3/typo3
Files changed:
typo3/sysext/fluid/Classes/ViewHelpers/Asset/CssViewHelper.php
typo3/sysext/fluid/Tests/Functional/ViewHelpers/Asset/CssViewHelperTest.php
Commit message: [SECURITY] Encode child node variables in f:asset.css view helper

Variables in child nodes like `<f:asset.css>{value}</f:asset.css>`
were not encoded and allow cross-site scripting. In case values shall
be taken as is, corresponding `f:format.raw` instruction has to be used.

Resolves: #97900
Releases: main, 11.5, 10.4
Change-Id: Id843a41c42bbe1f74cdc4efbc117b24d20026b97
Security-Bulletin: TYPO3-CORE-SA-2022-010
Security-References: CVE-2022-36108
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/75719
Tested-by: Oliver Hader <oliver.hader@typo3.org>
Reviewed-by: Oliver Hader <oliver.hader@typo3.org>


Vulnerable code:
    protected $escapeChildren = false;

Fixed code:
    protected $escapeChildren = true;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\View\StandaloneView;

    public static function childNodeRenderingIsCorrectDataProvider(): array
    {
        return [
            // Double quotes
            'variable with double quotes is encoded' => [
                '</style>/* " ', // variable value
                'body { color: #{color}; }', // inner template source
                'body { color: #&lt;/style&gt;/* &quot; ; }', // expectation
            ],
            'variable with double quotes is encoded in single quotes' => [
                '</style>/* " ', // variable value
                'body { color: \'#{color}\'; }', // inner template source
                'body { color: \'#&lt;/style&gt;/* &quot; \'; }', // expectation
            ],
            'variable with double quotes is encoded in double quotes' => [
                '</style>/* " ', // variable value
                'body { color: "#{color}"; }', // inner template source
                'body { color: "#&lt;/style&gt;/* &quot; "; }', // expectation
            ],
            // Single quotes
            'variable with single quotes is encoded' => [
                '</style>/* \' ', // variable value
                'body { color: #{color}; }', // inner template source
                'body { color: #&lt;/style&gt;/* &#039; ; }', // expectation
            ],
            'variable with single quotes is encoded in single quotes' => [
                '</style>/* \' ', // variable value
                'body { color: \'#{color}\'; }', // inner template source
                'body { color: \'#&lt;/style&gt;/* &#039; \'; }', // expectation
            ],
            'variable with single quotes is encoded in double quotes' => [
                '</style>/* \' ', // variable value
                'body { color: "#{color}"; }', // inner template source
                'body { color: "#&lt;/style&gt;/* &#039; "; }', // expectation
            ],
            // Raw instruction
            'raw instruction is passed' => [
                '</style>/* " ',
                'body { color: #{color -> f:format.raw()}; }',
                'body { color: #</style>/* " ; }',
            ],
            'raw instruction is passed in sigle quotes' => [
                '</style>/* " ',
                'body { color: \'#{color -> f:format.raw()}\'; }',
                'body { color: \'#</style>/* " \'; }',
            ],
            'raw instruction is passed in double quotes' => [
                '</style>/* " ',
                'body { color: "#{color -> f:format.raw()}"; }',
                'body { color: "#</style>/* " "; }',
            ],
        ];
    }

    /**
     * @test
     * @dataProvider childNodeRenderingIsCorrectDataProvider
     */
    public function childNodeRenderingIsCorrect(string $value, string $source, string $expectation): void
    {
        $assetCollector = new AssetCollector();
        GeneralUtility::setSingletonInstance(AssetCollector::class, $assetCollector);

        $view = new StandaloneView();
        $view->setTemplateSource(sprintf('<f:asset.css identifier="test">%s</f:asset.css>', $source));
        $view->assign('color', $value);
        $view->render();
        GeneralUtility::removeSingletonInstance(AssetCollector::class, $assetCollector);

        self::assertSame($expectation, $assetCollector->getInlineStyleSheets()['test']['source']);
    }