VYPR
High severityNVD Advisory· Published May 11, 2020· Updated Aug 4, 2024

CVE-2020-12790

CVE-2020-12790

Description

SEOmatic plugin for Craft CMS before 3.2.49 contains a server-side template injection vulnerability via unsanitized URLs, leading to credential disclosure.

AI Insight

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

SEOmatic plugin for Craft CMS before 3.2.49 contains a server-side template injection vulnerability via unsanitized URLs, leading to credential disclosure.

Vulnerability

The SEOmatic plugin for Craft CMS (versions before 3.2.49) fails to properly sanitize user-supplied URLs in the helpers/DynamicMeta.php file. The sanitizeUrl method decoded HTML entities but did not URL-decode the input before applying strip_tags. This allowed an attacker to inject a semicolon followed by a malicious Twig template into the URL, bypassing intended sanitization. [1][2]

Exploitation

An attacker can craft a URL containing a semicolon (;) followed by a Twig template expression. Because the URL is not URL-decoded prior to tag stripping, the malicious template is not removed and is subsequently processed by the Twig engine. The attack does not require authentication; it can be performed by any user who can cause the plugin to process a URL. The exploitation is a server-side template injection (SSTI) vector via the crafted URL. [1][2]

Impact

Successful exploitation allows an attacker to execute arbitrary Twig templates on the server. This can lead to full disclosure of sensitive credentials stored in the Craft CMS environment, such as database passwords and application keys. The severity is high as it compromises confidentiality and can lead to further system compromise. [1]

Mitigation

The vulnerability is fixed in SEOmatic version 3.2.49, released on 2020-03-24. The fix adds a urldecode call before the existing strip_tags in the sanitizeUrl method, ensuring that URL-encoded Twig syntax cannot bypass the tag stripping. Users are advised to upgrade immediately. No workaround is available. [2][3][4]

AI Insight generated on May 21, 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
nystudio107/craft-seomaticPackagist
< 3.2.493.2.49

Affected products

2

Patches

1
82f4a25b28fd

Merge branch 'release/3.2.49' into v3

https://github.com/nystudio107/craft-seomaticAndrew WelchMar 24, 2020via ghsa
6 files changed · +33 3
  • CHANGELOG.md+11 0 modified
    @@ -1,5 +1,16 @@
     # SEOmatic Changelog
     
    +## 3.2.49 - 2020.03.24
    +### Added
    +* Aliases will now auto-suggest in the Site URL Override settings field
    +* SEOmatic now will replace any stripped HTML tags with a space, so that the text is more readable
    +
    +### Fixed
    +* The Site URL Override is now parsed for both aliases and environment variables
    +
    +### Security
    +* Ensure that URLs are `urldecode`d before attempting to use a RegEx to strip tags from them
    +
     ## 3.2.48 - 2020.03.18
     ### Added
     * Added batching to sitemap generation so that the memory used is fixed, and no longer dependent on how many sitemap entries are being processed
    
  • composer.json+1 1 modified
    @@ -2,7 +2,7 @@
         "name": "nystudio107/craft-seomatic",
         "description": "SEOmatic facilitates modern SEO best practices & implementation for Craft CMS 3. It is a turnkey SEO system that is comprehensive, powerful, and flexible.",
         "type": "craft-plugin",
    -    "version": "3.2.48",
    +    "version": "3.2.49",
         "keywords": [
             "craft",
             "cms",
    
  • src/helpers/DynamicMeta.php+2 0 modified
    @@ -80,6 +80,7 @@ public static function sanitizeUrl(string $url, bool $checkStatus = true): strin
             $url = UrlHelper::stripQueryString($url);
             // HTML decode the entities, then strip out any tags
             $url = html_entity_decode($url, ENT_NOQUOTES, 'UTF-8');
    +        $url = urldecode($url);
             $url = strip_tags($url);
     
             // If this is a >= 400 status code, set the canonical URL to nothing
    @@ -647,6 +648,7 @@ public static function getLocalizedUrls(string $uri = null, int $siteId = null):
                 $url = UrlHelper::absoluteUrlWithProtocol($url);
     
                 $url = $url ?? '';
    +            $url = self::sanitizeUrl($url);
                 $language = $site->language;
                 $ogLanguage = LocalizationHelper::normalizeOgLocaleLanguage($language);
                 $hreflangLanguage = $language;
    
  • src/helpers/Text.php+17 2 modified
    @@ -128,15 +128,30 @@ public static function extractTextFromField($field): string
                 $result = self::extractTextFromTags($field);
             } else {
                 if (\is_array($field)) {
    -                $result = strip_tags((string)$field[0]);
    +                $result = self::smartStripTags((string)$field[0]);
                 } else {
    -                $result = strip_tags((string)$field);
    +                $result = self::smartStripTags((string)$field);
                 }
             }
     
             return $result;
         }
     
    +    /**
    +     * Strip HTML tags, but replace them with a space rather than just eliminating them
    +     *
    +     * @param $str
    +     * @return string
    +     */
    +    public static function smartStripTags($str)
    +    {
    +        $str = str_replace('<', ' <', $str);
    +        $str = strip_tags($str);
    +        $str = str_replace('  ', ' ', $str);
    +
    +        return $str;
    +    }
    +
         /**
          * Extract concatenated text from all of the tags in the $tagElement and
          * return as a comma-delimited string
    
  • src/helpers/UrlHelper.php+1 0 modified
    @@ -39,6 +39,7 @@ public static function siteUrl(string $path = '', $params = null, string $scheme
         {
             $siteUrl = Seomatic::$settings->siteUrlOverride;
             if (!empty($siteUrl)) {
    +            $siteUrl = MetaValue::parseString($siteUrl);
                 return rtrim($siteUrl, '/').'/'.ltrim($path, '/');
             }
     
    
  • src/templates/settings/plugin/_includes/general.twig+1 0 modified
    @@ -119,6 +119,7 @@
                     label: "Site URL Override"|t("seomatic"),
                     instructions: "SEOmatic uses the Craft `siteUrl` to generate the external URLs.  If you are using it in a non-standard environment, such as a headless GraphQL or ElementAPI server, you can override what it uses for the `siteUrl` below."|t("seomatic"),
                     suggestEnvVars: true,
    +                suggestAliases: true,
                     id: "siteUrlOverride",
                     name: "siteUrlOverride",
                     value: settings.siteUrlOverride,
    

Vulnerability mechanics

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

References

7

News mentions

0

No linked articles in our index yet.