From 6708d691034633cf02b1a7e32d79072f07339db7 Mon Sep 17 00:00:00 2001
From: Giannis Economou <gecon@antithesis.gr>
Date: Thu, 6 Jul 2017 14:20:21 +0300
Subject: [PATCH] [BUGFIX] Properly escape "dropzone-target" selector in
 DragUploader.js

We properly escape some characters of "dropzone-target" data attribute,
since it is being used as a CSS selector to insert the dropzone in
our DOM. The "dropzone-target" might contain characters that have a
special meaning in CSS, like for example a dot. Especially the dot
is typical for cases like flexforms fields.

This allows drag and drop file uploads even on such cases (like for
example working drag and drop file uploads in DCE content elements).

Resolves: #81812
Releases: master, 8.7
Change-Id: Ib1f5b5063e390f08436fd3a51978842754b698ef
Reviewed-on: https://review.typo3.org/53416
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Markus Klein <markus.klein@typo3.org>
Tested-by: Markus Klein <markus.klein@typo3.org>
Reviewed-by: Susanne Moog <susanne.moog@typo3.org>
Tested-by: Susanne Moog <susanne.moog@typo3.org>
---
 .../Form/Container/InlineControlContainer.php |  8 ++++---
 .../Public/JavaScript/DragUploader.js         | 11 +++++----
 .../core/Classes/Utility/StringUtility.php    | 14 +++++++++++
 .../Tests/Unit/Utility/StringUtilityTest.php  | 24 +++++++++++++++++++
 4 files changed, 49 insertions(+), 8 deletions(-)

diff --git a/typo3/sysext/backend/Classes/Form/Container/InlineControlContainer.php b/typo3/sysext/backend/Classes/Form/Container/InlineControlContainer.php
index 3110a06a585e..c0272af31404 100644
--- a/typo3/sysext/backend/Classes/Form/Container/InlineControlContainer.php
+++ b/typo3/sysext/backend/Classes/Form/Container/InlineControlContainer.php
@@ -25,6 +25,7 @@ use TYPO3\CMS\Core\Resource\Folder;
 use TYPO3\CMS\Core\Resource\OnlineMedia\Helpers\OnlineMediaHelperRegistry;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Core\Utility\MathUtility;
