VYPR
Moderate severityNVD Advisory· Published Jan 4, 2022· Updated Apr 23, 2025

XSS vulnerability in oro/platform

CVE-2021-41236

Description

OroPlatform email template preview is vulnerable to stored XSS, allowing attackers with edit permissions to execute arbitrary JavaScript when a victim previews the template.

AI Insight

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

OroPlatform email template preview is vulnerable to stored XSS, allowing attackers with edit permissions to execute arbitrary JavaScript when a victim previews the template.

Vulnerability

The email template preview functionality in OroPlatform is vulnerable to stored cross-site scripting (XSS). An attacker with permission to create or edit email templates can inject malicious JavaScript into the template content. When a user with sufficient privileges previews the template, the payload executes. The vulnerability exists in all versions prior to the commit that introduced sanitization (2a089c9) [1][2][3].

Exploitation

An attacker must be authenticated and have the 'create' or 'edit' permission on email templates. They craft an email template containing a malicious XSS payload. The attacker then needs to convince another user (e.g., an administrator) to preview that template. The payload executes in the victim's browser within the context of the OroPlatform application [1][3].

Impact

Successful exploitation allows the attacker to execute arbitrary JavaScript in the victim's session. This can lead to data theft, session hijacking, or unauthorized actions performed on behalf of the victim. The impact is on confidentiality and integrity, potentially escalating the attacker's privileges to those of the victim [1][3].

Mitigation

The fix is available in commit 2a089c9 which introduces HTML sanitization for email template content before rendering. Users are advised to upgrade to a version containing this fix as soon as possible. No workarounds are available [2][3]. The vulnerability is not listed on the CISA Known Exploited Vulnerabilities (KEV) catalog.

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
oro/platformPackagist
>= 3.1.0, < 3.1.213.1.21
oro/platformPackagist
>= 4.1.0, < 4.1.144.1.14
oro/platformPackagist
>= 4.2.0, < 4.2.84.2.8

Affected products

2

Patches

1
2a089c971fc7

