‪TYPO3CMS  ‪main
ProductionExceptionHandlerTest.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\MockObject\MockObject;
21 use Psr\Log\LoggerInterface;
22 use Psr\Log\LoggerTrait;
26 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
27 
28 final class ‪ProductionExceptionHandlerTest extends UnitTestCase
29 {
30  protected bool ‪$resetSingletonInstances = true;
32 
36  protected function ‪setUp(): void
37  {
38  parent::setUp();
39  $this->subject = $this->getMockBuilder(ProductionExceptionHandler::class)
40  ->onlyMethods(['discloseExceptionInformation', 'sendStatusHeaders', 'writeLogEntries'])
41  ->disableOriginalConstructor()
42  ->getMock();
43  $this->subject->method('discloseExceptionInformation')->willReturn(true);
44  }
45 
46  protected function ‪tearDown(): void
47  {
48  $previousExceptionHandler = set_exception_handler(function () {});
49  restore_exception_handler();
50  if ($previousExceptionHandler !== null) {
51  // testcase exception handler detected, remove it
52  restore_exception_handler();
53  }
54  parent::tearDown();
55  }
56 
61  {
62  $typo3InformationMock = $this->createMock(Typo3Information::class);
63  $typo3InformationMock->method('getCopyrightYear')->willReturn('1999-20XX');
64  GeneralUtility::addInstance(Typo3Information::class, $typo3InformationMock);
65  $message = '<b>b</b><script>alert(1);</script>';
66  $exception = new \Exception($message, 1476049364);
67  ob_start();
68  $this->subject->echoExceptionWeb($exception);
69  ‪$output = ob_get_contents();
70  ob_end_clean();
71  self::assertStringContainsString(htmlspecialchars($message), ‪$output);
72  self::assertStringNotContainsString($message, ‪$output);
73  }
74 
79  {
80  $typo3InformationMock = $this->createMock(Typo3Information::class);
81  $typo3InformationMock->method('getCopyrightYear')->willReturn('1999-20XX');
82  GeneralUtility::addInstance(Typo3Information::class, $typo3InformationMock);
83  $title = '<b>b</b><script>alert(1);</script>';
84  $exception = $this->getMockBuilder(\Exception::class)
85  ->addMethods(['getTitle'])
86  ->setConstructorArgs(['some message'])
87  ->getMock();
88  $exception->method('getTitle')->willReturn($title);
89  ob_start();
90  $this->subject->echoExceptionWeb($exception);
91  ‪$output = ob_get_contents();
92  ob_end_clean();
93  self::assertStringContainsString(htmlspecialchars($title), ‪$output);
94  self::assertStringNotContainsString($title, ‪$output);
95  }
96 
102  public static function ‪exampleUrlsForTokenAnonymization(): array
103  {
104  return [
105  'url with valid token' => [
106  'http://localhost/typo3/index.php?M=foo&moduleToken=5f1f7d447f22886e8ea206693b0d530ccd6b2b36',
107  'http://localhost/typo3/index.php?M=foo&moduleToken=--AnonymizedToken--',
108  ],
109  'url with valid token and encoded token' => [
110  'http://localhost/typo3/index.php?M=foo&moduleToken=5f1f7d447f22886e8ea206693b0d530ccd6b2b36&returnUrl=%2Ftypo3%2Findex%2Ephp%3FM%3Dfoo%26moduleToken%3D5f1f7d447f22886e8ea206693b0d530ccd6b2b36',
111  'http://localhost/typo3/index.php?M=foo&moduleToken=--AnonymizedToken--&returnUrl=%2Ftypo3%2Findex%2Ephp%3FM%3Dfoo%26moduleToken%3D--AnonymizedToken--',
112  ],
113  'url with valid token in the middle' => [
114  'http://localhost/typo3/index.php?M=foo&moduleToken=5f1f7d447f22886e8ea206693b0d530ccd6b2b36&param=asdf',
115  'http://localhost/typo3/index.php?M=foo&moduleToken=--AnonymizedToken--&param=asdf',
116  ],
117  'url with invalid token' => [
118  'http://localhost/typo3/index.php?M=foo&moduleToken=5f1f7d447f22886e8/e',
119  'http://localhost/typo3/index.php?M=foo&moduleToken=5f1f7d447f22886e8/e',
120  ],
121  'url with empty token' => [
122  'http://localhost/typo3/index.php?M=foo&moduleToken=',
123  'http://localhost/typo3/index.php?M=foo&moduleToken=',
124  ],
125  'url with no token' => [
126  'http://localhost/typo3/index.php?M=foo',
127  'http://localhost/typo3/index.php?M=foo',
128  ],
129  ];
130  }
131 
136  public function ‪logEntriesContainAnonymousTokens(string $originalUrl, string $expectedUrl): void
137  {
138  $typo3InformationMock = $this->createMock(Typo3Information::class);
139  $typo3InformationMock->method('getCopyrightYear')->willReturn('1999-20XX');
140  GeneralUtility::addInstance(Typo3Information::class, $typo3InformationMock);
142  $logger = new class () implements LoggerInterface {
143  use LoggerTrait;
144  public array $records = [];
145  public function log($level, string|\Stringable $message, array $context = []): void
146  {
147  $this->records[] = [
148  'level' => $level,
149  'message' => $message,
150  'context' => $context,
151  ];
152  }
153  };
154  ‪$subject->setLogger($logger);
155 
156  GeneralUtility::setIndpEnv('TYPO3_REQUEST_URL', $originalUrl);
157  ‪$GLOBALS['BE_USER'] = null;
158 
159  $exception = new \Exception('message', 1476049365);
160  ob_start();
161  ‪$subject->‪echoExceptionWeb($exception);
162  // output is caught, so it does not pollute the test run
163  ob_end_clean();
164 
165  self::assertEquals('critical', $logger->records[0]['level']);
166  self::assertEquals($expectedUrl, $logger->records[0]['context']['request_url']);
167  }
168 }
‪TYPO3\CMS\Core\Information\Typo3Information
Definition: Typo3Information.php:28
‪TYPO3\CMS\Core\Error\ProductionExceptionHandler
Definition: ProductionExceptionHandler.php:28
‪TYPO3\CMS\Core\Error\ProductionExceptionHandler\echoExceptionWeb
‪echoExceptionWeb(\Throwable $exception)
Definition: ProductionExceptionHandler.php:57
‪TYPO3\CMS\Core\Tests\Unit\Error\ProductionExceptionHandlerTest\tearDown
‪tearDown()
Definition: ProductionExceptionHandlerTest.php:46
‪TYPO3\CMS\Core\Tests\Unit\Error\ProductionExceptionHandlerTest
Definition: ProductionExceptionHandlerTest.php:29
‪TYPO3\CMS\Core\Tests\Unit\Error
Definition: DebugExceptionHandlerTest.php:18
‪TYPO3\CMS\Core\Tests\Unit\Error\ProductionExceptionHandlerTest\$resetSingletonInstances
‪bool $resetSingletonInstances
Definition: ProductionExceptionHandlerTest.php:30
‪TYPO3\CMS\Core\Tests\Unit\Error\ProductionExceptionHandlerTest\echoExceptionWebEscapesExceptionMessage
‪echoExceptionWebEscapesExceptionMessage()
Definition: ProductionExceptionHandlerTest.php:60
‪TYPO3\CMS\Core\Tests\Unit\Error\ProductionExceptionHandlerTest\exampleUrlsForTokenAnonymization
‪static string[][] exampleUrlsForTokenAnonymization()
Definition: ProductionExceptionHandlerTest.php:102
‪TYPO3\CMS\Core\Tests\Unit\Error\ProductionExceptionHandlerTest\setUp
‪setUp()
Definition: ProductionExceptionHandlerTest.php:36
‪TYPO3\CMS\Core\Tests\Unit\Error\ProductionExceptionHandlerTest\echoExceptionWebEscapesExceptionTitle
‪echoExceptionWebEscapesExceptionTitle()
Definition: ProductionExceptionHandlerTest.php:78
‪$output
‪$output
Definition: annotationChecker.php:119
‪TYPO3\CMS\Core\Tests\Unit\Error\ProductionExceptionHandlerTest\$subject
‪ProductionExceptionHandler &MockObject $subject
Definition: ProductionExceptionHandlerTest.php:31
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:51
‪TYPO3\CMS\Core\Tests\Unit\Error\ProductionExceptionHandlerTest\logEntriesContainAnonymousTokens
‪logEntriesContainAnonymousTokens(string $originalUrl, string $expectedUrl)
Definition: ProductionExceptionHandlerTest.php:136