‪TYPO3CMS  ‪main
SecurityTest.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use PHPUnit\Framework\Attributes\DataProvider;
21 use PHPUnit\Framework\Attributes\Test;
27 use TYPO3\TestingFramework\Core\Functional\Framework\DataHandling\ActionService;
28 use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
29 
33 final class ‪SecurityTest extends FunctionalTestCase
34 {
35  private ActionService ‪$actionService;
36 
37  protected array ‪$coreExtensionsToLoad = ['rte_ckeditor'];
38 
39  protected function ‪setUp(): void
40  {
41  parent::setUp();
42  $this->importCSVDataSet(__DIR__ . '/DataSet/pages.csv');
43  $this->importCSVDataSet(__DIR__ . '/../../Fixtures/be_users_admin.csv');
44  $backendUser = $this->setUpBackendUser(1);
45  $backendUser->workspace = 0;
46  GeneralUtility::makeInstance(Context::class)
47  ->setAspect('workspace', new ‪WorkspaceAspect(0));
48  ‪$GLOBALS['LANG'] = $this->get(LanguageServiceFactory::class)->createFromUserPreferences($backendUser);
49 
50  $this->actionService = GeneralUtility::makeInstance(ActionService::class);
51  }
52 
53  public static function ‪crossSiteScriptingDataProvider(): array
54  {
55  return [
56  [
57  'The "test" value might be =< x or > y...', // submitted payload
58  [
59  // @todo issue in `masterminds/html5`, first `<` should be parsed and encoded to `&lt;`
60  'The "test" value might be = x or &gt; y...', // default processing, HTML Sanitizer enabled
61  'The "test" value might be =< x or > y...', // default processing, HTML Sanitizer disabled
62  ],
63  ],
64  [
65  '<p undefined="<not-allowed>"></p>',
66  [
67  '<p></p>',
68  '<p></p>',
69  ],
70  ],
71  [
72  '<p undefined=<not-allowed>></p>',
73  [
74  '<p></p>',
75  '<p></p>',
76  ],
77  ],
78  [
79  '<p title="<encode-me>"></p>',
80  [
81  '<p title="&lt;encode-me&gt;"></p>',
82  '<p title="&lt;encode-me&gt;"></p>',
83  ],
84  ],
85  [
86  '<p title=<encode-me>></p>',
87  [
88  '<p title="&lt;encode-me&gt;"></p>',
89  '<p title="&lt;encode-me&gt;"></p>',
90  ],
91  ],
92  [
93  '<p title="""></p>',
94  [
95  '<p></p>',
96  '<p></p>',
97  ],
98  ],
99  [
100  '<p title="title"></p>',
101  [
102  '<p title="title"></p>',
103  '<p title="title"></p>',
104  ],
105  ],
106  [
107  '<p title="escape"<img src=src>"></p>',
108  [
109  '<p title="escape">"&gt;</p>',
110  '<p title="escape">"></p>',
111  ],
112  ],
113  [
114  '<p title=""""></p>',
115  [
116  '<p title></p>',
117  '<p title></p>',
118  ],
119  ],
120  [
121  '<p title=""anything"></p>',
122  [
123  '<p></p>',
124  '<p></p>',
125  ],
126  ],
127  [
128  '<p title=""anything""></p>',
129  [
130  '<p title></p>',
131  '<p title></p>',
132  ],
133  ],
134  [
135  '<p title="anything""></p>',
136  [
137  '<p></p>',
138  '<p></p>',
139  ],
140  ],
141  [
142  '<not-allowed><p title="</not-allowed><img src=x onerror=alert(1)><img src=x onerror=alert(2)>',
143  [
144  '<p>&lt;not-allowed&gt;</p>' . "\r\n" . '<p></p>',
145  '<p>&lt;not-allowed&gt;</p>' . "\r\n" . '<p></p>',
146  ],
147  ],
148  [
149  '<not-allowed><p title="</not-allowed><img src="x" onerror="alert(1)"><img src="x" onerror="alert(2)">',
150  [
151  '<p>&lt;not-allowed&gt;</p>' . "\r\n" . '<p></p>',
152  '<p>&lt;not-allowed&gt;</p>' . "\r\n" . '<p></p>',
153  ],
154  ],
155  [
156  '<script>alert(3)</script>',
157  [
158  '&lt;script&gt;alert(3)&lt;/script&gt;',
159  '&lt;script&gt;alert(3)&lt;/script&gt;',
160  ],
161  ],
162  [
163  '<p><script>alert(3)</script></p>',
164  [
165  '<p>&lt;script&gt;alert(3)&lt;/script&gt;</p>',
166  '<p>&lt;script&gt;alert(3)&lt;/script&gt;</p>',
167  ],
168  ],
169  [
170  '<title>title</title>',
171  [
172  '&lt;title&gt;title&lt;/title&gt;',
173  '&lt;title&gt;title&lt;/title&gt;',
174  ],
175  ],
176  [
177  '<p><title>title</title></p>',
178  [
179  '<p>&lt;title&gt;title&lt;/title&gt;</p>',
180  '<p>&lt;title&gt;title&lt;/title&gt;</p>',
181  ],
182  ],
183  [
184  '<font face="a" color="b" onmouseover="alert(1);">text</font>'
185  . '<img src="x" alt="test" onerror="alert(2)">',
186  [
187  '<font face="a" color="b">text</font>'
188  . '<img src="x" alt="test">',
189  // @todo "expected" for the time being without using HTML Sanitizer
190  '<font face="a" color="b" onmouseover="alert(1);">text</font>'
191  . '<img src="x" alt="test" onerror="alert(2)">',
192  ],
193  ],
194  [
195  '<p>'
196  . '<font face="a" color="b" onmouseover="alert(1);">text</font>'
197  . '<img src="x" alt="test" onerror="alert(2)">'
198  . '</p>',
199  [
200  '<p><font face="a" color="b">text</font>'
201  . '<img src="x" alt="test"></p>',
202  // @todo "expected" for the time being without using HTML Sanitizer
203  '<p><font face="a" color="b" onmouseover="alert(1);">text</font>'
204  . '<img src="x" alt="test" onerror="alert(2)"></p>',
205  ],
206  ],
207  [
208  '<p><a href="https://typo3.org" target="_blank" rel="noreferrer" role="button" onmouseover="alert(1)">text</a></p>',
209  [
210  '<p><a href="https://typo3.org" target="_blank" rel="noreferrer" role="button">text</a></p>',
211  // @todo "expected" for the time being without using HTML Sanitizer
212  '<p><a href="https://typo3.org" target="_blank" rel="noreferrer" role="button" onmouseover="alert(1)">text</a></p>',
213  ],
214  ],
215  [
216  '<p><a href="t3://page?uid=1" target="_blank" rel="noreferrer" role="button" onmouseover="alert(1)">text</a></p>',
217  [
218  '<p><a href="t3://page?uid=1" target="_blank" rel="noreferrer" role="button">text</a></p>',
219  // @todo "expected" for the time being without using HTML Sanitizer
220  '<p><a href="t3://page?uid=1" target="_blank" rel="noreferrer" role="button" onmouseover="alert(1)">text</a></p>',
221  ],
222  ],
223  [
224  '<?xml >s<img src=x onerror=alert(1)> ?>',
225  [
226  '&lt;?xml &gt;s&lt;img src=x onerror=alert(1)&gt; ?&gt;',
227  '<?xml >s<img src=x onerror=alert(1)> ?>',
228  ],
229  ],
230  ];
231  }
232 
238  #[DataProvider('crossSiteScriptingDataProvider')]
239  #[Test]
240  public function ‪markupIsSanitizedForContentBodytextWithHtmlSanitizerEnabled(string $input, array $expectations): void
241  {
242  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['features']['security.backend.htmlSanitizeRte'] = true;
243  $newIds = $this->actionService->createNewRecord('tt_content', 1, [
244  'CType' => 'text',
245  'bodytext' => $input,
246  ]);
247  $contentId = current($newIds['tt_content'] ?? 0);
248  self::assertGreaterThan(0, $contentId, 'Could not resolve content id');
249 
250  $connection = GeneralUtility::makeInstance(ConnectionPool::class)
251  ->getConnectionForTable('tt_content');
252  ‪$record = $connection->select(['bodytext'], 'tt_content', ['uid' => (int)$contentId])->fetchAssociative();
253  $bodytext = ‪$record['bodytext'] ?? null;
254 
255  $expectation = $expectations[0];
256  self::assertSame($expectation, $bodytext, sprintf('Given markup: `%s`', $input));
257  }
258 
264  #[DataProvider('crossSiteScriptingDataProvider')]
265  #[Test]
266  public function ‪markupIsSanitizedForContentBodytextWithHtmlSanitizerDisabled(string $input, array $expectations): void
267  {
268  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['features']['security.backend.htmlSanitizeRte'] = false;
269  $newIds = $this->actionService->createNewRecord('tt_content', 1, [
270  'CType' => 'text',
271  'bodytext' => $input,
272  ]);
273  $contentId = current($newIds['tt_content'] ?? 0);
274  self::assertGreaterThan(0, $contentId, 'Could not resolve content id');
275 
276  $connection = GeneralUtility::makeInstance(ConnectionPool::class)
277  ->getConnectionForTable('tt_content');
278  ‪$record = $connection->select(['bodytext'], 'tt_content', ['uid' => (int)$contentId])->fetchAssociative();
279  $bodytext = ‪$record['bodytext'] ?? null;
280 
281  $expectation = $expectations[1];
282  self::assertSame($expectation, $bodytext, sprintf('Given markup: `%s`', $input));
283  }
284 }
‪TYPO3\CMS\Core\Localization\LanguageServiceFactory
Definition: LanguageServiceFactory.php:25
‪TYPO3\CMS\Core\Tests\Functional\DataHandling\DataHandler
Definition: CheckboxValidationTest.php:18
‪TYPO3\CMS\Core\Context\WorkspaceAspect
Definition: WorkspaceAspect.php:31
‪TYPO3\CMS\Core\Tests\Functional\DataHandling\DataHandler\SecurityTest\$coreExtensionsToLoad
‪array $coreExtensionsToLoad
Definition: SecurityTest.php:37
‪TYPO3\CMS\Core\Context\Context
Definition: Context.php:54
‪TYPO3\CMS\Core\Tests\Functional\DataHandling\DataHandler\SecurityTest\markupIsSanitizedForContentBodytextWithHtmlSanitizerDisabled
‪markupIsSanitizedForContentBodytextWithHtmlSanitizerDisabled(string $input, array $expectations)
Definition: SecurityTest.php:266
‪TYPO3\CMS\Core\Tests\Functional\DataHandling\DataHandler\SecurityTest\markupIsSanitizedForContentBodytextWithHtmlSanitizerEnabled
‪markupIsSanitizedForContentBodytextWithHtmlSanitizerEnabled(string $input, array $expectations)
Definition: SecurityTest.php:240
‪TYPO3\CMS\Webhooks\Message\$record
‪identifier readonly int readonly array $record
Definition: PageModificationMessage.php:36
‪TYPO3\CMS\Core\Tests\Functional\DataHandling\DataHandler\SecurityTest\$actionService
‪ActionService $actionService
Definition: SecurityTest.php:35
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Tests\Functional\DataHandling\DataHandler\SecurityTest\crossSiteScriptingDataProvider
‪static crossSiteScriptingDataProvider()
Definition: SecurityTest.php:53
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Core\Tests\Functional\DataHandling\DataHandler\SecurityTest\setUp
‪setUp()
Definition: SecurityTest.php:39
‪TYPO3\CMS\Core\Tests\Functional\DataHandling\DataHandler\SecurityTest
Definition: SecurityTest.php:34