VYPR
Moderate severityNVD Advisory· Published Jul 5, 2023· Updated Oct 24, 2024

Statamic's Antlers sanitizer cannot effectively sanitize malicious SVG

CVE-2023-36828

Description

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.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Statamic CMS prior to 4.10.0 fails to sanitize SVG in the SVG tag, allowing stored XSS via manipulated SVG files.

Vulnerability

Overview

CVE-2023-36828 affects Statamic, a flat-first content management system built on Laravel and Git. In versions prior to 4.10.0, the SVG tag does not properly sanitize malicious SVG content, even when the sanitize function is explicitly used. The root cause is that the sanitize method in Svg.php (see [4]) did not apply any sanitization logic; it simply returned the SVG string unchanged. This allowed attackers to bypass the intended protection and inject arbitrary JavaScript or HTML within SVG elements [1][3].

Exploitation

Details

An attacker can exploit this vulnerability by uploading a crafted SVG file that contains malicious content, such as a ` with an embedded executing javascript: URLs, or other XSS vectors. The Statamic files controller only blocks certain file extensions (e.g., php, php3, php4, php5, phtml) but does not inspect SVG content. Therefore, a malicious SVG can be uploaded as a social media icon or other asset. When the SVG tag renders the file (even with the sanitize parameter set to true`), the payload is output without sanitization, leading to stored cross-site scripting (XSS) [3].

Impact

A successful exploit allows an attacker to execute arbitrary JavaScript in the context of a victim's browser. Since the SVG may be displayed in common layout areas like a footer, any user viewing a page containing the malicious SVG is affected. This can lead to session hijacking, credential theft, or other client-side attacks. The vulnerability is classified as stored XSS, requiring no authentication from the victim if the page is publicly accessible [3].

Mitigation

Statamic addressed this issue in version 4.10.0 by integrating the enshrined/svg-sanitize library into the SVG tag. The patch modifies the sanitize method to use the Sanitizer class, which allows administrators to configure allowed tags and attributes. The commit [2] shows the addition of the sanitization logic. Users should upgrade to Statamic 4.10.0 or later to remediate the vulnerability. No workarounds are documented for versions before 4.10.0.

AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
statamic/cmsPackagist
< 4.10.04.10.0

Affected products

2

Patches

1
c714893ad92d

[4.x] SVG tag sanitization (#8408)

https://github.com/statamic/cmsJason VargaJul 5, 2023via ghsa
3 files changed · +132 2
  • composer.json+1 0 modified
    @@ -12,6 +12,7 @@
             "ext-json": "*",
             "ajthinking/archetype": "^1.0.3",
             "composer/composer": "^1.10.22 || ^2.2.12",
    +        "enshrined/svg-sanitize": "^0.16.0",
             "facade/ignition-contracts": "^1.0",
             "guzzlehttp/guzzle": "^6.3 || ^7.0",
             "james-heinrich/getid3": "^1.9.21",
    
  • src/Tags/Svg.php+60 1 modified
    @@ -2,6 +2,9 @@
     
     namespace Statamic\Tags;
     
    +use enshrined\svgSanitize\data\AllowedAttributes;
    +use enshrined\svgSanitize\data\AllowedTags;
    +use enshrined\svgSanitize\Sanitizer;
     use Statamic\Facades\File;
     use Statamic\Facades\URL;
     use Statamic\Support\Str;
    @@ -47,11 +50,13 @@ public function index()
                 $svg = $this->setTitleAndDesc($svg);
             }
     
    -        return str_replace(
    +        $svg = str_replace(
                 '<svg',
                 collect(['<svg', $attributes])->filter()->implode(' '),
                 $svg
             );
    +
    +        return $this->sanitize($svg);
         }
     
         private function setTitleAndDesc($svg)
    @@ -79,4 +84,58 @@ private function setTitleAndDesc($svg)
     
             return $doc->saveHTML();
         }
    +
    +    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);
    +            }
    +        };
    +    }
     }
    
  • tests/Tags/SvgTagTest.php+71 1 modified
    @@ -4,6 +4,7 @@
     
     use Illuminate\Support\Facades\File;
     use Statamic\Facades\Parse;
    +use Statamic\View\Antlers\Language\Utilities\StringUtilities;
     use Tests\TestCase;
     
     class SvgTagTest extends TestCase
    @@ -17,7 +18,10 @@ public function setUp(): void
     
         private function tag($tag)
         {
    -        return Parse::template($tag, []);
    +        $output = Parse::template($tag, []);
    +
    +        // Normalize whitespace and line breaks for testing ease.
    +        return trim(StringUtilities::normalizeLineEndings($output));
         }
     
         /** @test */
    @@ -32,4 +36,70 @@ public function it_renders_svg_with_additional_params()
         {
             $this->assertStringStartsWith('<svg class="mb-2" xmlns="', $this->tag('{{ svg src="users" class="mb-2" }}'));
         }
    +
    +    /** @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" }}'));
    +    }
     }
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

8

News mentions

0

No linked articles in our index yet.