Commit 6a972407 authored by Oliver Hader's avatar Oliver Hader Committed by Oliver Hader
Browse files

[SECURITY] Encode passed arguments in Fluid view helpers

* XSS in `f:be.labels.csh` in argument `label`
* XSS in `f:be.menus.actionMenu` in argument `label`
* XSS in `f:form` in argument `fieldNamePrefix`

Resolves: #92602
Releases: master, 10.4, 9.5
Change-Id: I7574bfb60eb2e11ecfb98d187f2edd580f43cd93
Security-Bulletin: TYPO3-CORE-SA-2020-010
Security-References: CVE-2020-26227
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/66663


Tested-by: Oliver Hader's avatarOliver Hader <oliver.hader@typo3.org>
Reviewed-by: Oliver Hader's avatarOliver Hader <oliver.hader@typo3.org>
parent 0228e371
......@@ -1549,7 +1549,7 @@ class PageRenderer implements SingletonInterface
unset($this->requireJsConfig['paths'][$baseModuleName]);
}
// execute the main module, and load a possible callback function
$javaScriptCode = 'require(["' . $mainModuleName . '"]';
$javaScriptCode = 'require([' . GeneralUtility::quoteJSvalue($mainModuleName) . ']';
if ($callBackFunction !== null) {
$inlineCodeKey .= sha1($callBackFunction);
$javaScriptCode .= ', ' . $callBackFunction;
......
......@@ -109,9 +109,10 @@ class CshViewHelper extends AbstractBackendViewHelper
$table = '_MOD_' . $moduleName;
}
if (strpos($label, 'LLL:') === 0) {
$label = htmlspecialchars(self::getLanguageService()->sL($label));
$label = self::getLanguageService()->sL($label);
}
$label = '<label>' . $label . '</label>';
// Double encode can be set to true, once the typo3fluid/fluid fix is released and required
$label = '<label>' . htmlspecialchars($label, ENT_QUOTES, null, false) . '</label>';
return BackendUtility::wrapInHelp($table, $field, $label);
}
}
......@@ -88,7 +88,10 @@ class ActionMenuItemViewHelper extends AbstractTagBasedViewHelper
$this->evaluateSelectItemState($controller, $action, $arguments);
}
$this->tag->setContent($label);
$this->tag->setContent(
// Double encode can be set to true, once the typo3fluid/fluid fix is released and required
htmlspecialchars($label, ENT_QUOTES, null, false)
);
return $this->tag->render();
}
......
......@@ -388,7 +388,7 @@ abstract class AbstractFormFieldViewHelper extends AbstractFormViewHelper
if (substr($fieldName, -2) === '[]') {
$fieldName = substr($fieldName, 0, -2);
}
if (!in_array($fieldName, $hiddenFieldNames)) {
if (!in_array($fieldName, $hiddenFieldNames, true)) {
$hiddenFieldNames[] = $fieldName;
$viewHelperVariableContainer->addOrUpdate(
FormViewHelper::class,
......
......@@ -96,7 +96,7 @@ abstract class AbstractFormViewHelper extends AbstractTagBasedViewHelper
$name = $this->prefixFieldName($name) . '[__identity]';
$this->registerFieldNameForFormTokenGeneration($name);
return LF . '<input type="hidden" name="' . $name . '" value="' . $identifier . '" />' . LF;
return LF . '<input type="hidden" name="' . htmlspecialchars($name) . '" value="' . htmlspecialchars($identifier) . '" />' . LF;
}
/**
......
......@@ -279,11 +279,11 @@ class FormViewHelper extends AbstractFormViewHelper
];
$result = LF;
$result .= '<input type="hidden" name="' . $this->prefixFieldName('__referrer[@extension]') . '" value="' . $extensionName . '" />' . LF;
$result .= '<input type="hidden" name="' . $this->prefixFieldName('__referrer[@controller]') . '" value="' . $controllerName . '" />' . LF;
$result .= '<input type="hidden" name="' . $this->prefixFieldName('__referrer[@action]') . '" value="' . $actionName . '" />' . LF;
$result .= '<input type="hidden" name="' . $this->prefixFieldName('__referrer[arguments]') . '" value="' . htmlspecialchars($this->hashService->appendHmac(base64_encode(serialize($request->getArguments())))) . '" />' . LF;
$result .= '<input type="hidden" name="' . $this->prefixFieldName('__referrer[@request]') . '" value="' . htmlspecialchars($this->hashService->appendHmac(json_encode($actionRequest))) . '" />' . LF;
$result .= '<input type="hidden" name="' . htmlspecialchars($this->prefixFieldName('__referrer[@extension]')) . '" value="' . htmlspecialchars($extensionName) . '" />' . LF;
$result .= '<input type="hidden" name="' . htmlspecialchars($this->prefixFieldName('__referrer[@controller]')) . '" value="' . htmlspecialchars($controllerName) . '" />' . LF;
$result .= '<input type="hidden" name="' . htmlspecialchars($this->prefixFieldName('__referrer[@action]')) . '" value="' . htmlspecialchars($actionName) . '" />' . LF;
$result .= '<input type="hidden" name="' . htmlspecialchars($this->prefixFieldName('__referrer[arguments]')) . '" value="' . htmlspecialchars($this->hashService->appendHmac(base64_encode(serialize($request->getArguments())))) . '" />' . LF;
$result .= '<input type="hidden" name="' . htmlspecialchars($this->prefixFieldName('__referrer[@request]')) . '" value="' . htmlspecialchars($this->hashService->appendHmac(json_encode($actionRequest))) . '" />' . LF;
return $result;
}
......@@ -468,6 +468,6 @@ class FormViewHelper extends AbstractFormViewHelper
{
$formFieldNames = $this->renderingContext->getViewHelperVariableContainer()->get(\TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::class, 'formFieldNames');
$requestHash = $this->mvcPropertyMappingConfigurationService->generateTrustedPropertiesToken($formFieldNames, $this->getFieldNamePrefix());
return '<input type="hidden" name="' . $this->prefixFieldName('__trustedProperties') . '" value="' . htmlspecialchars($requestHash) . '" />';
return '<input type="hidden" name="' . htmlspecialchars($this->prefixFieldName('__trustedProperties')) . '" value="' . htmlspecialchars($requestHash) . '" />';
}
}
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Fluid\Tests\Functional\ViewHelpers\Be\Labels;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\View\StandaloneView;
use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
class CshViewHelperTest extends FunctionalTestCase
{
protected function setUp(): void
{
parent::setUp();
if (!isset($GLOBALS['LANG'])) {
$GLOBALS['LANG'] = GeneralUtility::makeInstance(LanguageService::class);
}
}
public function isRenderedDataProvider(): array
{
return [
'#1' => [
'<f:be.labels.csh table="table" field="field" label="{label}">{variable}</f:be.labels.csh>',
[
'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.goBack',
'variable' => 'variable<>&"\''
],
'<label>Go back</label>',
],
'#2' => [
'<f:be.labels.csh table="table" field="field" label="{label}">{variable}</f:be.labels.csh>',
[
'label' => 'label<>&"\'',
'variable' => 'variable<>&"\''
],
'<label>label&lt;&gt;&amp;&quot;&#039;</label>',
],
'#3' => [
'{f:be.labels.csh(table:\'table\' field:\'field\' label:label)}',
[
'label' => 'label<>&"\'',
'variable' => 'variable<>&"\''
],
'<label>label&lt;&gt;&amp;&quot;&#039;</label>',
],
'#4' => [
'{f:be.labels.csh(table:\'table\' field:\'field\' label:\'{label}\')}',
[
'label' => 'label<>&"\'',
'variable' => 'variable<>&"\''
],
'<label>label&lt;&gt;&amp;&quot;&#039;</label>',
],
];
}
/**
* @param string $source
* @param array $variables
* @param string $expectation
*
* @test
* @dataProvider isRenderedDataProvider
*/
public function isRendered(string $source, array $variables, string $expectation): void
{
$view = new StandaloneView();
$view->getRenderingContext()->getCache()->flush();
$view->setTemplateSource($source);
$view->assignMultiple($variables);
self::assertSame($expectation, $view->render());
}
}
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Fluid\Tests\Functional\ViewHelpers\Be\Menus;
use TYPO3\CMS\Fluid\View\StandaloneView;
use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
class ActionMenuItemViewHelperTest extends FunctionalTestCase
{
public function isRenderedDataProvider(): array
{
return [
'tag syntax' => [
'<f:be.menus.actionMenuItem label="{label}" controller="{controller}" action="{action}" />',
[
'label' => 'label<>&"\'',
'controller' => 'controller<>&"\'',
'action' => 'action<>&"\'',
],
'<option value="">label&lt;&gt;&amp;&quot;&#039;</option>',
],
'inline syntax' => [
'{f:be.menus.actionMenuItem(label:label, controller:controller, action:action)}',
[
'label' => 'label<>&"\'',
'controller' => 'controller<>&"\'',
'action' => 'action<>&"\'',
],
'<option value="">label&lt;&gt;&amp;&quot;&#039;</option>',
],
'inline syntax with quotes' => [
'{f:be.menus.actionMenuItem(label:\'{label}\', controller:\'{controller}\', action:\'{action}\')}',
[
'label' => 'label<>&"\'',
'controller' => 'controller<>&"\'',
'action' => 'action<>&"\'',
],
'<option value="">label&lt;&gt;&amp;&quot;&#039;</option>',
],
];
}
/**
* @param string $source
* @param array $variables
* @param string $expectation
*
* @test
* @dataProvider isRenderedDataProvider
*/
public function isRendered(string $source, array $variables, string $expectation): void
{
$view = new StandaloneView();
$view->setTemplateSource($source);
$view->assignMultiple($variables);
self::assertSame($expectation, $view->render());
}
}
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Fluid\Tests\Functional\ViewHelpers;
use TYPO3\CMS\Fluid\View\StandaloneView;
use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
class FormViewHelperTest extends FunctionalTestCase
{
public function isRenderedDataProvider(): array
{
return [
'#1' => [
'<f:form action="{action}" method="{method}" fieldNamePrefix="{fieldNamePrefix}" />',
[
'action' => 'fieldNamePrefix<>&"\'',
'method' => 'fieldNamePrefix<>&"\'',
'fieldNamePrefix' => 'fieldNamePrefix<>&"\'',
],
// first element having "@extension" in name attribute
'<input type="hidden" name="fieldNamePrefix&lt;&gt;&amp;&quot;\'[__referrer][@extension]" value="" />',
],
'#2' => [
'{f:form(action:action, method:method, fieldNamePrefix:fieldNamePrefix)}',
[
'action' => 'fieldNamePrefix<>&"\'',
'method' => 'fieldNamePrefix<>&"\'',
'fieldNamePrefix' => 'fieldNamePrefix<>&"\'',
],
// first element having "@extension" in name attribute
'<input type="hidden" name="fieldNamePrefix&lt;&gt;&amp;&quot;\'[__referrer][@extension]" value="" />',
],
];
}
/**
* @param string $source
* @param array $variables
* @param string $expectation
*
* @test
* @dataProvider isRenderedDataProvider
*/
public function isRendered(string $source, array $variables, string $expectation): void
{
$view = new StandaloneView();
$view->setTemplateSource($source);
$view->assignMultiple($variables);
$body = $view->render();
$actual = null;
if (preg_match('#<input[^>]+name=".+\[@extension\]"[^>]+>#m', $body, $matches)) {
$actual = $matches[0];
}
self::assertSame($expectation, $actual);
}
}
......@@ -119,7 +119,7 @@ class DatePickerViewHelper extends AbstractFormFieldViewHelper
$this->setErrorClassAttribute();
$content = '';
$content .= $this->tag->render();
$content .= '<input type="hidden" name="' . $name . '[dateFormat]" value="' . htmlspecialchars($dateFormat) . '" />';
$content .= '<input type="hidden" name="' . htmlspecialchars($name) . '[dateFormat]" value="' . htmlspecialchars($dateFormat) . '" />';
return $content;
}
......
......@@ -136,7 +136,7 @@ class TimePickerViewHelper extends AbstractFormFieldViewHelper
foreach (range(0, 23) as $hour) {
$hour = str_pad((string)$hour, 2, '0', STR_PAD_LEFT);
$selected = $hour === $value ? ' selected="selected"' : '';
$options .= '<option value="' . $hour . '"' . $selected . '>' . $hour . '</option>';
$options .= '<option value="' . htmlspecialchars($hour) . '" ' . $selected . '>' . htmlspecialchars($hour) . '</option>';
}
$hourSelector->setContent($options);
return $hourSelector->render();
......@@ -158,7 +158,7 @@ class TimePickerViewHelper extends AbstractFormFieldViewHelper
foreach (range(0, 59) as $minute) {
$minute = str_pad((string)$minute, 2, '0', STR_PAD_LEFT);
$selected = $minute === $value ? ' selected="selected"' : '';
$options .= '<option value="' . $minute . '"' . $selected . '>' . $minute . '</option>';
$options .= '<option value="' . htmlspecialchars($minute) . '"' . $selected . '>' . htmlspecialchars($minute) . '</option>';
}
$minuteSelector->setContent($options);
return $minuteSelector->render();
......
......@@ -99,7 +99,7 @@ class UploadedResourceViewHelper extends UploadViewHelper
// Use the file UID instead, but prefix it with "file:" to communicate this to the type converter
$resourcePointerValue = 'file:' . $resource->getOriginalResource()->getOriginalFile()->getUid();
}
$output .= '<input type="hidden" name="' . $this->getName() . '[submittedFile][resourcePointer]" value="' . htmlspecialchars($this->hashService->appendHmac((string)$resourcePointerValue)) . '"' . $resourcePointerIdAttribute . ' />';
$output .= '<input type="hidden" name="' . htmlspecialchars($this->getName()) . '[submittedFile][resourcePointer]" value="' . htmlspecialchars($this->hashService->appendHmac((string)$resourcePointerValue)) . '"' . $resourcePointerIdAttribute . ' />';
$this->templateVariableContainer->add($as, $resource);
$output .= $this->renderChildren();
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment