[BUGFIX] Correct record title escaping 85/49185/3
authorNicole Cordes <typo3@cordes.co>
Sat, 2 Jul 2016 17:26:08 +0000 (19:26 +0200)
committerMarkus Klein <markus.klein@typo3.org>
Wed, 27 Jul 2016 09:32:34 +0000 (11:32 +0200)
This patch removes default record title escaping in resolved DataProvider
data and adds proper escaping where html output is generated.

Resolves: #76399
Resolves: #76668
Resolves: #76900
Releases: master, 7.6
Change-Id: I03cf41c5200e920088116d1a67a2e342e46142d3
Reviewed-on: https://review.typo3.org/49185
Reviewed-by: Markus Klein <markus.klein@typo3.org>
Tested-by: Markus Klein <markus.klein@typo3.org>
typo3/sysext/backend/Classes/Form/Container/InlineRecordContainer.php
typo3/sysext/backend/Classes/Form/Element/SelectCheckBoxElement.php
typo3/sysext/backend/Classes/Form/Element/SelectMultipleSideBySideElement.php
typo3/sysext/backend/Classes/Form/Element/SelectSingleElement.php
typo3/sysext/backend/Classes/Form/FormDataProvider/AbstractItemProvider.php
typo3/sysext/backend/Classes/Form/FormDataProvider/TcaRecordTitle.php
typo3/sysext/backend/Tests/Unit/Form/FormDataProvider/TcaRecordTitleTest.php

index c6e503f..6590c15 100644 (file)
@@ -260,7 +260,7 @@ class InlineRecordContainer extends AbstractContainer
             $markup[] = '            </span>';
             $markup[] = '        </div>';
             $markup[] = '        <div class="media-body">';
-            $markup[] = '            <div class="alert-message">' . htmlspecialchars($message) .  '</div>';
+            $markup[] = '            <div class="alert-message">' . htmlspecialchars($message) . '</div>';
             $markup[] = '        </div>';
             $markup[] = '    </div>';
             $markup[] = '</div>';
@@ -302,8 +302,13 @@ class InlineRecordContainer extends AbstractContainer
         $objectId = $domObjectId . '-' . $foreignTable . '-' . $rec['uid'];
 
         $recordTitle = $data['recordTitle'];
-        if (empty($recordTitle)) {
-            $recordTitle = '<em>[' . $languageService->sL('LLL:EXT:lang/locallang_core.xlf:labels.no_title', true) . ']</em>';
+        if (!empty($recordTitle)) {
+            // The user function may return HTML, therefore we can't escape it
+            if (empty($data['processedTca']['ctrl']['formattedLabel_userFunc'])) {
+                $recordTitle = BackendUtility::getRecordTitlePrep($recordTitle);
+            }
+        } else {
+            $recordTitle = '<em>[' . htmlspecialchars($languageService->sL('LLL:EXT:lang/locallang_core.xlf:labels.no_title')) . ']</em>';
         }
 
         $altText = BackendUtility::getRecordIconAltText($rec, $foreignTable);
index 469fbf5..af0a9cd 100644 (file)
@@ -63,7 +63,7 @@ class SelectCheckBoxElement extends AbstractFormElement
                         $currentGroup++;
                         $groups[$currentGroup]['header'] = array(
                             'icon' => $selIcon,
-                            'title' => htmlspecialchars($p[0])
+                            'title' => $p[0]
                         );
                     } else {
                         // Check if some help text is available
@@ -103,7 +103,7 @@ class SelectCheckBoxElement extends AbstractFormElement
                             'disabled' => false,
                             'class' => '',
                             'icon' => (!empty($p[2]) ? FormEngineUtility::getIconHtml($p[2]) : $this->iconFactory->getIcon('empty-empty', Icon::SIZE_SMALL)->render()),
-                            'title' => htmlspecialchars($p[0], ENT_COMPAT, 'UTF-8', false),
+                            'title' => $p[0],
                             'help' => $help
                         );
                         $c++;
@@ -121,7 +121,7 @@ class SelectCheckBoxElement extends AbstractFormElement
                     $html[] = '<div class="panel-heading">';
                     $html[] = '<a data-toggle="collapse" href="#' . $groupId . '" aria-expanded="false" aria-controls="' . $groupId . '">';
                     $html[] = $group['header']['icon'];
-                    $html[] = $group['header']['title'];
+                    $html[] = htmlspecialchars($group['header']['title']);
                     $html[] = '</a>';
                     $html[] = '</div>';
                 }
@@ -146,7 +146,7 @@ class SelectCheckBoxElement extends AbstractFormElement
                         $tableRows[] =        '<label class="label-block" for="' . $item['id'] . '">' . $item['icon'] . '</label>';
                         $tableRows[] =    '</td>';
                         $tableRows[] =    '<td class="col-title">';
-                        $tableRows[] =        '<label class="label-block" for="' . $item['id'] . '">' . $item['title'] . '</label>';
+                        $tableRows[] =        '<label class="label-block" for="' . $item['id'] . '">' . htmlspecialchars($item['title'], ENT_COMPAT, 'UTF-8', false) . '</label>';
                         $tableRows[] =    '</td>';
                         $tableRows[] =    '<td>' . $item['help'] . '</td>';
                         $tableRows[] = '</tr>';
