CVE-2023-36828 (PHP, published 2023-07-05T22:15Z)
Description: value: Statamic is a flat-first, Laravel and Git powered content management system. Prior to version 4.10.0, the SVG tag does not sanitize malicious SVG. Therefore, an attacker can exploit this vulnerability to perform cross-site scripting attacks using SVG, even when using the `sanitize` function. Version 4.10.0 contains a patch for this issue.
Severity: nan (CVSS v3 base score: 5.4)
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/statamic/cms
Files changed:
composer.json
src/Tags/Svg.php
tests/Tags/SvgTagTest.php
Commit message: [4.x] SVG tag sanitization (#8408)



Vulnerable code:
        return str_replace(
        return Parse::template($tag, []);

Fixed code:
        "enshrined/svg-sanitize": "^0.16.0",
use enshrined\svgSanitize\data\AllowedAttributes;
use enshrined\svgSanitize\data\AllowedTags;
use enshrined\svgSanitize\Sanitizer;
        $svg = str_replace(

        return $this->sanitize($svg);

    private function sanitize($svg)
    {
        if ($this->params->bool('sanitize') === false) {
            return $svg;
        }

        $sanitizer = new Sanitizer;
        $sanitizer->removeXMLTag(! Str::startsWith($svg, '<?xml'));
        $sanitizer->setAllowedAttrs($this->getAllowedAttrs());
        $sanitizer->setAllowedTags($this->getAllowedTags());

        return $sanitizer->sanitize($svg);
    }

    private function getAllowedAttrs()
    {
        $attrs = $this->params->explode('allow_attrs', []);

        return new class($attrs) extends AllowedAttributes
        {
            private static $attrs = [];

            public function __construct($attrs)
            {
                self::$attrs = $attrs;
            }

            public static function getAttributes()
            {
                return array_merge(parent::getAttributes(), self::$attrs);
            }
        };
    }

    private function getAllowedTags()
    {
        $tags = $this->params->explode('allow_tags', []);

        return new class($tags) extends AllowedTags
        {
            private static $tags = [];

            public function __construct($tags)
            {
                self::$tags = $tags;
            }

            public static function getTags()
            {
                return array_merge(parent::getTags(), self::$tags);
            }
        };
    }
use Statamic\View\Antlers\Language\Utilities\StringUtilities;
        $output = Parse::template($tag, []);

        // Normalize whitespace and line breaks for testing ease.
        return trim(StringUtilities::normalizeLineEndings($output));

    /** @test */
    public function it_sanitizes()
    {
        File::put(resource_path('xss.svg'), <<<'SVG'
<svg>
    <path onload="loadxss" onclick="clickxss"></path>
    <script>alert("xss")</script>
    <foreignObject></foreignObject>
    <mesh></mesh>
</svg>
SVG);

        $this->assertEquals(<<<'SVG'
<svg>
  <path></path>
</svg>
SVG,
            $this->tag('{{ svg src="xss" sanitize="true" }}')
        );

        $this->assertEquals(<<<'SVG'
<svg>
  <path onclick="clickxss"></path>
  <foreignObject></foreignObject>
  <mesh></mesh>
</svg>
SVG,
            $this->tag('{{ svg src="xss" sanitize="true" allow_tags="mesh|foreignObject" allow_attrs="onclick" }}')
        );
    }

    /** @test */
    public function sanitizing_doesnt_add_xml_tag()
    {
        // Thes sanitizer package adds an xml tag by default.
        // We want to make sure if there wasn't one to begin with, it doesn't add one.

        $svg = <<<'SVG'
<svg>
  <path></path>
</svg>
SVG;

        File::put(resource_path('xmltag.svg'), $svg);

        $this->assertEquals($svg, $this->tag('{{ svg src="xmltag" sanitize="true" }}'));
    }

    /** @test */
    public function sanitizing_doesnt_remove_an_xml_tag()
    {
        // Thes sanitizer package adds an xml tag by default.
        // We want to make sure that we haven't configured it to remove it if we wanted it there to begin with.

        $svg = <<<'SVG'
<?xml version="1.0" encoding="UTF-8"?>
<svg>
  <path></path>
</svg>
SVG;

        File::put(resource_path('xmltag.svg'), $svg);

        $this->assertEquals($svg, $this->tag('{{ svg src="xmltag" sanitize="true" }}'));
    }