Critical severityNVD Advisory· Published Oct 27, 2022· Updated Apr 23, 2025
RCE vulnerability in Pimcore/Mail & Dynamic Text Layout
CVE-2022-39365
Description
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.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
pimcore/pimcorePackagist | < 10.5.9 | 10.5.9 |
Affected products
1Patches
143aa34e018f5[Mail] Renderer email content twig templates in a sandbox (#13347)
11 files changed · +238 −28
bundles/CoreBundle/DependencyInjection/Configuration.php+31 −0 modified@@ -180,6 +180,7 @@ public function getConfigTreeBuilder(): TreeBuilder $this->addCustomViewsNode($rootNode); $this->addGlossaryNode($rootNode); $this->buildRedirectsStatusCodes($rootNode); + $this->addTemplatingEngineNode($rootNode); return $treeBuilder; } @@ -2276,4 +2277,34 @@ private function addGlossaryNode(ArrayNodeDefinition $rootNode): void ->useAttributeAsKey('name') ->prototype('scalar'); } + + 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(); + } }
bundles/CoreBundle/DependencyInjection/PimcoreCoreExtension.php+5 −0 modified@@ -95,6 +95,11 @@ public function loadInternal(array $config, ContainerBuilder $container) $container->setParameter('pimcore.documents.web_to_print.default_controller_print_page', $config['documents']['web_to_print']['default_controller_print_page']); $container->setParameter('pimcore.documents.web_to_print.default_controller_print_container', $config['documents']['web_to_print']['default_controller_print_container']); + //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']); + // register pimcore config on container // TODO is this bad practice? // TODO only extract what we need as parameter?
bundles/CoreBundle/Resources/config/pimcore/default.yaml+6 −0 modified@@ -293,6 +293,12 @@ pimcore: 'abbr', 'option', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' ] + templating_engine: + twig: + sandbox_security_policy: + tags: ['set'] + filters: ['escape', 'trans'] + functions: ['path', 'asset'] presta_sitemap: # do not add properties by default defaults:
bundles/CoreBundle/Resources/config/templating_twig.yaml+11 −0 modified@@ -84,4 +84,15 @@ services: Twig\Extra\String\StringExtension: ~ + 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' + Pimcore\Twig\Extension\AdminExtension: ~
doc/Development_Documentation/05_Objects/01_Object_Classes/03_Layout_Elements/01_Dynamic_Text_Labels.md+14 −0 modified@@ -67,3 +67,17 @@ Here is an example of Twig content in htmleditor source edit mode:   + +### 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'] +``` \ No newline at end of file
doc/Development_Documentation/19_Development_Tools_and_Details/25_Email_Framework/README.md+14 −0 modified@@ -98,3 +98,17 @@ $mail->bcc("bcc@pimcore.org"); $mail->html("<b>some</b> rich text"); $mail->send(); ``` + +## 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'] +``` \ No newline at end of file
doc/Development_Documentation/23_Installation_and_Upgrade/09_Upgrade_Notes/README.md+12 −0 modified@@ -1,6 +1,18 @@ # Upgrade Notes ## 10.5.8 +- [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'] + ``` + - [Nginx] Static pages nginx config has been updated to fix the issue for home static page generation. please adapt the following configuration: ```nginx map $args $static_page_root {
lib/Mail.php+18 −8 modified@@ -28,6 +28,7 @@ use Symfony\Component\Mime\Header\Headers; use Symfony\Component\Mime\Header\MailboxListHeader; use Symfony\Component\Mime\Part\AbstractPart; +use Twig\Sandbox\SecurityError; class Mail extends Email { @@ -651,13 +652,22 @@ private function getDebugMailRecipients(array $recipients): array * * @return string */ - private function renderParams(string $string): string + private function renderParams(string $string, string $context): string { $templatingEngine = \Pimcore::getContainer()->get('pimcore.templating.engine.delegating'); - $twig = $templatingEngine->getTwigEnvironment(); - $template = $twig->createTemplate($string); - - return $template->render($this->getParams()); + 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(); + } } /** @@ -676,7 +686,7 @@ public function getSubjectRendered() } if ($subject) { - return $this->renderParams($subject); + return $this->renderParams($subject, 'subject'); } return ''; @@ -707,7 +717,7 @@ public function getBodyHtmlRendered() $content = null; if ($html) { - $content = $this->renderParams($html); + $content = $this->renderParams($html, 'body'); // modifying the content e.g set absolute urls... $content = MailHelper::embedAndModifyCss($content, $this->getDocument()); @@ -731,7 +741,7 @@ public function getBodyTextRendered() //if the content was manually set with $obj->text(); this content will be used if ($text) { - $content = $this->renderParams($text); + $content = $this->renderParams($text, 'body'); } else { //creating text version from html email try {
lib/Templating/TwigDefaultDelegatingEngine.php+17 −13 modified@@ -15,34 +15,28 @@ namespace Pimcore\Templating; +use Pimcore\Config; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Templating\DelegatingEngine as BaseDelegatingEngine; use Symfony\Component\Templating\EngineInterface; use Twig\Environment; +use Twig\Extension\SandboxExtension; /** * @internal */ class TwigDefaultDelegatingEngine extends BaseDelegatingEngine { - /** - * @var Environment - */ - protected $twig; - /** * @var bool */ protected $delegate = false; /** - * @param Environment $twig * @param EngineInterface[] $engines */ - public function __construct(Environment $twig, array $engines = []) + public function __construct(protected Environment $twig, protected Config $config, array $engines = []) { - $this->twig = $twig; - parent::__construct($engines); } @@ -100,14 +94,24 @@ public function isDelegate() return $this->delegate; } - /** - * @return Environment - */ - public function getTwigEnvironment(): Environment + public function getTwigEnvironment(bool $sandboxed = false): Environment { + if ($sandboxed) { + /** @var SandboxExtension $sandboxExtension */ + $sandboxExtension = $this->twig->getExtension(SandboxExtension::class); + $sandboxExtension->enableSandbox(); + } + return $this->twig; } + public function disableSandboxExtensionFromTwigEnvironment(): void + { + /** @var SandboxExtension $sandboxExtension */ + $sandboxExtension = $this->twig->getExtension(SandboxExtension::class); + $sandboxExtension->disableSandbox(); + } + /** * @param string $view * @param array $parameters
lib/Twig/Sandbox/SecurityPolicy.php+91 −0 added@@ -0,0 +1,91 @@ +<?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 + } +}
models/DataObject/ClassDefinition/Layout/Text.php+19 −7 modified@@ -15,8 +15,10 @@ namespace Pimcore\Model\DataObject\ClassDefinition\Layout; +use Pimcore\Logger; use Pimcore\Model; use Pimcore\Model\DataObject\Concrete; +use Twig\Sandbox\SecurityError; class Text extends Model\DataObject\ClassDefinition\Layout implements Model\DataObject\ClassDefinition\Data\LayoutDefinitionEnrichmentInterface { @@ -143,13 +145,23 @@ public function enrichLayoutDefinition(/* ?Concrete */ $object, /* array */ $con } $templatingEngine = \Pimcore::getContainer()->get('pimcore.templating.engine.delegating'); - $twig = $templatingEngine->getTwigEnvironment(); - $template = $twig->createTemplate($this->html); - $this->html = $template->render(array_merge($context, - [ - 'object' => $object, - ] - )); + 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(); + } return $this; }
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
6- github.com/advisories/GHSA-5qxq-vgmm-q39mghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2022-39365ghsaADVISORY
- github.com/pimcore/pimcore/commit/43aa34e018f5cd447bceb864358285ba92f68372ghsaWEB
- github.com/pimcore/pimcore/pull/13347ghsaWEB
- github.com/pimcore/pimcore/pull/13347.patchghsaWEB
- github.com/pimcore/pimcore/security/advisories/GHSA-5qxq-vgmm-q39mghsaWEB
News mentions
0No linked articles in our index yet.