BAP-20848: Unwanted script execution possible in email template preview (#31379)

https://github.com/oroinc/platformYefim YevtushenkoDec 20, 2021via ghsa
3 files changed · +50 48
  • src/Oro/Bundle/EmailBundle/Provider/EmailRenderer.php+16 1 modified
    @@ -7,6 +7,7 @@
     use Oro\Bundle\EntityBundle\Twig\Sandbox\TemplateRenderer;
     use Oro\Bundle\EntityBundle\Twig\Sandbox\TemplateRendererConfigProviderInterface;
     use Oro\Bundle\EntityBundle\Twig\Sandbox\VariableProcessorRegistry;
    +use Oro\Bundle\UIBundle\Tools\HtmlTagHelper;
     use Symfony\Contracts\Translation\TranslatorInterface;
     use Twig\Environment;
     
    @@ -20,6 +21,9 @@ class EmailRenderer extends TemplateRenderer
         /** @var TranslatorInterface */
         private $translator;
     
    +    /** @var HtmlTagHelper|null */
    +    private $htmlTagHelper;
    +
         public function __construct(
             Environment $environment,
             TemplateRendererConfigProviderInterface $configProvider,
    @@ -31,6 +35,11 @@ public function __construct(
             $this->translator = $translator;
         }
     
    +    public function setHtmlTagHelper(HtmlTagHelper $htmlTagHelper): void
    +    {
    +        $this->htmlTagHelper = $htmlTagHelper;
    +    }
    +
         /**
          * Compiles the given email template.
          *
    @@ -61,8 +70,14 @@ public function compilePreview(EmailTemplateInterface $template): string
         {
             $this->ensureSandboxConfigured();
     
    +        if ($this->htmlTagHelper) {
    +            $content = $this->htmlTagHelper->sanitize($template->getContent(), 'default', false);
    +        } else {
    +            $content = $template->getContent();
    +        }
    +
             return $this->environment
    -            ->createTemplate('{% verbatim %}' . $template->getContent() . '{% endverbatim %}')
    +            ->createTemplate('{% verbatim %}' . $content . '{% endverbatim %}')
                 ->render();
         }
     
    
  • src/Oro/Bundle/EmailBundle/Resources/config/services.yml+1 0 modified
    @@ -338,6 +338,7 @@ services:
                 - '@Doctrine\Inflector\Inflector'
             calls:
                 - [addSystemVariableDefaultFilter, ['userSignature', 'oro_html_sanitize']]
    +            - [setHtmlTagHelper, ['@oro_ui.html_tag_helper']]
             lazy: true
     
         oro_email.email_renderer_configuration:
    
  • src/Oro/Bundle/EmailBundle/Tests/Unit/Provider/EmailRendererTest.php+33 47 modified
    @@ -22,22 +22,9 @@
     
     class EmailRendererTest extends \PHPUnit\Framework\TestCase
     {
    -    private const ENTITY_VARIABLE_TEMPLATE =
    -        '{% if %val% is defined %}'
    -        . '{{ _entity_var("%name%", %val%, %parent%) }}'
    -        . '{% else %}'
    -        . '{{ "oro.email.variable.not.found" }}'
    -        . '{% endif %}';
    -
         /** @var TemplateRendererConfigProviderInterface|\PHPUnit\Framework\MockObject\MockObject */
         private $configProvider;
     
    -    /** @var VariableProcessorRegistry|\PHPUnit\Framework\MockObject\MockObject */
    -    private $variablesProcessorRegistry;
    -
    -    /** @var TranslatorInterface|\PHPUnit\Framework\MockObject\MockObject */
    -    private $translation;
    -
         /** @var ContainerInterface|\PHPUnit\Framework\MockObject\MockObject */
         private $container;
     
    @@ -46,27 +33,38 @@ class EmailRendererTest extends \PHPUnit\Framework\TestCase
     
         protected function setUp(): void
         {
    -        $environment = new Environment(new ArrayLoader(), ['strict_variables' => true]);
             $this->configProvider = $this->createMock(TemplateRendererConfigProviderInterface::class);
    -        $this->variablesProcessorRegistry = $this->createMock(VariableProcessorRegistry::class);
    -        $this->translation = $this->createMock(TranslatorInterface::class);
             $this->container = $this->createMock(ContainerInterface::class);
     
    -        $this->translation->expects($this->any())
    +        $variablesProcessorRegistry = $this->createMock(VariableProcessorRegistry::class);
    +
    +        $htmlTagHelper = $this->createMock(HtmlTagHelper::class);
    +        $htmlTagHelper->expects(self::any())
    +            ->method('sanitize')
    +            ->with(self::isType(\PHPUnit\Framework\Constraint\IsType::TYPE_STRING), 'default', false)
    +            ->willReturnCallback(static function (string $content) {
    +                return $content . '(sanitized)';
    +            });
    +
    +        $translation = $this->createMock(TranslatorInterface::class);
    +        $translation->expects(self::any())
                 ->method('trans')
                 ->willReturnArgument(0);
     
    +        $environment = new Environment(new ArrayLoader(), ['strict_variables' => true]);
             $environment->addExtension(new SandboxExtension(new SecurityPolicy()));
             $environment->addExtension(new HttpKernelExtension());
             $environment->addExtension(new HtmlTagExtension($this->container));
     
             $this->renderer = new EmailRenderer(
                 $environment,
                 $this->configProvider,
    -            $this->variablesProcessorRegistry,
    -            $this->translation,
    +            $variablesProcessorRegistry,
    +            $translation,
                 (new InflectorFactory())->build()
             );
    +
    +        $this->renderer->setHtmlTagHelper($htmlTagHelper);
         }
     
         private function getEmailTemplate(string $content, string $subject = ''): EmailTemplateInterface
    @@ -78,37 +76,25 @@ private function getEmailTemplate(string $content, string $subject = ''): EmailT
             return $emailTemplate;
         }
     
    -    private function getEntityVariableTemplate(string $propertyName, string $path, string $parentPath): string
    -    {
    -        return strtr(
    -            self::ENTITY_VARIABLE_TEMPLATE,
    -            [
    -                '%name%' => $propertyName,
    -                '%val%' => $path,
    -                '%parent%' => $parentPath,
    -            ]
    -        );
    -    }
    -
         private function expectVariables(array $entityVariableProcessors = [], array $systemVariableValues = []): void
         {
             $entityVariableProcessorsMap = [];
             foreach ($entityVariableProcessors as $entityClass => $processors) {
                 $entityVariableProcessorsMap[] = [$entityClass, $processors];
             }
    -        $this->configProvider->expects($this->any())
    +        $this->configProvider->expects(self::any())
                 ->method('getEntityVariableProcessors')
                 ->willReturnMap($entityVariableProcessorsMap);
    -        $this->configProvider->expects($this->any())
    +        $this->configProvider->expects(self::any())
                 ->method('getSystemVariableValues')
                 ->willReturn($systemVariableValues);
         }
     
    -    public function testCompilePreview()
    +    public function testCompilePreview(): void
         {
             $entity = new TestEntityForVariableProvider();
     
    -        $this->configProvider->expects($this->any())
    +        $this->configProvider->expects(self::any())
                 ->method('getConfiguration')
                 ->willReturn([
                     'properties' => [],
    @@ -123,10 +109,10 @@ public function testCompilePreview()
             $emailTemplate->setContent($template);
             $emailTemplate->setSubject('');
     
    -        $this->assertSame($template, $this->renderer->compilePreview($emailTemplate));
    +        self::assertSame($template.'(sanitized)', $this->renderer->compilePreview($emailTemplate));
         }
     
    -    public function testCompileMessage()
    +    public function testCompileMessage(): void
         {
             $entity = new TestEntityForVariableProvider();
             $entity->setField1('Test');
    @@ -144,7 +130,7 @@ public function testCompileMessage()
                 . ' {{ system.testVar }}'
                 . ' N/A';
     
    -        $this->configProvider->expects($this->any())
    +        $this->configProvider->expects(self::any())
                 ->method('getConfiguration')
                 ->willReturn([
                     'properties' => [],
    @@ -155,7 +141,7 @@ public function testCompileMessage()
             $this->expectVariables($entityVariableProcessors, $systemVars);
     
             $htmlTagHelper = $this->createMock(HtmlTagHelper::class);
    -        $this->container->expects($this->once())
    +        $this->container->expects(self::once())
                 ->method('get')
                 ->with('oro_ui.html_tag_helper')
                 ->willReturn($htmlTagHelper);
    @@ -169,13 +155,13 @@ public function testCompileMessage()
             $result = $this->renderer->compileMessage($emailTemplate, $templateParams);
             $expectedContent = 'test <a href="http://example.com">test</a>   2 test_system N/A';
     
    -        $this->assertIsArray($result);
    -        $this->assertCount(2, $result);
    -        $this->assertSame($subject, $result[0]);
    -        $this->assertSame($expectedContent, $result[1]);
    +        self::assertIsArray($result);
    +        self::assertCount(2, $result);
    +        self::assertSame($subject, $result[0]);
    +        self::assertSame($expectedContent, $result[1]);
         }
     
    -    public function testRenderTemplate()
    +    public function testRenderTemplate(): void
         {
             $template = 'test '
                 . "\n"
    @@ -184,7 +170,7 @@ public function testRenderTemplate()
                 . '{{ system.currentDate }}';
     
             $entity = new TestEntityForVariableProvider();
    -        $this->configProvider->expects($this->any())
    +        $this->configProvider->expects(self::any())
                 ->method('getConfiguration')
                 ->willReturn([
                     'properties' => [],
    @@ -197,7 +183,7 @@ public function testRenderTemplate()
             ], ['currentDate' => '10-12-2019']);
     
             $htmlTagHelper = $this->createMock(HtmlTagHelper::class);
    -        $this->container->expects($this->once())
    +        $this->container->expects(self::once())
                 ->method('get')
                 ->with('oro_ui.html_tag_helper')
                 ->willReturn($htmlTagHelper);
    @@ -208,6 +194,6 @@ public function testRenderTemplate()
                 'test '
                 . "\n"
                 . '10-12-2019';
    -        $this->assertSame($expectedRenderedResult, $result);
    +        self::assertSame($expectedRenderedResult, $result);
         }
     }
    

Vulnerability mechanics

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

References

4

News mentions

0

No linked articles in our index yet.