‪TYPO3CMS  11.5
SecureHtmlRenderingTest.php
Go to the documentation of this file.
1 <?php
2 
3 /*
4  * This file is part of the TYPO3 CMS project.
5  *
6  * It is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU General Public License, either version 2
8  * of the License, or any later version.
9  *
10  * For the full copyright and license information, please read the
11  * LICENSE.txt file that was distributed with this source code.
12  *
13  * The TYPO3 project - inspiring people to share!
14  */
15 
17 
21 use TYPO3\TestingFramework\Core\Functional\Framework\DataHandling\Scenario\DataHandlerFactory;
22 use TYPO3\TestingFramework\Core\Functional\Framework\DataHandling\Scenario\DataHandlerWriter;
23 use TYPO3\TestingFramework\Core\Functional\Framework\Frontend\Internal\AbstractInstruction;
24 use TYPO3\TestingFramework\Core\Functional\Framework\Frontend\Internal\TypoScriptInstruction;
25 use TYPO3\TestingFramework\Core\Functional\Framework\Frontend\InternalRequest;
26 use TYPO3\TestingFramework\Core\Functional\Framework\Frontend\InternalResponse;
27 use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
28 
29 class ‪SecureHtmlRenderingTest extends FunctionalTestCase
30 {
32 
33  private const ‪TYPE_PLAIN = 'plain';
34  private const ‪TYPE_DISABLE_HTML_SANITIZE = 'disable-htmlSanitize';
35  protected const ‪LANGUAGE_PRESETS = [
36  'EN' => ['id' => 0, 'title' => 'English', 'locale' => 'en_US.UTF8', 'iso' => 'en', 'hrefLang' => 'en-US', 'direction' => ''],
37  ];
38 
40  'SC_OPTIONS' => [
41  'Core/TypoScript/TemplateService' => [
42  'runThroughTemplatesPostProcessing' => [
43  'FunctionalTest' => \TYPO3\TestingFramework\Core\Functional\Framework\Frontend\Hook\TypoScriptInstructionModifier::class . '->apply',
44  ],
45  ],
46  ],
47  ];
48 
49  protected ‪$coreExtensionsToLoad = ['fluid_styled_content'];
50 
51  protected function ‪setUp(): void
52  {
53  parent::setUp();
54 
56  'acme-com',
57  $this->‪buildSiteConfiguration(1000, 'https://acme.com/'),
58  [$this->‪buildDefaultLanguageConfiguration('EN', 'https://acme.us/')]
59  );
60 
61  $this->withDatabaseSnapshot(function () {
62  $this->‪setUpDatabase();
63  });
64  }
65 
66  protected function ‪setUpDatabase(): void
67  {
68  $backendUser = $this->setUpBackendUserFromFixture(1);
70 
71  $scenarioFile = __DIR__ . '/Fixtures/SecureHtmlScenario.yaml';
72  $factory = DataHandlerFactory::fromYamlFile($scenarioFile);
73  $writer = DataHandlerWriter::withBackendUser($backendUser);
74  $writer->invokeFactory($factory);
75  static::failIfArrayIsNotEmpty(
76  $writer->getErrors()
77  );
78 
79  $this->setUpFrontendRootPage(
80  1000,
81  [
82  'constants' => ['EXT:fluid_styled_content/Configuration/TypoScript/constants.typoscript'],
83  'setup' => ['EXT:fluid_styled_content/Configuration/TypoScript/setup.typoscript'],
84  ],
85  [
86  'title' => 'ACME Root',
87  ]
88  );
89  }
90 
92  {
93  return [
94  '#01' => [
95  '01: <script>alert(1)</script>',
96  '<p>01: &lt;script&gt;alert(1)&lt;/script&gt;</p>',
97  ],
98  '#02' => [
99  '02: <unknown a="a" b="b">value</unknown>',
100  '<p>02: &lt;unknown a="a" b="b"&gt;value&lt;/unknown&gt;</p>',
101  ],
102  '#03' => [
103  '03: <img img="img" alt="alt" onerror="alert(1)">',
104  '<p>03: <img alt="alt"></p>',
105  ],
106  '#04' => [
107  '04: <img src="img" alt="alt" onerror="alert(1)">',
108  '<p>04: <img src="img" alt="alt"></p>',
109  ],
110  '#05' => [
111  '05: <img/src="img"/onerror="alert(1)">',
112  '<p>05: &lt;img/src="img"/onerror="alert(1)"&gt;</p>',
113  ],
114  '#06' => [
115  '06: <strong>Given that x < y and y > z...</strong>',
116  '<p>06: <strong>Given that x &lt; y and y &gt; z...</strong></p>',
117  ],
118  '#07' => [
119  '07: <a href="t3://page?uid=1000" target="_blank" rel="noreferrer" class="button" role="button" onmouseover="alert(1)">TYPO3</a>',
120  '<p>07: <a href="/" target="_blank" rel="noreferrer" class="button" role="button">TYPO3</a></p>',
121  ],
122  '#08' => [
123  '08: <?xml >s<img src=x onerror=alert(1)> ?>',
124  // Note: The TYPO3 HTML Parser encodes processing instructions, it's therefore
125  // expected and "OK" that the img tag is not encoded but sanitized.
126  // If the HTML Parser would not run, the expected result would be:
127  // '<p>08: &lt;?xml &gt;s&lt;img src=x onerror=alert(1)&gt; ?&gt;</p>',
128  '<p>08: &lt;?xml &gt;s<img src="x"> ?&gt;</p>',
129  ],
130  ];
131  }
132 
139  public function ‪defaultParseFuncRteAvoidCrossSiteScripting(string $payload, string $expectation): void
140  {
141  $instructions = [
143  ];
144  $response = $this->‪invokeFrontendRendering(...$instructions);
145  self::assertSame($expectation, (string)$response->getBody());
146  }
147 
148  public static function ‪htmlViewHelperAvoidsCrossSiteScriptingDataProvider(): array
149  {
150  return [
151  '#01 ' . self::TYPE_PLAIN => [
153  '01: <script>alert(1)</script>',
154  '<p>01: &lt;script&gt;alert(1)&lt;/script&gt;</p>',
155  ],
156  '#01 ' . self::TYPE_DISABLE_HTML_SANITIZE => [
158  '01: <script>alert(1)</script>',
159  '<p>01: &lt;script&gt;alert(1)&lt;/script&gt;</p>',
160  ],
161  '#03 ' . self::TYPE_PLAIN => [
163  '03: <img img="img" alt="alt" onerror="alert(1)">',
164  '<p>03: <img alt="alt"></p>',
165  ],
166  '#03 ' . self::TYPE_DISABLE_HTML_SANITIZE => [
168  '03: <img img="img" alt="alt" onerror="alert(1)">',
169  '<p>03: <img img="img" alt="alt" onerror="alert(1)"></p>',
170  ],
171  '#07 ' . self::TYPE_PLAIN => [
173  '07: <a href="t3://page?uid=1000" target="_blank" rel="noreferrer" class="button" role="button" onmouseover="alert(1)">TYPO3</a>',
174  '<p>07: <a href="/" target="_blank" rel="noreferrer" class="button" role="button">TYPO3</a></p>',
175  ],
176  '#07 ' . self::TYPE_DISABLE_HTML_SANITIZE => [
178  '07: <a href="t3://page?uid=1000" target="_blank" rel="noreferrer" class="button" role="button" onmouseover="alert(1)">TYPO3</a>',
179  '<p>07: <a href="/" target="_blank" rel="noreferrer" class="button" role="button" onmouseover="alert(1)">TYPO3</a></p>',
180  ],
181  '#08 ' . self::TYPE_PLAIN => [
183  '08: <meta whatever="whatever">',
184  '<p>08: </p>',
185  ],
186  '#08 ' . self::TYPE_DISABLE_HTML_SANITIZE => [
188  '08: <meta whatever="whatever">',
189  '<p>08: <meta whatever="whatever"></p>',
190  ],
191  // `sdfield` is in `styles.content.allowTags` constant
192  '#09 ' . self::TYPE_PLAIN => [
194  '09: <sdfield onmouseover="alert(1)">',
195  '<p>09: &lt;sdfield onmouseover="alert(1)"&gt;&lt;/sdfield&gt;</p>',
196  ],
197  '#09 ' . self::TYPE_DISABLE_HTML_SANITIZE => [
199  '09: <sdfield onmouseover="alert(1)">',
200  '<p>09: <sdfield onmouseover="alert(1)"></p>',
201  ],
202  '#10 ' . self::TYPE_PLAIN => [
204  '10: <meta itemprop="type" content="voice">',
205  '<p>10: <meta itemprop="type" content="voice"></p>',
206  ],
207  '#10 ' . self::TYPE_DISABLE_HTML_SANITIZE => [
209  '10: <meta itemprop="type" content="voice">',
210  '<p>10: <meta itemprop="type" content="voice"></p>',
211  ],
212  ];
213  }
214 
222  public function ‪htmlViewHelperAvoidsCrossSiteScripting(string $type, string $payload, string $expectation): void
223  {
224  $instructions = [
225  $this->‪createFluidTemplateContentObject($type, $payload),
226  ];
227  if ($type === self::TYPE_DISABLE_HTML_SANITIZE) {
228  $instructions[] = $this->‪createDisableHtmlSanitizeInstruction();
229  }
230  $response = $this->‪invokeFrontendRendering(...$instructions);
231  self::assertSame($expectation, trim((string)$response->getBody(), "\n"));
232  }
233 
238  private function ‪invokeFrontendRendering(AbstractInstruction ...$instructions): InternalResponse
239  {
240  $sourcePageId = 1100;
241 
242  $request = (new InternalRequest('https://acme.us/'))
243  ->withPageId($sourcePageId)
244  ->withInstructions(
245  [
247  ]
248  );
249 
250  if (count($instructions) > 0) {
251  $request = $this->‪applyInstructions($request, ...$instructions);
252  }
253 
254  return $this->executeFrontendSubRequest($request);
255  }
256 
257  private function ‪createDefaultInstruction(): TypoScriptInstruction
258  {
259  return (new TypoScriptInstruction(TemplateService::class))
260  ->withTypoScript([
261  'config.' => [
262  'no_cache' => 1,
263  'debug' => 0,
264  'admPanel' => 0,
265  'disableAllHeaderCode' => 1,
266  'sendCacheHeaders' => 0,
267  ],
268  'page' => 'PAGE',
269  'page.' => [
270  'typeNum' => 0,
271  ],
272  ]);
273  }
274 
275  private function ‪createTextContentObjectWithDefaultParseFuncRteInstruction(string $value): TypoScriptInstruction
276  {
277  // default configuration as shipped in ext:fluid_styled_content
278  return (new TypoScriptInstruction(TemplateService::class))
279  ->withTypoScript([
280  'page.' => [
281  '10' => 'TEXT',
282  '10.' => [
283  'value' => $value,
284  'parseFunc' => '< lib.parseFunc_RTE',
285  ],
286  ],
287  ]);
288  }
289 
290  private function ‪createDisableHtmlSanitizeInstruction(): TypoScriptInstruction
291  {
292  return (new TypoScriptInstruction(TemplateService::class))
293  ->withTypoScript([
294  'lib.' => [
295  'parseFunc_RTE.' => [
296  'htmlSanitize' => '0',
297  ],
298  ],
299  ]);
300  }
301 
302  private function ‪createFluidTemplateContentObject(string $type, string $payload): TypoScriptInstruction
303  {
304  return (new TypoScriptInstruction(TemplateService::class))
305  ->withTypoScript([
306  'page.' => [
307  '10' => 'FLUIDTEMPLATE',
308  '10.' => [
309  'file' => 'EXT:fluid_styled_content/Tests/Functional/Rendering/Fixtures/FluidTemplate.html',
310  'variables.' => [
311  'type' => 'TEXT',
312  'type.' => [
313  'value' => $type,
314  ],
315  'payload' => 'TEXT',
316  'payload.' => [
317  'value' => $payload,
318  ],
319  ],
320  ],
321  ],
322  ]);
323  }
324 }
‪TYPO3\CMS\FluidStyledContent\Tests\Functional\Rendering\SecureHtmlRenderingTest\invokeFrontendRendering
‪InternalResponse invokeFrontendRendering(AbstractInstruction ... $instructions)
Definition: SecureHtmlRenderingTest.php:237
‪TYPO3\CMS\Core\Tests\Functional\SiteHandling\SiteBasedTestTrait
Definition: SiteBasedTestTrait.php:36
‪TYPO3
‪TYPO3\CMS\FluidStyledContent\Tests\Functional\Rendering\SecureHtmlRenderingTest
Definition: SecureHtmlRenderingTest.php:30
‪TYPO3\CMS\Core\Tests\Functional\SiteHandling\SiteBasedTestTrait\writeSiteConfiguration
‪writeSiteConfiguration(string $identifier, array $site=[], array $languages=[], array $errorHandling=[])
Definition: SiteBasedTestTrait.php:58
‪TYPO3\CMS\FluidStyledContent\Tests\Functional\Rendering\SecureHtmlRenderingTest\createDisableHtmlSanitizeInstruction
‪createDisableHtmlSanitizeInstruction()
Definition: SecureHtmlRenderingTest.php:289
‪TYPO3\CMS\FluidStyledContent\Tests\Functional\Rendering\SecureHtmlRenderingTest\defaultParseFuncRteAvoidCrossSiteScripting
‪defaultParseFuncRteAvoidCrossSiteScripting(string $payload, string $expectation)
Definition: SecureHtmlRenderingTest.php:138
‪TYPO3\CMS\FluidStyledContent\Tests\Functional\Rendering\SecureHtmlRenderingTest\htmlViewHelperAvoidsCrossSiteScripting
‪htmlViewHelperAvoidsCrossSiteScripting(string $type, string $payload, string $expectation)
Definition: SecureHtmlRenderingTest.php:221
‪TYPO3\CMS\FluidStyledContent\Tests\Functional\Rendering\SecureHtmlRenderingTest\TYPE_DISABLE_HTML_SANITIZE
‪const TYPE_DISABLE_HTML_SANITIZE
Definition: SecureHtmlRenderingTest.php:33
‪TYPO3\CMS\FluidStyledContent\Tests\Functional\Rendering\SecureHtmlRenderingTest\setUpDatabase
‪setUpDatabase()
Definition: SecureHtmlRenderingTest.php:65
‪TYPO3\CMS\Core\Tests\Functional\SiteHandling\SiteBasedTestTrait\buildDefaultLanguageConfiguration
‪array buildDefaultLanguageConfiguration(string $identifier, string $base)
Definition: SiteBasedTestTrait.php:126
‪TYPO3\CMS\FluidStyledContent\Tests\Functional\Rendering\SecureHtmlRenderingTest\setUp
‪setUp()
Definition: SecureHtmlRenderingTest.php:50
‪TYPO3\CMS\FluidStyledContent\Tests\Functional\Rendering\SecureHtmlRenderingTest\createTextContentObjectWithDefaultParseFuncRteInstruction
‪createTextContentObjectWithDefaultParseFuncRteInstruction(string $value)
Definition: SecureHtmlRenderingTest.php:274
‪TYPO3\CMS\Core\Core\Bootstrap\initializeLanguageObject
‪static initializeLanguageObject()
Definition: Bootstrap.php:598
‪TYPO3\CMS\FluidStyledContent\Tests\Functional\Rendering\SecureHtmlRenderingTest\$configurationToUseInTestInstance
‪$configurationToUseInTestInstance
Definition: SecureHtmlRenderingTest.php:38
‪TYPO3\CMS\FluidStyledContent\Tests\Functional\Rendering\SecureHtmlRenderingTest\defaultParseFuncRteAvoidsCrossSiteScriptingDataProvider
‪defaultParseFuncRteAvoidsCrossSiteScriptingDataProvider()
Definition: SecureHtmlRenderingTest.php:90
‪TYPO3\CMS\FluidStyledContent\Tests\Functional\Rendering\SecureHtmlRenderingTest\createDefaultInstruction
‪createDefaultInstruction()
Definition: SecureHtmlRenderingTest.php:256
‪TYPO3\CMS\FluidStyledContent\Tests\Functional\Rendering\SecureHtmlRenderingTest\htmlViewHelperAvoidsCrossSiteScriptingDataProvider
‪static htmlViewHelperAvoidsCrossSiteScriptingDataProvider()
Definition: SecureHtmlRenderingTest.php:147
‪TYPO3\CMS\FluidStyledContent\Tests\Functional\Rendering\SecureHtmlRenderingTest\TYPE_PLAIN
‪const TYPE_PLAIN
Definition: SecureHtmlRenderingTest.php:32
‪TYPO3\CMS\Core\TypoScript\TemplateService
Definition: TemplateService.php:46
‪TYPO3\CMS\FluidStyledContent\Tests\Functional\Rendering\SecureHtmlRenderingTest\$coreExtensionsToLoad
‪$coreExtensionsToLoad
Definition: SecureHtmlRenderingTest.php:48
‪TYPO3\CMS\Core\Tests\Functional\SiteHandling\SiteBasedTestTrait\applyInstructions
‪InternalRequest applyInstructions(InternalRequest $request, AbstractInstruction ... $instructions)
Definition: SiteBasedTestTrait.php:254
‪TYPO3\CMS\FluidStyledContent\Tests\Functional\Rendering\SecureHtmlRenderingTest\LANGUAGE_PRESETS
‪const LANGUAGE_PRESETS
Definition: SecureHtmlRenderingTest.php:34
‪TYPO3\CMS\Core\Core\Bootstrap
Definition: Bootstrap.php:70
‪TYPO3\CMS\Core\Tests\Functional\SiteHandling\SiteBasedTestTrait\buildSiteConfiguration
‪array buildSiteConfiguration(int $rootPageId, string $base='')
Definition: SiteBasedTestTrait.php:111
‪TYPO3\CMS\FluidStyledContent\Tests\Functional\Rendering
Definition: SecureHtmlRenderingTest.php:16
‪TYPO3\CMS\FluidStyledContent\Tests\Functional\Rendering\SecureHtmlRenderingTest\createFluidTemplateContentObject
‪createFluidTemplateContentObject(string $type, string $payload)
Definition: SecureHtmlRenderingTest.php:301