Skip to content

Commit

Permalink
[SECURITY] Deny pages' TSconfig and tsconfig_includes for non-admins
Browse files Browse the repository at this point in the history
Fields `TSconfig` and `tsconfig_includes` of table `pages` can be
misused by restricted users to contain malicious instructions and
lead to cross-site scripting as well as arbitrary code execution.
Since user input cannot be sanitized properly, the field is now
available for admin users only. In addition directory traversal
in TSconfig static includes has been mitigated.

Resolves: #88565
Releases: master, 9.5, 8.7
Security-Commit: c551eca73a374b5d3ef84940d54d8a57086067ae
Security-Bulletin: TYPO3-CORE-SA-2019-019
Change-Id: I53524322b3ba385c6c94897ba2791c42c0e4a481
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/61131
Tested-by: Oliver Hader <oliver.hader@typo3.org>
Reviewed-by: Oliver Hader <oliver.hader@typo3.org>
  • Loading branch information
ohader committed Jun 25, 2019
1 parent fe43834 commit 822e62e
Show file tree
Hide file tree
Showing 6 changed files with 194 additions and 0 deletions.
42 changes: 42 additions & 0 deletions Classes/Hooks/PagesTsConfigGuard.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Core\Hooks;

/*
* 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!
*/

use TYPO3\CMS\Core\DataHandling\DataHandler;

/**
* Guard to only allow modifications of pages.TSconfig for admin users.
*/
class PagesTsConfigGuard
{
/**
* @param array $incomingFieldArray
* @param string $table
* @param string $id
* @param DataHandler $dataHandler
*/
public function processDatamap_preProcessFieldArray(
array &$incomingFieldArray,
string $table,
string $id,
DataHandler $dataHandler
) {
if ($table === 'pages' && !$dataHandler->admin) {
unset($incomingFieldArray['TSconfig']);
unset($incomingFieldArray['tsconfig_includes']);
}
}
}
2 changes: 2 additions & 0 deletions Configuration/TCA/pages.php
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@
'TSconfig' => [
'exclude' => true,
'label' => 'TSconfig:',
'displayCond' => 'HIDE_FOR_NON_ADMINS',
'config' => [
'type' => 'text',
'cols' => 40,
Expand Down Expand Up @@ -794,6 +795,7 @@
'tsconfig_includes' => [
'exclude' => true,
'label' => 'LLL:EXT:frontend/Resources/Private/Language/locallang_tca.xlf:pages.tsconfig_includes',
'displayCond' => 'HIDE_FOR_NON_ADMINS',
'config' => [
'type' => 'select',
'renderType' => 'selectMultipleSideBySide',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
"pages",,,,,
,"uid","pid","title","TSconfig","tsconfig_includes"
,1,0,"FunctionalTest","",""
,88,1,"DataHandlerTest","",""
,89,88,"Relations","",""
,90,88,"Target","",""
,91,1,"New page","custom.setting = 1","EXT:package/file.tsconfig"
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
"pages",,,,,
,"uid","pid","title","TSconfig","tsconfig_includes"
,1,0,"FunctionalTest","",""
,88,1,"DataHandlerTest","",""
,89,88,"Relations","",""
,90,88,"Target","",""
,91,1,"New page","",""
135 changes: 135 additions & 0 deletions Tests/Functional/DataHandling/Regular/Hooks/PagesTsConfigGuardTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Core\Tests\Functional\DataHandling\Regular\Hooks;

/*
* 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!
*/

use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\DataHandling\DataHandler;
use TYPO3\CMS\Core\Type\Bitmask\Permission;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\StringUtility;
use TYPO3\CMS\Lang\LanguageService;
use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;

class PagesTsConfigGuardTest extends FunctionalTestCase
{
/**
* @var string
*/
private $scenarioDataSetDirectory = 'typo3/sysext/core/Tests/Functional/DataHandling/Regular/DataSet/';

/**
* @var string
*/
private $assertionDataSetDirectory = 'typo3/sysext/core/Tests/Functional/DataHandling/Regular/Hooks/DataSet/';

/**
* The fixture which is used when initializing a backend user
*
* @var string
*/
protected $backendUserFixture = 'typo3/sysext/sv/Tests/Functional/Fixtures/be_users.xml';

protected function setUp()
{
parent::setUp();
$GLOBALS['LANG'] = $this->getMockBuilder(LanguageService::class)
->setMethods(['sL'])
->getMock();

$this->importScenarioDataSet('LiveDefaultPages');
$this->importDataSet(dirname($this->backendUserFixture) . '/be_groups.xml');
// define page create permissions for backend user group 9 on page 1
GeneralUtility::makeInstance(ConnectionPool::class)
->getConnectionForTable('pages')
->update(
'pages',
['perms_groupid' => 9, 'perms_group' => Permission::ALL],
['uid' => 1]
);
}

/**
* @test
*/
public function pagesTsConfigIsConsideredForAdminUser()
{
$backendUser = $this->setUpBackendUserFromFixture(1);
$identifier = StringUtility::getUniqueId('NEW');

$dataMap = [
'pages' => [
$identifier => [
'pid' => 1,
'title' => 'New page',
'TSconfig' => 'custom.setting = 1',
'tsconfig_includes' => 'EXT:package/file.tsconfig',
],
],
];

$this->assertProcessedDataMap($dataMap, $backendUser);
$this->assertAssertionDataSet('pagesTsConfigIsConsideredForAdminUser');
}

/**
* @test
*/
public function pagesTsConfigIsIgnoredForNonAdminUser()
{
$backendUser = $this->setUpBackendUserFromFixture(9);
$identifier = StringUtility::getUniqueId('NEW');

$dataMap = [
'pages' => [
$identifier => [
'pid' => 1,
'title' => 'New page',
'TSconfig' => 'custom.setting = 1',
'tsconfig_includes' => 'EXT:package/file.tsconfig',
],
],
];

$this->assertProcessedDataMap($dataMap, $backendUser);
$this->assertAssertionDataSet('pagesTsConfigIsIgnoredForNonAdminUser');
}

/**
* @param string $dataSetName
*/
private function importScenarioDataSet($dataSetName)
{
$fileName = rtrim($this->scenarioDataSetDirectory, '/') . '/' . $dataSetName . '.csv';
$fileName = GeneralUtility::getFileAbsFileName($fileName);
$this->importCSVDataSet($fileName);
}

private function assertAssertionDataSet($dataSetName)
{
$fileName = rtrim($this->assertionDataSetDirectory, '/') . '/' . $dataSetName . '.csv';
$fileName = GeneralUtility::getFileAbsFileName($fileName);
$this->assertCSVDataSet($fileName);
}

private function assertProcessedDataMap(array $dataMap, BackendUserAuthentication $backendUser)
{
$dataHandler = GeneralUtility::makeInstance(DataHandler::class);
$dataHandler->start($dataMap, [], $backendUser);
$dataHandler->process_datamap();
static::assertEmpty($dataHandler->errorLog);
}
}
1 change: 1 addition & 0 deletions ext_localconf.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
}

$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processDatamapClass'][] = \TYPO3\CMS\Core\Hooks\DestroySessionHook::class;
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processDatamapClass'][] = \TYPO3\CMS\Core\Hooks\PagesTsConfigGuard::class;

$signalSlotDispatcher->connect(
\TYPO3\CMS\Core\Resource\ResourceStorage::class,
Expand Down

0 comments on commit 822e62e

Please sign in to comment.