CVE-2024-28191 (PHP, published 2024-04-09T14:15Z)
Description: value: Contao is an open source content management system. Starting in version 4.0.0 and prior to version 4.13.40 and 5.3.4, it is possible to inject insert tags in frontend forms if the output is structured in a very specific way. Contao versions 4.13.40 and 5.3.4 have a patch for this issue. As a workaround, do not output user data from frontend forms next to each other, always separate them by at least one character.
Severity: nan (CVSS v3 base score: )
Weakness: NVD-CWE-noinfo \u2014 Insufficient Information
There is insufficient information about the issue to classify it; details are unkown or unspecified.
Repository: https://github.com/contao/contao
Files changed:
core-bundle/contao/library/Contao/Input.php
core-bundle/src/String/SimpleTokenParser.php
core-bundle/tests/Contao/InputTest.php
core-bundle/tests/String/SimpleTokenParserTest.php
Commit message: Merge pull request from GHSA-747v-52c4-8vj8

Co-authored-by: Leo Feyer <1192057+leofeyer@users.noreply.github.com>

Vulnerable code:
		return str_replace(array('{{', '}}'), array('&#123;&#123;', '&#125;&#125;'), (string) $varValue);
                return $data[$matches[1]];
        $source = '{{ foo }}';
        $encoded = '&#123;&#123; foo &#125;&#125;';
    public function testEncodeNoneMode(string $source, string $expected, string|null $expectedEncoded = null): void
        $this->assertSame($expectedEncoded.$expectedEncoded, Input::encodeInput($source.$source, InputEncodingMode::encodeNone));
        yield ['{}', '{}'];
        yield ['{{{}}}', '{{{}}}', '&#123;&#123;{&#125;&#125;}'];
            '{if token=="foo"}',
            '{if token=="foo"}',

Fixed code:
		$varValue = str_replace(array('{{', '}}'), array('&#123;&#123;', '&#125;&#125;'), (string) $varValue);

		// Encode single curly braces at the beginning and end of the string
		return preg_replace(array('/^(\s*)\{|\{(\s*)$/', '/^(\s*)\}|\}(\s*)$/'), array('$1&#123;$2', '$1&#125;$2'), $varValue);
use Contao\Input;
                return Input::encodeInsertTags($data[$matches[1]]);
        $source = ' {{ foo }} { bar } ';
        $encoded = ' &#123;&#123; foo &#125;&#125; { bar &#125; ';
    public function testEncodeNoneMode(string $source, string $expected, string|null $expectedEncoded = null, string|null $expectedEncodedDouble = null): void
        $this->assertSame($expectedEncodedDouble ?? $expectedEncoded.$expectedEncoded, Input::encodeInput($source.$source, InputEncodingMode::encodeNone));
        yield ['{}', '{}', '&#123;&#125;', '&#123;}{&#125;'];
        yield ['{{{}}}', '{{{}}}', '&#123;&#123;{&#125;&#125;&#125;', '&#123;&#123;{&#125;&#125;}&#123;&#123;{&#125;&#125;&#125;'];
        yield ['{ start {and} end }', '{ start {and} end }', '&#123; start {and} end &#125;', '&#123; start {and} end }{ start {and} end &#125;'];
        yield ["\n\t { foo }\n\t ", "\n\t { foo }\n\t ", "\n\t &#123; foo &#125;\n\t ", "\n\t &#123; foo }\n\t \n\t { foo &#125;\n\t "];
        yield 'Test regular curly braces do not get encoded' => [
            '##token##',
            ['token' => 'foo { bar } baz'],
            'foo { bar } baz',
        ];

            '&#123;if token=="foo"&#125;',
        ];

        yield 'Test insert tags insertion not possible' => [
            '##token##',
            ['token' => '{{date}}'],
            '&#123;&#123;date&#125;&#125;',
            '&#123;if token=="foo"&#125;',
        ];

        yield 'Test insert tags insertion not possible with multiple tokens' => [
            '##token1####token2####token3##',
            ['token1' => '{', 'token2' => '{date}', 'token3' => '}'],
            '&#123;&#123;date&#125;&#125;',