CVE-2018-14716 (PHP, published 2018-08-06T20:29Z)
Description: value: A Server Side Template Injection (SSTI) was discovered in the SEOmatic plugin before 3.1.4 for Craft CMS, because requests that don't match any elements incorrectly generate the canonicalUrl, and can lead to execution of Twig code.
Severity: MEDIUM (CVSS v3 base score: 7.5)
Weakness: CWE-94 \u2014 Improper Control of Generation of Code ('Code Injection')
The product constructs all or part of a code segment using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the syntax or behavior of the intended code segment.
Repository: https://github.com/nystudio107/craft-seomatic
Files changed:
src/helpers/DynamicMeta.php
src/models/MetaGlobalVars.php
src/seomatic-config/globalmeta/GlobalVars.php
src/services/Helper.php
Commit message: Changed the way requests that don't match any elements generate the `canonicalUrl`, to avoid potentially executing injected Twig code

Signed-off-by: Andrew Welch <andrew@nystudio107.com>


Vulnerable code:
        'canonicalUrl'            => '{{ craft.app.request.pathInfo | striptags }}',
use craft\helpers\UrlHelper;
     * Twig {% paginate %} tag: https://docs.craftcms.com/v3/templating/tags/paginate.html#the-pageInfo-variable
     * The resulting array of arrays has `id`, `language`, `ogLanguage`, `hreflangLanguage`,
     * and `url` as keys.

    /**
     * Return a flattened, indented menu of the given $path
     *
     * @param string $path
     *
     * @return array
     */
    public static function getTypeMenu($path): array
    {
        return SchemaHelper::getTypeMenu($path);
    }

    /**
     * Return a single menu of schemas starting at $path
     *
     * @param string $path
     *
     * @return array
     */
    public static function getSingleTypeMenu($path): array
    {
        return SchemaHelper::getSingleTypeMenu($path);
    }

Fixed code:
            // Strip any query string params, and make sure we have an absolute URL with protocol
            if ($urlParams === null) {
                $url = UrlHelper::stripQueryString($url);
            }
            $url = UrlHelper::absoluteUrlWithProtocol($url);

    /**
     * @inheritdoc
     */
    public function init()
    {
        parent::init();
        // If we have potentially unsafe Twig code, strip it out
        if (!empty($this->canonicalUrl)) {
            if (strpos($this->canonicalUrl, 'craft.app.request.pathInfo') !== false) {
                $this->canonicalUrl = '{seomatic.helper.safeCanonicalUrl()}';
            }
        }
    }

        'canonicalUrl'            => '{seomatic.helper.safeCanonicalUrl()}',
use nystudio107\seomatic\helpers\UrlHelper;
use Craft;
use yii\base\InvalidConfigException;

    /**
     * Return the canonical URL for the request, with the query string stripped
     *
     * @return string
     */
    public static function safeCanonicalUrl(): string
    {
        $url = '';
        try {
            $url = Craft::$app->getRequest()->getPathInfo();
        } catch (InvalidConfigException $e) {
            Craft::error($e->getMessage(), __METHOD__);
        }
        $url = UrlHelper::stripQueryString($url);

        return UrlHelper::absoluteUrlWithProtocol($url);
    }

     * Twig {% paginate %} tag:
     * https://docs.craftcms.com/v3/templating/tags/paginate.html#the-pageInfo-variable
     * The resulting array of arrays has `id`, `language`, `ogLanguage`,
     * `hreflangLanguage`, and `url` as keys.

    /**
     * Return a flattened, indented menu of the given $path
     *
     * @param string $path
     *
     * @return array
     */
    public static function getTypeMenu($path): array
    {
        return SchemaHelper::getTypeMenu($path);
    }

    /**
     * Return a single menu of schemas starting at $path
     *
     * @param string $path
     *
     * @return array
     */
    public static function getSingleTypeMenu($path): array
    {
        return SchemaHelper::getSingleTypeMenu($path);
    }