diff --git a/Neos.Media.Browser/Classes/Controller/AssetController.php b/Neos.Media.Browser/Classes/Controller/AssetController.php
index 7f78d961061..f15304dfa1b 100644
--- a/Neos.Media.Browser/Classes/Controller/AssetController.php
+++ b/Neos.Media.Browser/Classes/Controller/AssetController.php
@@ -14,6 +14,7 @@
use Doctrine\Common\Persistence\Proxy as DoctrineProxy;
use Doctrine\ORM\EntityNotFoundException;
+use enshrined\svgSanitize\Sanitizer;
use Neos\Error\Messages\Error;
use Neos\Error\Messages\Message;
use Neos\Flow\Annotations as Flow;
@@ -35,6 +36,7 @@
use Neos\Media\Domain\Model\AssetCollection;
use Neos\Media\Domain\Model\AssetInterface;
use Neos\Media\Domain\Model\AssetSource\AssetNotFoundExceptionInterface;
+use Neos\Media\Domain\Model\AssetSource\AssetProxy\AssetProxyInterface;
use Neos\Media\Domain\Model\AssetSource\AssetProxyRepositoryInterface;
use Neos\Media\Domain\Model\AssetSource\AssetSourceConnectionExceptionInterface;
use Neos\Media\Domain\Model\AssetSource\AssetSourceInterface;
@@ -370,7 +372,8 @@ public function showAction(string $assetSourceIdentifier, string $assetProxyIden
$this->view->assignMultiple([
'assetProxy' => $assetProxy,
- 'assetCollections' => $this->assetCollectionRepository->findAll()
+ 'assetCollections' => $this->assetCollectionRepository->findAll(),
+ 'assetContainsMaliciousContent' => $this->checkForMaliciousContent($assetProxy)
]);
} catch (AssetNotFoundExceptionInterface | AssetSourceConnectionExceptionInterface $e) {
$this->view->assign('connectionError', $e);
@@ -423,6 +426,7 @@ public function editAction(string $assetSourceIdentifier, string $assetProxyIden
'assetCollections' => $this->assetCollectionRepository->findAll(),
'contentPreview' => $contentPreview,
'assetSource' => $assetSource,
+ 'assetContainsMaliciousContent' => $this->checkForMaliciousContent($assetProxy),
'canShowVariants' => ($assetProxy instanceof NeosAssetProxy) && ($assetProxy->getAsset() instanceof VariantSupportInterface)
]);
} catch (AssetNotFoundExceptionInterface | AssetSourceConnectionExceptionInterface $e) {
@@ -1022,4 +1026,25 @@ private function forwardWithConstraints(string $actionName, string $controllerNa
}
$this->forward($actionName, $controllerName, null, $arguments);
}
+
+ private function checkForMaliciousContent(AssetProxyInterface $assetProxy): bool
+ {
+ if ($assetProxy->getMediaType() == 'image/svg+xml') {
+ // @todo: Simplify again when https://github.com/darylldoyle/svg-sanitizer/pull/90 is merged and released.
+ $previousXmlErrorHandling = libxml_use_internal_errors(true);
+ $sanitizer = new Sanitizer();
+
+ $resource = stream_get_contents($assetProxy->getImportStream());
+
+ $sanitizer->sanitize($resource);
+ libxml_clear_errors();
+ libxml_use_internal_errors($previousXmlErrorHandling);
+ $issues = $sanitizer->getXmlIssues();
+ if ($issues && count($issues) > 0) {
+ return true;
+ }
+ }
+
+ return false;
+ }
}
diff --git a/Neos.Media.Browser/Resources/Private/Partials/ContentDefaultPreview.html b/Neos.Media.Browser/Resources/Private/Partials/ContentDefaultPreview.html
index f32baac89dd..3f0963cdf42 100644
--- a/Neos.Media.Browser/Resources/Private/Partials/ContentDefaultPreview.html
+++ b/Neos.Media.Browser/Resources/Private/Partials/ContentDefaultPreview.html
@@ -1,7 +1,14 @@
{namespace m=Neos\Media\ViewHelpers}
{namespace neos=Neos\Neos\ViewHelpers}
diff --git a/Neos.Media.Browser/Resources/Private/Templates/Asset/Edit.html b/Neos.Media.Browser/Resources/Private/Templates/Asset/Edit.html
index f40c47e7df1..06ec12fa5d7 100644
--- a/Neos.Media.Browser/Resources/Private/Templates/Asset/Edit.html
+++ b/Neos.Media.Browser/Resources/Private/Templates/Asset/Edit.html
@@ -78,7 +78,19 @@ {neos:backend.translate(id: 'connectionError', package: 'Neos.Media.Browser'
{neos:backend.translate(id: 'metadata.filename', package: 'Neos.Media.Browser')} |
- {assetProxy.filename} |
+
+
+
+ {assetProxy.filename}
+
+ {neos:backend.translate(id: 'message.assetContainsMaliciousContent', package: 'Neos.Media.Browser')}
+
+
+
+ {assetProxy.filename}
+
+
+ |
{neos:backend.translate(id: 'metadata.lastModified', package: 'Neos.Media.Browser')} |
diff --git a/Neos.Media.Browser/Resources/Private/Templates/Asset/Show.html b/Neos.Media.Browser/Resources/Private/Templates/Asset/Show.html
index 0375e584638..c74719983bb 100644
--- a/Neos.Media.Browser/Resources/Private/Templates/Asset/Show.html
+++ b/Neos.Media.Browser/Resources/Private/Templates/Asset/Show.html
@@ -39,7 +39,19 @@
{neos:backend.translate(id: 'metadata.filename', package: 'Neos.Media.Browser')} |
- {assetProxy.filename} |
+
+
+
+ {assetProxy.filename}
+
+ {neos:backend.translate(id: 'message.assetContainsMaliciousContent', package: 'Neos.Media.Browser')}
+
+
+
+ {assetProxy.filename}
+
+
+ |
{neos:backend.translate(id: 'metadata.lastModified', package: 'Neos.Media.Browser')} |
@@ -85,9 +97,16 @@
diff --git a/Neos.Media.Browser/Resources/Private/Translations/en/Main.xlf b/Neos.Media.Browser/Resources/Private/Translations/en/Main.xlf
index 07f3354ed02..1dd0ef007a1 100644
--- a/Neos.Media.Browser/Resources/Private/Translations/en/Main.xlf
+++ b/Neos.Media.Browser/Resources/Private/Translations/en/Main.xlf
@@ -101,6 +101,9 @@
+
+
+
diff --git a/Neos.Media.Browser/composer.json b/Neos.Media.Browser/composer.json
index 85edd1fe87f..27ef651e188 100644
--- a/Neos.Media.Browser/composer.json
+++ b/Neos.Media.Browser/composer.json
@@ -12,6 +12,7 @@
],
"require": {
"php": "^8.2",
+ "ext-libxml": "*",
"neos/media": "self.version",
"neos/contentrepository-core": "self.version",
"neos/neos": "self.version",
@@ -22,7 +23,8 @@
"neos/utility-mediatypes": "*",
"neos/error-messages": "*",
"doctrine/common": "^2.7 || ^3.0",
- "doctrine/orm": "^2.6"
+ "doctrine/orm": "^2.6",
+ "enshrined/svg-sanitize": "^0.16.0"
},
"autoload": {
"psr-4": {
diff --git a/Neos.Neos/Classes/Controller/Module/Management/WorkspacesController.php b/Neos.Neos/Classes/Controller/Module/Management/WorkspacesController.php
index ec474622f5c..2e0ce705dd3 100644
--- a/Neos.Neos/Classes/Controller/Module/Management/WorkspacesController.php
+++ b/Neos.Neos/Classes/Controller/Module/Management/WorkspacesController.php
@@ -936,8 +936,8 @@ protected function renderContentChanges(
'diff' => $diffArray
];
}
- // The && in belows condition is on purpose as creating a thumbnail for comparison only works
- // if actually BOTH are ImageInterface (or NULL).
+ // The && in belows condition is on purpose as creating a thumbnail for comparison only works
+ // if actually BOTH are ImageInterface (or NULL).
} elseif (
($originalPropertyValue instanceof ImageInterface || $originalPropertyValue === null)
&& ($changedPropertyValue instanceof ImageInterface || $changedPropertyValue === null)
diff --git a/Neos.Neos/Migrations/Postgresql/Version20230727164600.php b/Neos.Neos/Migrations/Postgresql/Version20230727164600.php
new file mode 100644
index 00000000000..e584f6e34a3
--- /dev/null
+++ b/Neos.Neos/Migrations/Postgresql/Version20230727164600.php
@@ -0,0 +1,57 @@
+abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on "postgresql".');
+
+ $this->addSql('ALTER TABLE neos_neos_eventlog_domain_model_event ADD dimensionshash VARCHAR(32) DEFAULT NULL');
+ $this->addSql('CREATE INDEX dimensionshash ON neos_neos_eventlog_domain_model_event (dimensionshash)');
+ }
+
+ /**
+ * @param Schema $schema
+ * @throws \Doctrine\DBAL\Exception
+ */
+ public function down(Schema $schema) : void
+ {
+ $this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on "postgresql".');
+
+ $this->addSql('DROP INDEX dimensionshash');
+ $this->addSql('ALTER TABLE neos_neos_eventlog_domain_model_event DROP dimensionshash');
+ }
+
+ /**
+ * @param Schema $schema
+ * @throws \Doctrine\DBAL\Driver\Exception
+ * @throws \Doctrine\DBAL\Exception
+ */
+ public function postUp(Schema $schema): void
+ {
+ $eventLogResult = $this->connection->executeQuery('SELECT dimension FROM neos_neos_eventlog_domain_model_event where dimensionshash IS NULL AND dimension IS NOT NULL LIMIT 1');
+
+ while ($eventLogInfo = $eventLogResult->fetchAssociative()) {
+ $dimensionsArray = unserialize($eventLogInfo['dimension'], ['allowed_classes' => false]);
+ $dimensionsHash = Utility::sortDimensionValueArrayAndReturnDimensionsHash($dimensionsArray);
+ $this->connection->executeStatement('UPDATE neos_neos_eventlog_domain_model_event SET dimensionshash = ? WHERE dimension = ?', [$dimensionsHash, $eventLogInfo['dimension']]);
+ $eventLogResult = $this->connection->executeQuery('SELECT dimension FROM neos_neos_eventlog_domain_model_event where dimensionshash IS NULL AND dimension IS NOT NULL LIMIT 1');
+ }
+ }
+}
diff --git a/composer.json b/composer.json
index 6b9142bcea2..8f8f059a8e5 100644
--- a/composer.json
+++ b/composer.json
@@ -42,7 +42,8 @@
"neos/party": "~7.0.3",
"neos/fusion-form": "^1.0 || ^2.0",
"neos/form": "*",
- "neos/kickstarter": "~9.0.0"
+ "neos/kickstarter": "~9.0.0",
+ "enshrined/svg-sanitize": "^0.16.0"
},
"replace": {
"packagefactory/atomicfusion-afx": "*",