‪TYPO3CMS  9.5
ServerResponseCheck.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 GuzzleHttp\Client;
21 use GuzzleHttp\Exception\BadResponseException;
22 use function GuzzleHttp\Promise\settle;
23 use Psr\Http\Message\ResponseInterface;
29 
37 {
38  protected const ‪WRAP_FLAT = 1;
39  protected const ‪WRAP_NESTED = 2;
40 
44  protected ‪$useMarkup;
45 
49  protected ‪$messageQueue;
50 
54  protected ‪$assetLocation;
55 
60 
64  protected ‪$fileDeclarations;
65 
66  public function ‪__construct(bool ‪$useMarkup = true)
67  {
68  $this->useMarkup = ‪$useMarkup;
69 
70  $fileName = bin2hex(random_bytes(4));
71  $folderName = bin2hex(random_bytes(4));
72  $this->assetLocation = new ‪FileLocation(sprintf('/typo3temp/assets/%s.tmp/', $folderName));
73  $fileadminDir = rtrim(‪$GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'] ?? 'fileadmin', '/');
74  $this->fileadminLocation = new ‪FileLocation(sprintf('/%s/%s.tmp/', $fileadminDir, $folderName));
75  $this->fileDeclarations = $this->‪initializeFileDeclarations($fileName);
76  }
77 
78  public function ‪asStatus(): ‪Status
79  {
80  ‪$messageQueue = $this->‪getStatus();
81  $messages = [];
82  foreach (‪$messageQueue->‪getAllMessages() as $flashMessage) {
83  $messages[] = $flashMessage->getMessage();
84  }
85  $detailsLink = sprintf(
86  '<p><a href="%s" rel="noreferrer" target="_blank">%s</a></p>',
87  'https://docs.typo3.org/c/typo3/cms-core/master/en-us/Changelog/9.5.x/Feature-91354-IntegrateServerResponseSecurityChecks.html',
88  'Please see documentation for further details...'
89  );
91  $title = 'Potential vulnerabilities';
92  $label = $detailsLink;
93  $severity = ‪Status::ERROR;
95  $title = 'Warnings';
96  $label = $detailsLink;
97  $severity = ‪Status::WARNING;
98  }
99  return new Status(
100  'Server Response on static files',
101  $title ?? 'OK',
102  $this->‪wrapList($messages, $label ?? '', self::WRAP_NESTED),
103  $severity ?? ‪Status::OK
104  );
105  }
106 
107  public function ‪getStatus(): ‪FlashMessageQueue
108  {
109  ‪$messageQueue = new ‪FlashMessageQueue('install-server-response-check');
110  if (PHP_SAPI === 'cli-server') {
112  new ‪FlashMessage(
113  'Skipped for PHP_SAPI=cli-server',
114  'Checks skipped',
116  )
117  );
118  return ‪$messageQueue;
119  }
120  try {
121  $this->‪buildFileDeclarations();
124  } finally {
126  }
127  return ‪$messageQueue;
128  }
129 
130  protected function ‪initializeFileDeclarations(string $fileName): array
131  {
132  $cspClosure = function (ResponseInterface $response): ?StatusMessage {
133  $cspHeader = new ContentSecurityPolicyHeader(
134  $response->getHeaderLine('content-security-policy')
135  );
136 
137  if ($cspHeader->isEmpty()) {
138  return new StatusMessage(
139  'missing Content-Security-Policy for this location'
140  );
141  }
142  if (!$cspHeader->mitigatesCrossSiteScripting()) {
143  return new StatusMessage(
144  'weak Content-Security-Policy for this location "%s"',
145  $response->getHeaderLine('content-security-policy')
146  );
147  }
148  return null;
149  };
150 
151  return [
152  (new FileDeclaration($this->assetLocation, $fileName . '.html'))
153  ->withExpectedContentType('text/html')
154  ->withExpectedContent('HTML content'),
155  (new FileDeclaration($this->assetLocation, $fileName . '.wrong'))
156  ->withUnexpectedContentType('text/html')
157  ->withExpectedContent('HTML content'),
158  (new FileDeclaration($this->assetLocation, $fileName . '.html.wrong'))
159  ->withUnexpectedContentType('text/html')
160  ->withExpectedContent('HTML content'),
161  (new FileDeclaration($this->assetLocation, $fileName . '.1.svg.wrong'))
163  ->withUnexpectedContentType('image/svg+xml')
164  ->withExpectedContent('SVG content'),
165  (new FileDeclaration($this->assetLocation, $fileName . '.2.svg.wrong'))
167  ->withUnexpectedContentType('image/svg')
168  ->withExpectedContent('SVG content'),
169  (new FileDeclaration($this->assetLocation, $fileName . '.php.wrong', true))
171  ->withUnexpectedContent('PHP content'),
172  (new FileDeclaration($this->assetLocation, $fileName . '.html.txt'))
173  ->withExpectedContentType('text/plain')
174  ->withUnexpectedContentType('text/html')
175  ->withExpectedContent('HTML content'),
176  (new FileDeclaration($this->assetLocation, $fileName . '.php.txt', true))
178  ->withUnexpectedContent('PHP content'),
179  (new FileDeclaration($this->fileadminLocation, $fileName . '.html'))
181  ->withHandler($cspClosure),
182  (new FileDeclaration($this->fileadminLocation, $fileName . '.svg'))
184  ->withHandler($cspClosure),
185  ];
186  }
187 
188  protected function ‪buildFileDeclarations(): void
189  {
190  foreach ($this->fileDeclarations as $fileDeclaration) {
191  $filePath = $fileDeclaration->getFileLocation()->getFilePath();
192  if (!is_dir($filePath)) {
193  GeneralUtility::mkdir_deep($filePath);
194  }
195  file_put_contents(
196  $filePath . $fileDeclaration->getFileName(),
197  $fileDeclaration->buildContent()
198  );
199  }
200  }
201 
202  protected function ‪purgeFileDeclarations(): void
203  {
204  GeneralUtility::rmdir($this->assetLocation->getFilePath(), true);
205  GeneralUtility::rmdir($this->fileadminLocation->getFilePath(), true);
206  }
207 
209  {
210  $promises = [];
211  $client = new Client(['timeout' => 10]);
212  foreach ($this->fileDeclarations as $fileDeclaration) {
213  $promises[] = $client->requestAsync('GET', $fileDeclaration->getUrl());
214  }
215  foreach (settle($promises)->wait() as $index => $response) {
216  $fileDeclaration = $this->fileDeclarations[$index];
217  if (($response['reason'] ?? null) instanceof BadResponseException) {
219  new ‪FlashMessage(
220  sprintf(
221  '(%d): %s',
222  $response['reason']->getCode(),
223  $response['reason']->getRequest()->getUri()
224  ),
225  'HTTP warning',
227  )
228  );
229  continue;
230  }
231  if (!($response['value'] ?? null) instanceof ResponseInterface || $fileDeclaration->matches($response['value'])) {
232  continue;
233  }
235  new FlashMessage(
236  $this->‪createMismatchMessage($fileDeclaration, $response['value']),
237  'Unexpected server response',
238  $fileDeclaration->shallFail() ? ‪FlashMessage::ERROR : ‪FlashMessage::WARNING
239  )
240  );
241  }
242  }
243 
245  {
248  return;
249  }
251  new FlashMessage(
252  sprintf('All %d files processed correctly', count($this->fileDeclarations)),
253  'Expected server response',
255  )
256  );
257  }
258 
259  protected function ‪createMismatchMessage(‪FileDeclaration $fileDeclaration, ResponseInterface $response): string
260  {
261  $messageParts = array_map(
262  function (‪StatusMessage $mismatch): string {
263  return vsprintf(
264  $mismatch->‪getMessage(),
265  $this->wrapValues($mismatch->‪getValues(), '<code>', '</code>')
266  );
267  },
268  $fileDeclaration->‪getMismatches($response)
269  );
270  return $this->‪wrapList($messageParts, $fileDeclaration->‪getUrl(), self::WRAP_FLAT);
271  }
272 
273  protected function ‪wrapList(array $items, string $label, int $style): string
274  {
275  if (!$this->useMarkup) {
276  return sprintf(
277  '%s%s',
278  $label ? $label . ': ' : '',
279  implode(', ', $items)
280  );
281  }
282  if ($style === self::WRAP_NESTED) {
283  return sprintf(
284  '%s<ul>%s</ul>',
285  $label,
286  implode('', $this->‪wrapItems($items, '<li>', '</li>'))
287  );
288  }
289  return sprintf(
290  '<p>%s%s</p>',
291  $label,
292  implode('', $this->‪wrapItems($items, '<br>', ''))
293  );
294  }
295 
296  protected function ‪wrapItems(array $items, string $before, string $after): array
297  {
298  return array_map(
299  function (string $item) use ($before, $after): string {
300  return $before . $item . $after;
301  },
302  array_filter($items)
303  );
304  }
305 
306  protected function ‪wrapValues(array $values, string $before, string $after): array
307  {
308  return array_map(
309  function (string $value) use ($before, $after): string {
310  return $this->‪wrapValue($value, $before, $after);
311  },
312  array_filter($values)
313  );
314  }
315 
316  protected function ‪wrapValue(string $value, string $before, string $after): string
317  {
318  if ($this->useMarkup) {
319  return $before . htmlspecialchars($value) . $after;
320  }
321  return $value;
322  }
323 }
‪TYPO3\CMS\Install\SystemEnvironment\ServerResponse\ServerResponseCheck\$messageQueue
‪FlashMessageQueue $messageQueue
Definition: ServerResponseCheck.php:47
‪TYPO3\CMS\Install\SystemEnvironment\ServerResponse\ServerResponseCheck\$fileadminLocation
‪FileLocation $fileadminLocation
Definition: ServerResponseCheck.php:55
‪TYPO3\CMS\Install\SystemEnvironment\ServerResponse\ServerResponseCheck\asStatus
‪asStatus()
Definition: ServerResponseCheck.php:73
‪TYPO3\CMS\Install\SystemEnvironment\ServerResponse\FileDeclaration\FLAG_BUILD_SVG
‪const FLAG_BUILD_SVG
Definition: FileDeclaration.php:31
‪TYPO3\CMS\Install\SystemEnvironment\ServerResponse\ServerResponseCheck\processFileDeclarations
‪processFileDeclarations(FlashMessageQueue $messageQueue)
Definition: ServerResponseCheck.php:203
‪TYPO3\CMS\Install\SystemEnvironment\CheckInterface
Definition: CheckInterface.php:31
‪TYPO3\CMS\Reports\Status\ERROR
‪const ERROR
Definition: Status.php:27
‪TYPO3\CMS\Install\SystemEnvironment\ServerResponse\StatusMessage
Definition: StatusMessage.php:24
‪TYPO3\CMS\Install\SystemEnvironment\ServerResponse\ServerResponseCheck\buildFileDeclarations
‪buildFileDeclarations()
Definition: ServerResponseCheck.php:183
‪TYPO3\CMS\Install\SystemEnvironment\ServerResponse\ServerResponseCheck\getStatus
‪getStatus()
Definition: ServerResponseCheck.php:102
‪TYPO3\CMS\Install\SystemEnvironment\ServerResponse\FileDeclaration\getMismatches
‪StatusMessage[] getMismatches(ResponseInterface $response)
Definition: FileDeclaration.php:112
‪TYPO3\CMS\Core\Messaging\FlashMessageQueue\addMessage
‪addMessage(FlashMessage $message)
Definition: FlashMessageQueue.php:75
‪TYPO3\CMS\Install\SystemEnvironment\ServerResponse\ServerResponseCheck\WRAP_FLAT
‪const WRAP_FLAT
Definition: ServerResponseCheck.php:38
‪TYPO3\CMS\Install\SystemEnvironment\ServerResponse\ServerResponseCheck\purgeFileDeclarations
‪purgeFileDeclarations()
Definition: ServerResponseCheck.php:197
‪TYPO3\CMS\Reports\Status\OK
‪const OK
Definition: Status.php:25
‪TYPO3\CMS\Install\SystemEnvironment\ServerResponse\ContentSecurityPolicyHeader
Definition: ContentSecurityPolicyHeader.php:26
‪TYPO3\CMS\Reports\Status
Definition: Status.php:22
‪TYPO3\CMS\Core\Messaging\AbstractMessage\WARNING
‪const WARNING
Definition: AbstractMessage.php:28
‪TYPO3\CMS\Install\SystemEnvironment\ServerResponse
Definition: ContentSecurityPolicyDirective.php:18
‪TYPO3\CMS\Install\SystemEnvironment\ServerResponse\FileDeclaration\getUrl
‪string getUrl()
Definition: FileDeclaration.php:219
‪TYPO3\CMS\Install\SystemEnvironment\ServerResponse\FileLocation
Definition: FileLocation.php:30
‪TYPO3\CMS\Install\SystemEnvironment\ServerResponse\ServerResponseCheck\initializeFileDeclarations
‪initializeFileDeclarations(string $fileName)
Definition: ServerResponseCheck.php:125
‪TYPO3\CMS\Install\SystemEnvironment\ServerResponse\StatusMessage\getValues
‪string[] getValues()
Definition: StatusMessage.php:50
‪TYPO3\CMS\Install\SystemEnvironment\ServerResponse\FileDeclaration\FLAG_BUILD_SVG_DOCUMENT
‪const FLAG_BUILD_SVG_DOCUMENT
Definition: FileDeclaration.php:33
‪TYPO3\CMS\Install\SystemEnvironment\ServerResponse\ServerResponseCheck
Definition: ServerResponseCheck.php:37
‪TYPO3\CMS\Core\Messaging\FlashMessageQueue\getAllMessages
‪FlashMessage[] getAllMessages($severity=null)
Definition: FlashMessageQueue.php:107
‪TYPO3\CMS\Reports\Status\WARNING
‪const WARNING
Definition: Status.php:26
‪TYPO3\CMS\Install\SystemEnvironment\ServerResponse\ServerResponseCheck\$assetLocation
‪FileLocation $assetLocation
Definition: ServerResponseCheck.php:51
‪TYPO3\CMS\Core\Messaging\AbstractMessage\OK
‪const OK
Definition: AbstractMessage.php:27
‪TYPO3\CMS\Install\SystemEnvironment\ServerResponse\FileDeclaration\FLAG_BUILD_PHP
‪const FLAG_BUILD_PHP
Definition: FileDeclaration.php:30
‪TYPO3\CMS\Install\SystemEnvironment\ServerResponse\ServerResponseCheck\WRAP_NESTED
‪const WRAP_NESTED
Definition: ServerResponseCheck.php:39
‪TYPO3\CMS\Core\Messaging\FlashMessage
Definition: FlashMessage.php:22
‪TYPO3\CMS\Install\SystemEnvironment\ServerResponse\ServerResponseCheck\wrapItems
‪wrapItems(array $items, string $before, string $after)
Definition: ServerResponseCheck.php:291
‪TYPO3\CMS\Install\SystemEnvironment\ServerResponse\FileDeclaration
Definition: FileDeclaration.php:28
‪TYPO3\CMS\Install\SystemEnvironment\ServerResponse\ServerResponseCheck\createMismatchMessage
‪createMismatchMessage(FileDeclaration $fileDeclaration, ResponseInterface $response)
Definition: ServerResponseCheck.php:254
‪TYPO3\CMS\Install\SystemEnvironment\ServerResponse\StatusMessage\getMessage
‪string getMessage()
Definition: StatusMessage.php:42
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:5
‪TYPO3\CMS\Install\SystemEnvironment\ServerResponse\ServerResponseCheck\__construct
‪__construct(bool $useMarkup=true)
Definition: ServerResponseCheck.php:61
‪TYPO3\CMS\Install\SystemEnvironment\ServerResponse\ServerResponseCheck\finishMessageQueue
‪finishMessageQueue(FlashMessageQueue $messageQueue)
Definition: ServerResponseCheck.php:239
‪TYPO3\CMS\Install\SystemEnvironment\ServerResponse\ServerResponseCheck\$fileDeclarations
‪FileDeclaration[] $fileDeclarations
Definition: ServerResponseCheck.php:59
‪TYPO3\CMS\Install\SystemEnvironment\ServerResponse\ServerResponseCheck\$useMarkup
‪bool $useMarkup
Definition: ServerResponseCheck.php:43
‪TYPO3\CMS\Install\SystemEnvironment\ServerResponse\ServerResponseCheck\wrapList
‪wrapList(array $items, string $label, int $style)
Definition: ServerResponseCheck.php:268
‪TYPO3\CMS\Install\SystemEnvironment\ServerResponse\FileDeclaration\FLAG_BUILD_HTML_DOCUMENT
‪const FLAG_BUILD_HTML_DOCUMENT
Definition: FileDeclaration.php:32
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:45
‪TYPO3\CMS\Core\Messaging\FlashMessageQueue
Definition: FlashMessageQueue.php:25
‪TYPO3\CMS\Install\SystemEnvironment\ServerResponse\ServerResponseCheck\wrapValues
‪wrapValues(array $values, string $before, string $after)
Definition: ServerResponseCheck.php:301
‪TYPO3\CMS\Install\SystemEnvironment\ServerResponse\ServerResponseCheck\wrapValue
‪wrapValue(string $value, string $before, string $after)
Definition: ServerResponseCheck.php:311
‪TYPO3\CMS\Core\Messaging\AbstractMessage\ERROR
‪const ERROR
Definition: AbstractMessage.php:29