+use TYPO3\CMS\Core\Utility\StringUtility;
 
 /**
  * Inline element entry container.
@@ -438,8 +439,9 @@ class InlineControlContainer extends AbstractContainer
 
         $foreign_table = $inlineConfiguration['foreign_table'];
         $allowed = $groupFieldConfiguration['allowed'];
-        $objectPrefix = $this->inlineStackProcessor->getCurrentStructureDomObjectIdPrefix($this->data['inlineFirstPid']) . '-' . $foreign_table;
-        $nameObject = $this->inlineStackProcessor->getCurrentStructureDomObjectIdPrefix($this->data['inlineFirstPid']);
+        $currentStructureDomObjectIdPrefix = $this->inlineStackProcessor->getCurrentStructureDomObjectIdPrefix($this->data['inlineFirstPid']);
+        $objectPrefix = $currentStructureDomObjectIdPrefix . '-' . $foreign_table;
+        $nameObject = $currentStructureDomObjectIdPrefix;
         $mode = 'db';
         $showUpload = false;
         $elementBrowserEnabled = true;
@@ -501,7 +503,7 @@ class InlineControlContainer extends AbstractContainer
                 $maxFileSize = GeneralUtility::getMaxUploadFileSize() * 1024;
                 $item .= ' <a href="#" class="btn btn-default t3js-drag-uploader inlineNewFileUploadButton ' . $this->inlineData['config'][$nameObject]['md5'] . '"
 					' . $buttonStyle . '
-					data-dropzone-target="#' . htmlspecialchars($this->inlineStackProcessor->getCurrentStructureDomObjectIdPrefix($this->data['inlineFirstPid'])) . '"
+					data-dropzone-target="#' . htmlspecialchars(StringUtility::escapeCssSelector($currentStructureDomObjectIdPrefix)) . '"
 					data-insert-dropzone-before="1"
 					data-file-irre-object="' . htmlspecialchars($objectPrefix) . '"
 					data-file-allowed="' . htmlspecialchars($allowed) . '"
diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/DragUploader.js b/typo3/sysext/backend/Resources/Public/JavaScript/DragUploader.js
index e2dc9ddae0cc..bbcbabf9656f 100644
--- a/typo3/sysext/backend/Resources/Public/JavaScript/DragUploader.js
+++ b/typo3/sysext/backend/Resources/Public/JavaScript/DragUploader.js
@@ -57,15 +57,16 @@ define(['jquery',
 		var me = this;
 		me.$body = $('body');
 		me.$element = $(element);
-		me.$trigger = $(me.$element.data('dropzone-trigger'));
+		me.$trigger = $(me.$element.data('dropzoneTrigger'));
 		me.$dropzone = $('<div />').addClass('dropzone').hide();
-		me.irreObjectUid = me.$element.data('file-irre-object');
-		if (me.irreObjectUid && me.$element.nextAll(me.$element.data('dropzone-target')).length !== 0) {
+		me.irreObjectUid = me.$element.data('fileIrreObject');
+		var dropZoneEscapedTarget = me.$element.data('dropzoneTarget');
+		if (me.irreObjectUid && me.$element.nextAll(dropZoneEscapedTarget).length !== 0) {
 			me.dropZoneInsertBefore = true;
-			me.$dropzone.insertBefore(me.$element.data('dropzone-target'));
+			me.$dropzone.insertBefore(dropZoneEscapedTarget);
 		} else {
 			me.dropZoneInsertBefore = false;
-			me.$dropzone.insertAfter(me.$element.data('dropzone-target'));
+			me.$dropzone.insertAfter(dropZoneEscapedTarget);
 		}
 		me.$dropzoneMask = $('<div />').addClass('dropzone-mask').appendTo(me.$dropzone);
 		me.$fileInput = $('<input type="file" multiple name="files[]" />').addClass('upload-file-picker').appendTo(me.$body);
diff --git a/typo3/sysext/core/Classes/Utility/StringUtility.php b/typo3/sysext/core/Classes/Utility/StringUtility.php
index a4800dfa46e1..c9ff0c9aa18e 100644
--- a/typo3/sysext/core/Classes/Utility/StringUtility.php
+++ b/typo3/sysext/core/Classes/Utility/StringUtility.php
@@ -93,4 +93,18 @@ class StringUtility
         $uniqueId = uniqid($prefix, true);
         return str_replace('.', '', $uniqueId);
     }
+
+    /**
+     * Escape a CSS selector to be used for DOM queries
+     *
+     * This method takes care to escape any CSS selector meta character.
+     * The result may be used to query the DOM like $('#' + escapedSelector)
+     *
+     * @param string $selector
+     * @return string
+     */
+    public static function escapeCssSelector(string $selector) : string
+    {
+        return preg_replace('([#:.\\[\\],=@])', '\\$1', $selector);
+    }
 }
diff --git a/typo3/sysext/core/Tests/Unit/Utility/StringUtilityTest.php b/typo3/sysext/core/Tests/Unit/Utility/StringUtilityTest.php
index 73cd95d17099..52f14afe3458 100644
--- a/typo3/sysext/core/Tests/Unit/Utility/StringUtilityTest.php
+++ b/typo3/sysext/core/Tests/Unit/Utility/StringUtilityTest.php
@@ -217,4 +217,28 @@ class StringUtilityTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
     {
         $this->assertNotContains('.', StringUtility::getUniqueId());
     }
+
+    /**
+     * @param string $selector
+     * @param string $expectedValue
+     * @dataProvider escapeCssSelectorDataProvider
+     */
+    public function escapeCssSelector(string $selector, string $expectedValue)
+    {
+        $this->assertEquals($expectedValue, StringUtility::escapeCssSelector($selector));
+    }
+
+    /**
+     * @return array
+     */
+    public function escapeCssSelectorDataProvider() : array
+    {
+        return [
+            ['data.field', 'data\\.field'],
+            ['#theId', '\\#theId'],
+            ['.theId:hover', '\\.theId\\:hover'],
+            ['.theId:hover', '\\.theId\\:hover'],
+            ['input[name=foo]', 'input\\[name\\=foo\\]'],
+        ];
+    }
 }
-- 
GitLab