index d74b3a1..dcfd44a 100644 (file)
@@ -104,7 +104,7 @@ class SelectMultipleSideBySideElement extends AbstractFormElement
                     $disabledAttr = ' disabled="disabled"';
                     $classAttr = ' class="hidden"';
                 }
-                $opt[] = '<option value="' . htmlspecialchars($p[1]) . '" title="' . $p[0] . '"' . $classAttr . $disabledAttr . '>' . $p[0] . '</option>';
+                $opt[] = '<option value="' . htmlspecialchars($p[1]) . '" title="' . htmlspecialchars($p[0]) . '"' . $classAttr . $disabledAttr . '>' . htmlspecialchars($p[0]) . '</option>';
             }
             // Put together the selector box:
             $selector_itemListStyle = isset($config['itemListStyle'])
index 60a9e7e..aea1689 100644 (file)
@@ -106,7 +106,6 @@ class SelectSingleElement extends AbstractFormElement
                 );
             } else {
                 // IS ITEM
-                $title = htmlspecialchars($item['0'], ENT_COMPAT, 'UTF-8', false);
                 $icon = !empty($item[2]) ? FormEngineUtility::getIconHtml($item[2], $title, $title) : '';
                 $selected = $selectedValue === (string)$item[1];
 
@@ -115,7 +114,7 @@ class SelectSingleElement extends AbstractFormElement
                 }
 
                 $selectItemGroups[$selectItemGroupCount]['items'][] = array(
-                    'title' => $title,
+                    'title' => $item[0],
                     'value' => $item[1],
                     'icon' => $icon,
                     'selected' => $selected,
@@ -125,7 +124,7 @@ class SelectSingleElement extends AbstractFormElement
                 // ICON
                 if ($icon) {
                     $selectIcons[] = array(
-                        'title' => $title,
+                        'title' => $item[0],
                         'icon' => $icon,
                         'index' => $selectItemCounter,
                     );
@@ -155,7 +154,7 @@ class SelectSingleElement extends AbstractFormElement
                 foreach ($selectItemGroup['items'] as $item) {
                     $options .= '<option value="' . htmlspecialchars($item['value']) . '" data-icon="' .
                         htmlspecialchars($item['icon']) . '"'
-                        . ($item['selected'] ? ' selected="selected"' : '') . '>' . $item['title'] . '</option>';
+                        . ($item['selected'] ? ' selected="selected"' : '') . '>' . htmlspecialchars($item['title'], ENT_COMPAT, 'UTF-8', false) . '</option>';
                 }
                 $hasIcons = !empty($item['icon']);
             }
@@ -216,7 +215,7 @@ class SelectSingleElement extends AbstractFormElement
                 $html[] =            '<td>';
 
                 if (is_array($selectIcon)) {
-                    $html[] = '<a href="#" title="' . $selectIcon['title'] . '" data-select-index="' . $selectIcon['index'] . '">';
+                    $html[] = '<a href="#" title="' . htmlspecialchars($selectIcon['title'], ENT_COMPAT, 'UTF-8', false) . '" data-select-index="' . htmlspecialchars($selectIcon['index']) . '">';
                     $html[] = $selectIcon['icon'];
                     $html[] = '</a>';
                 }
index 5cf1008..fe2a4b8 100644 (file)
@@ -477,7 +477,7 @@ abstract class AbstractItemProvider
                 }
                 // Add the item
                 $items[] = [
-                    $labelPrefix . htmlspecialchars(BackendUtility::getRecordTitle($foreignTable, $foreignRow)),
+                    $labelPrefix . BackendUtility::getRecordTitle($foreignTable, $foreignRow),
                     $foreignRow['uid'],
                     $icon
                 ];
index f53df8f..ca9d44a 100644 (file)
@@ -69,10 +69,7 @@ class TcaRecordTitle implements FormDataProviderInterface
             $fieldName = $result['isOnSymmetricSide']
                 ? $result['inlineParentConfig']['symmetric_label']
                 : $result['inlineParentConfig']['foreign_label'];
-            // @todo: this is a mixup here. problem is the prep method cuts the string, but also hsc's the thing.
-            // @todo: this is uncool for the userfuncs, so it is applied only here. however, the OuterWrapContainer
-            // @todo: also prep()'s the title created by the else patch below ... find a better separation and clean this up!
-            $result['recordTitle'] = BackendUtility::getRecordTitlePrep($this->getRecordTitleForField($fieldName, $result));
+            $result['recordTitle'] = $this->getRecordTitleForField($fieldName, $result);
         } elseif (isset($result['processedTca']['ctrl']['label_userFunc'])) {
             // userFunc takes precedence over everything else
             $parameters = [
@@ -127,7 +124,7 @@ class TcaRecordTitle implements FormDataProviderInterface
             }
         }
 
-        $result['recordTitle'] = htmlspecialchars(implode(', ', $titles));
+        $result['recordTitle'] = implode(', ', $titles);
         return $result;
     }
 
index 0338495..4a04aca 100644 (file)
@@ -271,13 +271,6 @@ class TcaRecordTitleTest extends UnitTestCase
                 'aValue',
                 'aValue',
             ],
-            'html is escaped' => [
-                [
-                    'type' => 'input',
-                ],
-                '<foo>',
-                '&lt;foo&gt;',
-            ],
             'date input' => [
                 [
                     'type' => 'input',