Skip to content

Commit

Permalink
[SECURITY] Prohibit TypoScript in form yaml files
Browse files Browse the repository at this point in the history
Only evaluate TypoScript-like instructions like

```
submitButtonLabel = TEXT
submitButtonLabel.value = Bar
```

defined within

`plugin.tx_form.settings.formDefinitionOverrides`
and
`plugin.tx_form.settings.yamlSettingsOverrides`

and **not** within form definition yaml files or
the form setup yaml files.

This is achieved by not searching the entire form
definition or form setup for TypoScript instructions,
but only the actual TypoScript.

Resolves: #98403
Releases: main, 11.5, 10.4
Change-Id: I7b066f109d6061715c2240b01ed15185c58fa9f5
Security-Bulletin: TYPO3-CORE-SA-2022-015
Security-References: CVE-2022-23503
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/77092
Reviewed-by: Oliver Hader <oliver.hader@typo3.org>
Tested-by: Oliver Hader <oliver.hader@typo3.org>
  • Loading branch information
waldhacker1 authored and ohader committed Dec 13, 2022
1 parent 4a41c71 commit fb74f1d
Show file tree
Hide file tree
Showing 4 changed files with 252 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -158,12 +158,13 @@ protected function overrideByTypoScriptSettings(array $formDefinition): array
isset($this->settings['formDefinitionOverrides'][$formDefinition['identifier']])
&& !empty($this->settings['formDefinitionOverrides'][$formDefinition['identifier']])
) {
$formDefinitionOverrides = GeneralUtility::makeInstance(TypoScriptService::class)
->resolvePossibleTypoScriptConfiguration($this->settings['formDefinitionOverrides'][$formDefinition['identifier']]);

ArrayUtility::mergeRecursiveWithOverrule(
$formDefinition,
$this->settings['formDefinitionOverrides'][$formDefinition['identifier']]
$formDefinitionOverrides
);
$formDefinition = GeneralUtility::makeInstance(TypoScriptService::class)
->resolvePossibleTypoScriptConfiguration($formDefinition);
}
return $formDefinition;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,17 +133,18 @@ protected function overrideConfigurationByTypoScript(array $yamlSettings, string
{
$typoScript = parent::getConfiguration(self::CONFIGURATION_TYPE_SETTINGS, $extensionName);
if (is_array($typoScript['yamlSettingsOverrides'] ?? null) && !empty($typoScript['yamlSettingsOverrides'])) {
ArrayUtility::mergeRecursiveWithOverrule(
$yamlSettings,
$typoScript['yamlSettingsOverrides']
);

$yamlSettingsOverrides = $typoScript['yamlSettingsOverrides'];
if (($GLOBALS['TYPO3_REQUEST'] ?? null) instanceof ServerRequestInterface
&& ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'])->isFrontend()
) {
$yamlSettings = GeneralUtility::makeInstance(TypoScriptService::class)
->resolvePossibleTypoScriptConfiguration($yamlSettings);
$yamlSettingsOverrides = GeneralUtility::makeInstance(TypoScriptService::class)
->resolvePossibleTypoScriptConfiguration($yamlSettingsOverrides);
}

ArrayUtility::mergeRecursiveWithOverrule(
$yamlSettings,
$yamlSettingsOverrides
);
}
return $yamlSettings;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -594,4 +594,99 @@ public function overrideByTypoScriptSettingsReturnsOverriddenConfigurationIfTypo

self::assertSame($expected, $mockController->_call('overrideByTypoScriptSettings', $input));
}

/**
* @test
*/
public function overrideByTypoScriptSettingsDoesNotEvaluateTypoScriptLookalikeInstructionsFromYamlSettings(): void
{
$formDefinitionYaml = [
'identifier' => 'ext-form-identifier',
'prototypeName' => 'standard',
'label' => [
'value' => 'Label override',
'_typoScriptNodeValue' => 'TEXT',
],
'renderables' => [
0 => [
'identifier' => 'page-1',
'type' => 'Page',
'label' => 'Label',
'renderables' => [
0 => [
'identifier' => 'text-1',
'type' => 'Text',
'label' => 'Label',
],
],
],
],
];

$formTypoScript = [
'formDefinitionOverrides' => [
'ext-form-identifier' => [
'renderables' => [
0 => [
'renderables' => [
0 => [
'label' => [
'value' => 'Label override',
'_typoScriptNodeValue' => 'TEXT',
],
],
],
],
],
],
],
];

$evaluatedFormTypoScript = [
'renderables' => [
0 => [
'renderables' => [
0 => [
'label' => 'Label override',
],
],
],
],
];

$expected = [
'identifier' => 'ext-form-identifier',
'prototypeName' => 'standard',
'label' => [
'value' => 'Label override',
'_typoScriptNodeValue' => 'TEXT',
],
'renderables' => [
0 => [
'identifier' => 'page-1',
'type' => 'Page',
'label' => 'Label',
'renderables' => [
0 => [
'identifier' => 'text-1',
'type' => 'Text',
'label' => 'Label override',
],
],
],
],
];

$controllerMock = $this->getAccessibleMock(FormFrontendController::class, [
'dummy',
], [], '', false);

$typoScriptServiceProphecy = $this->prophesize(TypoScriptService::class);
$typoScriptServiceProphecy->resolvePossibleTypoScriptConfiguration($formTypoScript['formDefinitionOverrides']['ext-form-identifier'])->willReturn($evaluatedFormTypoScript);
GeneralUtility::addInstance(TypoScriptService::class, $typoScriptServiceProphecy->reveal());

$controllerMock->_set('settings', $formTypoScript);

self::assertSame($expected, $controllerMock->_call('overrideByTypoScriptSettings', $formDefinitionYaml));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
<?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\Form\Tests\Unit\Mvc\Configuration;

use Prophecy\PhpUnit\ProphecyTrait;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Core\Core\SystemEnvironmentBuilder;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Configuration\FrontendConfigurationManager;
use TYPO3\CMS\Form\Mvc\Configuration\ConfigurationManager;
use TYPO3\CMS\Form\Mvc\Configuration\ConfigurationManagerInterface;
use TYPO3\CMS\Form\Mvc\Configuration\TypoScriptService;
use TYPO3\TestingFramework\Core\Unit\UnitTestCase;

class ConfigurationManagerTest extends UnitTestCase
{
use ProphecyTrait;

/**
* @var bool
*/
protected $resetSingletonInstances = true;

public function tearDown(): void
{
unset($GLOBALS['TYPO3_REQUEST']);
parent::tearDown();
}

/**
* @test
*/
public function getConfigurationDoesNotEvaluateTypoScriptLookalikeInstructionsFromYamlSettingsInFrontendContext(): void
{
$yamlSettings = [
'prototypes' => [
'standard' => [
'formElementsDefinition' => [
'Form' => [
'renderingOptions' => [
'submitButtonLabel' => 'Foo',
'templateVariant' => 'version1',
'addQueryString' => [
'value' => 'Baz',
'_typoScriptNodeValue' => 'TEXT',
],
],
],
],
],
],
];

$formTypoScript = [
'settings' => [
'yamlSettingsOverrides' => [
'prototypes' => [
'standard' => [
'formElementsDefinition' => [
'Form' => [
'renderingOptions' => [
'submitButtonLabel' => [
'value' => 'Bar',
'_typoScriptNodeValue' => 'TEXT',
],
],
],
],
],
],
],
],
];

$evaluatedFormTypoScript = [
'prototypes' => [
'standard' => [
'formElementsDefinition' => [
'Form' => [
'renderingOptions' => [
'submitButtonLabel' => 'Bar',
],
],
],
],
],
];

$expected = [
'prototypes' => [
'standard' => [
'formElementsDefinition' => [
'Form' => [
'renderingOptions' => [
'submitButtonLabel' => 'Bar',
'templateVariant' => 'version1',
'addQueryString' => [
'value' => 'Baz',
'_typoScriptNodeValue' => 'TEXT',
],
],
],
],
],
],
];

$configurationManagerMock = $this->getAccessibleMock(ConfigurationManager::class, [
'getTypoScriptSettings',
'getYamlSettingsFromCache',
], [], '', false);
$configurationManagerMock->method('getYamlSettingsFromCache')->with(self::anything())->willReturn($yamlSettings);

$serverRequestProphecy = $this->prophesize(ServerRequestInterface::class);
$serverRequestProphecy->getAttribute('applicationType')->willReturn(SystemEnvironmentBuilder::REQUESTTYPE_FE);
$GLOBALS['TYPO3_REQUEST'] = $serverRequestProphecy->reveal();

$typoScriptServiceProphecy = $this->prophesize(TypoScriptService::class);
$typoScriptServiceProphecy->resolvePossibleTypoScriptConfiguration($formTypoScript['settings']['yamlSettingsOverrides'])->willReturn($evaluatedFormTypoScript);
GeneralUtility::addInstance(TypoScriptService::class, $typoScriptServiceProphecy->reveal());

$concreteConfigurationManagerProphecy = $this->prophesize(FrontendConfigurationManager::class);
$concreteConfigurationManagerProphecy->getConfiguration('form', null)->willReturn($formTypoScript);

$configurationManagerMock->_set('concreteConfigurationManager', $concreteConfigurationManagerProphecy->reveal());
$configurationManagerMock->method('getTypoScriptSettings')->with(self::anything())->willReturn([]);

self::assertSame($expected, $configurationManagerMock->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_YAML_SETTINGS, 'form'));
}
}

0 comments on commit fb74f1d

Please sign in to comment.