Skip to content

Commit 2c1e036

Browse files
author
ogorkun
committed
MC-34385: Filter fields allowing HTML
1 parent 278e422 commit 2c1e036

File tree

6 files changed

+146
-9
lines changed

6 files changed

+146
-9
lines changed

app/code/Magento/Cms/Model/BlockRepository.php

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,13 @@
1212
use Magento\Cms\Model\ResourceModel\Block\CollectionFactory as BlockCollectionFactory;
1313
use Magento\Framework\Api\DataObjectHelper;
1414
use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface;
15+
use Magento\Framework\App\ObjectManager;
1516
use Magento\Framework\Exception\CouldNotDeleteException;
1617
use Magento\Framework\Exception\CouldNotSaveException;
1718
use Magento\Framework\Exception\NoSuchEntityException;
1819
use Magento\Framework\Reflection\DataObjectProcessor;
20+
use Magento\Framework\Validation\ValidationException;
21+
use Magento\Framework\Validator\HTML\WYSIWYGValidatorInterface;
1922
use Magento\Store\Model\StoreManagerInterface;
2023

2124
/**
@@ -69,6 +72,11 @@ class BlockRepository implements BlockRepositoryInterface
6972
*/
7073
private $collectionProcessor;
7174

75+
/**
76+
* @var WYSIWYGValidatorInterface
77+
*/
78+
private $wysiwygValidator;
79+
7280
/**
7381
* @param ResourceBlock $resource
7482
* @param BlockFactory $blockFactory
@@ -79,6 +87,7 @@ class BlockRepository implements BlockRepositoryInterface
7987
* @param DataObjectProcessor $dataObjectProcessor
8088
* @param StoreManagerInterface $storeManager
8189
* @param CollectionProcessorInterface $collectionProcessor
90+
* @param WYSIWYGValidatorInterface|null $wysiwygValidator
8291
*/
8392
public function __construct(
8493
ResourceBlock $resource,
@@ -89,7 +98,8 @@ public function __construct(
8998
DataObjectHelper $dataObjectHelper,
9099
DataObjectProcessor $dataObjectProcessor,
91100
StoreManagerInterface $storeManager,
92-
CollectionProcessorInterface $collectionProcessor = null
101+
CollectionProcessorInterface $collectionProcessor = null,
102+
?WYSIWYGValidatorInterface $wysiwygValidator = null
93103
) {
94104
$this->resource = $resource;
95105
$this->blockFactory = $blockFactory;
@@ -100,13 +110,46 @@ public function __construct(
100110
$this->dataObjectProcessor = $dataObjectProcessor;
101111
$this->storeManager = $storeManager;
102112
$this->collectionProcessor = $collectionProcessor ?: $this->getCollectionProcessor();
113+
$this->wysiwygValidator = $wysiwygValidator
114+
?? ObjectManager::getInstance()->get(WYSIWYGValidatorInterface::class);
115+
}
116+
117+
/**
118+
* Validate block's content.
119+
*
120+
* @param Data\BlockInterface|Block $block
121+
* @throws CouldNotSaveException
122+
* @return void
123+
*/
124+
private function validateHtml(Data\BlockInterface $block): void
125+
{
126+
$oldContent = null;
127+
if ($block->getId()) {
128+
if ($block instanceof Block && $block->getOrigData()) {
129+
$oldContent = $block->getOrigData(Data\BlockInterface::CONTENT);
130+
} else {
131+
$oldBlock = $this->getById($block->getId());
132+
$oldContent = $oldBlock->getContent();
133+
}
134+
}
135+
if ($block->getContent() && $block->getContent() !== $oldContent) {
136+
//Validate HTML content.
137+
try {
138+
$this->wysiwygValidator->validate($block->getContent());
139+
} catch (ValidationException $exception) {
140+
throw new CouldNotSaveException(
141+
__('Content HTML has restricted elements. %1', $exception->getMessage()),
142+
$exception
143+
);
144+
}
145+
}
103146
}
104147

105148
/**
106149
* Save Block data
107150
*
108151
* @param \Magento\Cms\Api\Data\BlockInterface $block
109-
* @return Block
152+
* @return Block|Data\BlockInterface
110153
* @throws CouldNotSaveException
111154
*/
112155
public function save(Data\BlockInterface $block)
@@ -115,6 +158,8 @@ public function save(Data\BlockInterface $block)
115158
$block->setStoreId($this->storeManager->getStore()->getId());
116159
}
117160

161+
$this->validateHtml($block);
162+
118163
try {
119164
$this->resource->save($block);
120165
} catch (\Exception $exception) {

app/code/Magento/Cms/Model/PageRepository.php

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -133,15 +133,21 @@ public function __construct(
133133
private function validateLayoutUpdate(Data\PageInterface $page): void
134134
{
135135
//Persisted data
136-
$savedPage = $page->getId() ? $this->getById($page->getId()) : null;
136+
$oldData = null;
137+
if ($page->getId() && $page instanceof Page) {
138+
$oldData = $page->getOrigData();
139+
}
137140
//Custom layout update can be removed or kept as is.
138141
if ($page->getCustomLayoutUpdateXml()
139-
&& (!$savedPage || $page->getCustomLayoutUpdateXml() !== $savedPage->getCustomLayoutUpdateXml())
142+
&& (
143+
!$oldData
144+
|| $page->getCustomLayoutUpdateXml() !== $oldData[Data\PageInterface::CUSTOM_LAYOUT_UPDATE_XML]
145+
)
140146
) {
141147
throw new \InvalidArgumentException('Custom layout updates must be selected from a file');
142148
}
143149
if ($page->getLayoutUpdateXml()
144-
&& (!$savedPage || $page->getLayoutUpdateXml() !== $savedPage->getLayoutUpdateXml())
150+
&& (!$oldData || $page->getLayoutUpdateXml() !== $oldData[Data\PageInterface::LAYOUT_UPDATE_XML])
145151
) {
146152
throw new \InvalidArgumentException('Custom layout updates must be selected from a file');
147153
}
@@ -161,12 +167,12 @@ public function save(\Magento\Cms\Api\Data\PageInterface $page)
161167
$page->setStoreId($storeId);
162168
}
163169
$pageId = $page->getId();
170+
if ($pageId && !($page instanceof Page && $page->getOrigData())) {
171+
$page = $this->hydrator->hydrate($this->getById($pageId), $this->hydrator->extract($page));
172+
}
164173

165174
try {
166175
$this->validateLayoutUpdate($page);
167-
if ($pageId) {
168-
$page = $this->hydrator->hydrate($this->getById($pageId), $this->hydrator->extract($page));
169-
}
170176
$this->resource->save($page);
171177
$this->identityMap->add($page);
172178
} catch (\Exception $exception) {

app/code/Magento/Cms/Model/PageRepository/ValidationComposite.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
use Magento\Cms\Api\Data\PageInterface;
1212
use Magento\Cms\Api\PageRepositoryInterface;
1313
use Magento\Framework\Api\SearchCriteriaInterface;
14+
use Magento\Framework\App\ObjectManager;
15+
use Magento\Framework\EntityManager\HydratorInterface;
1416

1517
/**
1618
* Validates and saves a page
@@ -27,13 +29,20 @@ class ValidationComposite implements PageRepositoryInterface
2729
*/
2830
private $validators;
2931

32+
/**
33+
* @var HydratorInterface
34+
*/
35+
private $hydrator;
36+
3037
/**
3138
* @param PageRepositoryInterface $repository
3239
* @param ValidatorInterface[] $validators
40+
* @param HydratorInterface|null $hydrator
3341
*/
3442
public function __construct(
3543
PageRepositoryInterface $repository,
36-
array $validators = []
44+
array $validators = [],
45+
?HydratorInterface $hydrator = null
3746
) {
3847
foreach ($validators as $validator) {
3948
if (!$validator instanceof ValidatorInterface) {
@@ -44,13 +53,17 @@ public function __construct(
4453
}
4554
$this->repository = $repository;
4655
$this->validators = $validators;
56+
$this->hydrator = $hydrator ?? ObjectManager::getInstance()->get(HydratorInterface::class);
4757
}
4858

4959
/**
5060
* @inheritdoc
5161
*/
5262
public function save(PageInterface $page)
5363
{
64+
if ($page->getId()) {
65+
$page = $this->hydrator->hydrate($this->getById($page->getId()), $this->hydrator->extract($page));
66+
}
5467
foreach ($this->validators as $validator) {
5568
$validator->validate($page);
5669
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace Magento\Cms\Model\PageRepository\Validator;
10+
11+
use Magento\Cms\Api\Data\PageInterface;
12+
use Magento\Cms\Model\Page;
13+
use Magento\Cms\Model\PageRepository\ValidatorInterface;
14+
use Magento\Framework\Validation\ValidationException;
15+
use Magento\Framework\Validator\HTML\WYSIWYGValidatorInterface;
16+
17+
/**
18+
* Validates pages' content.
19+
*/
20+
class ContentValidator implements ValidatorInterface
21+
{
22+
23+
/**
24+
* @var WYSIWYGValidatorInterface
25+
*/
26+
private $wysiwygValidator;
27+
28+
/**
29+
* @param WYSIWYGValidatorInterface $wysiwygValidator
30+
*/
31+
public function __construct(WYSIWYGValidatorInterface $wysiwygValidator)
32+
{
33+
$this->wysiwygValidator = $wysiwygValidator;
34+
}
35+
36+
/**
37+
* @inheritDoc
38+
*/
39+
public function validate(PageInterface $page): void
40+
{
41+
$oldValue = null;
42+
if ($page->getId() && $page instanceof Page && $page->getOrigData()) {
43+
$oldValue = $page->getOrigData(PageInterface::CONTENT);
44+
}
45+
46+
if ($page->getContent() && $page->getContent() !== $oldValue) {
47+
try {
48+
$this->wysiwygValidator->validate($page->getContent());
49+
} catch (ValidationException $exception) {
50+
throw new ValidationException(
51+
__('Content HTML contains restricted elements. %1', $exception->getMessage()),
52+
$exception
53+
);
54+
}
55+
}
56+
}
57+
}

app/code/Magento/Cms/etc/di.xml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@
233233
<argument name="repository" xsi:type="object">Magento\Cms\Model\PageRepository</argument>
234234
<argument name="validators" xsi:type="array">
235235
<item name="layout_update" xsi:type="object">Magento\Cms\Model\PageRepository\Validator\LayoutUpdateValidator</item>
236+
<item name="content" xsi:type="object">Magento\Cms\Model\PageRepository\Validator\ContentValidator</item>
236237
</argument>
237238
</arguments>
238239
</type>
@@ -249,4 +250,16 @@
249250
</arguments>
250251
</type>
251252
<preference for="Magento\Framework\Validator\HTML\WYSIWYGValidatorInterface" type="Magento\Cms\Model\Wysiwyg\Validator" />
253+
<type name="Magento\Framework\Console\CommandListInterface">
254+
<arguments>
255+
<argument name="commands" xsi:type="array">
256+
<item name="cms_wysiwyg_restrict" xsi:type="object">Magento\Cms\Command\WysiwygRestrictCommand</item>
257+
</argument>
258+
</arguments>
259+
</type>
260+
<type name="Magento\Cms\Model\PageRepository\Validator\ContentValidator">
261+
<arguments>
262+
<argument name="hydrator" xsi:type="object">Magento\Framework\EntityManager\AbstractModelHydrator</argument>
263+
</arguments>
264+
</type>
252265
</config>

lib/internal/Magento/Framework/Validator/HTML/ConfigurableWYSIWYGValidator.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ function (string $tag) use ($allowedTags): bool {
5656
*/
5757
public function validate(string $content): void
5858
{
59+
if (mb_strlen($content) === 0) {
60+
return;
61+
}
5962
$dom = $this->loadHtml($content);
6063
$xpath = new \DOMXPath($dom);
6164

0 commit comments

Comments
 (0)