CVE-2022-39365 (PHP, published 2022-10-27T15:15Z)
Description: value: Pimcore is an open source data and experience management platform. Prior to version 10.5.9, the user controlled twig templates rendering in `Pimcore/Mail` & `ClassDefinition\Layout\Text` is vulnerable to server-side template injection, which could lead to remote code execution. Version 10.5.9 contains a patch for this issue. As a workaround, one may apply the patch manually.
Severity: nan (CVSS v3 base score: 9.8)
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/pimcore/pimcore
Files changed:
bundles/CoreBundle/DependencyInjection/Configuration.php
bundles/CoreBundle/DependencyInjection/PimcoreCoreExtension.php
bundles/CoreBundle/Resources/config/pimcore/default.yaml
bundles/CoreBundle/Resources/config/templating_twig.yaml
doc/Development_Documentation/05_Objects/01_Object_Classes/03_Layout_Elements/01_Dynamic_Text_Labels.md
doc/Development_Documentation/19_Development_Tools_and_Details/25_Email_Framework/README.md
doc/Development_Documentation/23_Installation_and_Upgrade/09_Upgrade_Notes/README.md
lib/Mail.php
lib/Templating/TwigDefaultDelegatingEngine.php
lib/Twig/Sandbox/SecurityPolicy.php
models/DataObject/ClassDefinition/Layout/Text.php
Commit message: [Mail] Renderer email content twig templates in a sandbox (#13347)

* [Mail] Renderer email content twig templates in a sandbox

* [Mail] Renderer email content twig templates in a sandbox

* [Mail] Renderer email content twig templates in a sandbox

* [Mail] Renderer email content twig templates in a sandbox

* [Mail] Renderer email content twig templates in a sandbox

* [Mail] Renderer email content twig templates in a sandbox

* Apply suggestions from code review

Co-authored-by: Sebastian Blank <blank@data-factory.net>

* Update lib/Templating/TwigDefaultDelegatingEngine.php

Co-authored-by: Jacob Dreesen <j.dreesen@neusta.de>

* [Twig] Renderer user controlled twig templates in a sandbox - review changes #13347

* [Twig] Renderer user controlled twig templates in a sandbox - use custom security policy to whitelist object properties and methods execution by default #13347

* [Twig] Renderer user controlled twig templates in a sandbox - review changes #13347

* [Twig] Renderer user controlled twig templates in a sandbox - fix phpstan #13347

* [Twig] Renderer user controlled twig templates in a sandbox - fix service definition #13347

* [Twig] Renderer user controlled twig templates in a sandbox - docs typo #13347

Co-authored-by: Sebastian Blank <blank@data-factory.net>
Co-authored-by: Jacob Dreesen <j.dreesen@neusta.de>

Vulnerable code:
    private function renderParams(string $string): string
        $twig = $templatingEngine->getTwigEnvironment();
        $template = $twig->createTemplate($string);

        return $template->render($this->getParams());
            return $this->renderParams($subject);
            $content = $this->renderParams($html);
            $content = $this->renderParams($text);
    /**
     * @var Environment
     */
    protected $twig;

     * @param Environment $twig
    public function __construct(Environment $twig, array $engines = [])
        $this->twig = $twig;

    /**
     * @return Environment
     */
    public function getTwigEnvironment(): Environment
        $twig = $templatingEngine->getTwigEnvironment();
        $template = $twig->createTemplate($this->html);
        $this->html = $template->render(array_merge($context,
            [
                'object' => $object,
            ]
        ));

Fixed code:
        $this->addTemplatingEngineNode($rootNode);

    private function addTemplatingEngineNode(ArrayNodeDefinition $rootNode): void
    {
        $rootNode
            ->children()
            ->arrayNode('templating_engine')
            ->addDefaultsIfNotSet()
            ->children()
                ->arrayNode('twig')
                ->addDefaultsIfNotSet()
                ->children()
                    ->arrayNode('sandbox_security_policy')
                        ->info('Whitelist tags, filters & functions for evaluating twig 
                        templates in a sandbox environment e.g. used by Mailer & Text layout component.')
                        ->children()
                            ->arrayNode('tags')
                                ->scalarPrototype()->end()
                            ->end()
                            ->arrayNode('filters')
                                ->scalarPrototype()->end()
                            ->end()
                            ->arrayNode('functions')
                                ->scalarPrototype()->end()
                            ->end()
                        ->end()
                    ->end()
                ->end()
            ->end()
        ->end();
    }
        //twig security policy whitelist config
        $container->setParameter('pimcore.templating.twig.sandbox_security_policy.tags', $config['templating_engine']['twig']['sandbox_security_policy']['tags']);
        $container->setParameter('pimcore.templating.twig.sandbox_security_policy.filters', $config['templating_engine']['twig']['sandbox_security_policy']['filters']);
        $container->setParameter('pimcore.templating.twig.sandbox_security_policy.functions', $config['templating_engine']['twig']['sandbox_security_policy']['functions']);

    templating_engine:
        twig:
            sandbox_security_policy:
                tags: ['set']
                filters: ['escape', 'trans']
                functions: ['path', 'asset']
    Pimcore\Twig\Sandbox\SecurityPolicy:
        arguments:
            $allowedTags: '%pimcore.templating.twig.sandbox_security_policy.tags%'
            $allowedFilters: '%pimcore.templating.twig.sandbox_security_policy.filters%'
            $allowedFunctions: '%pimcore.templating.twig.sandbox_security_policy.functions%'

    Twig\Extension\SandboxExtension:
        class: Twig\Extension\SandboxExtension
        arguments:
            $policy: '@Pimcore\Twig\Sandbox\SecurityPolicy'


### Sandbox Restrictions
Dynamic Text renders user controlled twig templates in a sandbox with restrictive
security policies for tags, filters & functions. Please use following configuration to allow more in template rendering:

```yaml
    pimcore:
          templating_engine:
              twig:
                sandbox_security_policy:
                  tags: ['if']
                  filters: ['upper']
                  functions: ['include', 'path']
```

## Sandbox Restrictions
Sending mails renders user controlled twig templates in a sandbox with restrictive 
security policies for tags, filters & functions. Please use following configuration to allow more in template rendering:

```yaml
    pimcore:
          templating_engine:
              twig:
                sandbox_security_policy:
                  tags: ['if']
                  filters: ['upper']
                  functions: ['include', 'path']
```
- [Twig] Sending mails and Dataobject Text Layouts, which allow rendering user controlled twig templates are now executed in a sandbox with restrictive security policies for tags, filters, functions.
         Please use following configuration to allow more in template rendering:
  ```yaml
  pimcore:
        templating_engine:
            twig:
              sandbox_security_policy:
                tags: ['if']
                filters: ['upper']
                functions: ['include', 'path', 'range']
  ```

use Twig\Sandbox\SecurityError;
    private function renderParams(string $string, string $context): string
        try {
            $twig = $templatingEngine->getTwigEnvironment(true);
            $template = $twig->createTemplate($string);

            return $template->render($this->getParams());
        } catch (SecurityError $e) {
            Logger::err((string) $e);

            throw new \Exception(sprintf("Failed rendering the %s: %s. Please check your twig sandbox security policy or contact the administrator.",
            $context, substr($e->getMessage(),0, strpos($e->getMessage(), ' in "__string'))));
        } finally {
            $templatingEngine->disableSandboxExtensionFromTwigEnvironment();
        }
            return $this->renderParams($subject, 'subject');
            $content = $this->renderParams($html, 'body');
            $content = $this->renderParams($text, 'body');
use Pimcore\Config;
use Twig\Extension\SandboxExtension;
    public function __construct(protected Environment $twig, protected Config $config, array $engines = [])
    public function getTwigEnvironment(bool $sandboxed = false): Environment
        if ($sandboxed) {
            /** @var SandboxExtension $sandboxExtension */
            $sandboxExtension = $this->twig->getExtension(SandboxExtension::class);
            $sandboxExtension->enableSandbox();
        }

    public function disableSandboxExtensionFromTwigEnvironment(): void
    {
        /** @var SandboxExtension $sandboxExtension */
        $sandboxExtension = $this->twig->getExtension(SandboxExtension::class);
        $sandboxExtension->disableSandbox();
    }

<?php

declare(strict_types=1);

/**
 * Pimcore
 *
 * This source file is available under two different licenses:
 * - GNU General Public License version 3 (GPLv3)
 * - Pimcore Commercial License (PCL)
 * Full copyright and license information is available in
 * LICENSE.md which is distributed with this source code.
 *
 *  @copyright  Copyright (c) Pimcore GmbH (http://www.pimcore.org)
 *  @license    http://www.pimcore.org/license     GPLv3 and PCL
 */

namespace Pimcore\Twig\Sandbox;

use Twig\Sandbox\SecurityNotAllowedFilterError;
use Twig\Sandbox\SecurityNotAllowedFunctionError;
use Twig\Sandbox\SecurityNotAllowedTagError;
use Twig\Sandbox\SecurityPolicyInterface;

/**
 * Note: Reused to disable checks on object methods and properties.
 *
 * Represents a security policy which need to be enforced when sandbox mode is enabled.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
final class SecurityPolicy implements SecurityPolicyInterface
{
    private array $allowedTags;
    private array $allowedFilters;
    private array $allowedFunctions;

    public function __construct(array $allowedTags = [], array $allowedFilters = [], array $allowedFunctions = [])
    {
        $this->allowedTags = $allowedTags;
        $this->allowedFilters = $allowedFilters;
        $this->allowedFunctions = $allowedFunctions;
    }

    public function setAllowedTags(array $tags)
    {
        $this->allowedTags = $tags;
    }

    public function setAllowedFilters(array $filters)
    {
        $this->allowedFilters = $filters;
    }

    public function setAllowedFunctions(array $functions)
    {
        $this->allowedFunctions = $functions;
    }

    public function checkSecurity($tags, $filters, $functions): void
    {
        foreach ($tags as $tag) {
            if (!\in_array($tag, $this->allowedTags)) {
                throw new SecurityNotAllowedTagError(sprintf('Tag "%s" is not allowed.', $tag), $tag);
            }
        }

        foreach ($filters as $filter) {
            if (!\in_array($filter, $this->allowedFilters)) {
                throw new SecurityNotAllowedFilterError(sprintf('Filter "%s" is not allowed.', $filter), $filter);
            }
        }

        foreach ($functions as $function) {
            //check if a function is allowed or a pimcore twig functions
            if (!\in_array($function, $this->allowedFunctions) && !str_starts_with($function, 'pimcore_')) {
                throw new SecurityNotAllowedFunctionError(sprintf('Function "%s" is not allowed.', $function), $function);
            }
        }
    }

    public function checkMethodAllowed($obj, $method): void
    {
        //do not perform any checks
    }

    public function checkPropertyAllowed($obj, $method): void
    {
        //do not perform any checks
    }
}
use Pimcore\Logger;
use Twig\Sandbox\SecurityError;
        try {
            $twig = $templatingEngine->getTwigEnvironment(true);
            $template = $twig->createTemplate($this->html);
            $this->html = $template->render(array_merge($context,
                [
                    'object' => $object,
                ]
            ));
        } catch (SecurityError $e) {
            Logger::err((string) $e);

            $this->html = sprintf("<h2>Error</h2>Failed rendering the template: <b>%s</b>.
                Please check your twig sandbox security policy or contact the administrator.",
                substr($e->getMessage(),0, strpos($e->getMessage(), ' in "__string')));
        } finally {
            $templatingEngine->disableSandboxExtensionFromTwigEnvironment();
        }