‪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\Attributes\DataProvider;
21 use PHPUnit\Framework\Attributes\Test;
22 use PHPUnit\Framework\MockObject\MockObject;
23 use Psr\Log\LoggerInterface;
24 use Psr\Log\LoggerTrait;
29 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
30 
31 final class ‪ProductionExceptionHandlerTest extends UnitTestCase
32 {
33  protected bool ‪$resetSingletonInstances = true;
35 
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 
57  #[Test]
59  {
60  $typo3InformationMock = $this->createMock(Typo3Information::class);
61  $typo3InformationMock->method('getCopyrightYear')->willReturn('1999-20XX');
62  GeneralUtility::addInstance(Typo3Information::class, $typo3InformationMock);
63  $message = '<b>b</b><script>alert(1);</script>';
64  $exception = new \Exception($message, 1476049364);
65  ob_start();
66  $this->subject->echoExceptionWeb($exception);
67  ‪$output = ob_get_contents();
68  ob_end_clean();
69  self::assertStringContainsString(htmlspecialchars($message), ‪$output);
70  self::assertStringNotContainsString($message, ‪$output);
71  }
72 
73  #[Test]
75  {
76  $typo3InformationMock = $this->createMock(Typo3Information::class);
77  $typo3InformationMock->method('getCopyrightYear')->willReturn('1999-20XX');
78  GeneralUtility::addInstance(Typo3Information::class, $typo3InformationMock);
79  $title = '<b>b</b><script>alert(1);</script>';
80  $exception = $this->createMock(StatusException::class);
81  $exception->method('getTitle')->willReturn($title);
82  ob_start();
83  $this->subject->echoExceptionWeb($exception);
84  ‪$output = ob_get_contents();
85  ob_end_clean();
86  self::assertStringContainsString(htmlspecialchars($title), ‪$output);
87  self::assertStringNotContainsString($title, ‪$output);
88  }
89 
90  public static function ‪exampleUrlsForTokenAnonymization(): array
91  {
92  return [
93  'url with valid token' => [
94  'http://localhost/typo3/index.php?M=foo&moduleToken=5f1f7d447f22886e8ea206693b0d530ccd6b2b36',
95  'http://localhost/typo3/index.php?M=foo&moduleToken=--AnonymizedToken--',
96  ],
97  'url with valid token and encoded token' => [
98  'http://localhost/typo3/index.php?M=foo&moduleToken=5f1f7d447f22886e8ea206693b0d530ccd6b2b36&returnUrl=%2Ftypo3%2Findex%2Ephp%3FM%3Dfoo%26moduleToken%3D5f1f7d447f22886e8ea206693b0d530ccd6b2b36',
99  'http://localhost/typo3/index.php?M=foo&moduleToken=--AnonymizedToken--&returnUrl=%2Ftypo3%2Findex%2Ephp%3FM%3Dfoo%26moduleToken%3D--AnonymizedToken--',
100  ],
101  'url with valid token in the middle' => [
102  'http://localhost/typo3/index.php?M=foo&moduleToken=5f1f7d447f22886e8ea206693b0d530ccd6b2b36&param=asdf',
103  'http://localhost/typo3/index.php?M=foo&moduleToken=--AnonymizedToken--&param=asdf',
104  ],
105  'url with invalid token' => [
106  'http://localhost/typo3/index.php?M=foo&moduleToken=5f1f7d447f22886e8/e',
107  'http://localhost/typo3/index.php?M=foo&moduleToken=5f1f7d447f22886e8/e',
108  ],
109  'url with empty token' => [
110  'http://localhost/typo3/index.php?M=foo&moduleToken=',
111  'http://localhost/typo3/index.php?M=foo&moduleToken=',
112  ],
113  'url with no token' => [
114  'http://localhost/typo3/index.php?M=foo',
115  'http://localhost/typo3/index.php?M=foo',
116  ],
117  ];
118  }
119 
120  #[DataProvider('exampleUrlsForTokenAnonymization')]
121  #[Test]
122  public function ‪logEntriesContainAnonymousTokens(string $originalUrl, string $expectedUrl): void
123  {
124  $typo3InformationMock = $this->createMock(Typo3Information::class);
125  $typo3InformationMock->method('getCopyrightYear')->willReturn('1999-20XX');
126  GeneralUtility::addInstance(Typo3Information::class, $typo3InformationMock);
128  $logger = new class () implements LoggerInterface {
129  use LoggerTrait;
130  public array $records = [];
131  public function log($level, string|\Stringable $message, array $context = []): void
132  {
133  $this->records[] = [
134  'level' => $level,
135  'message' => $message,
136  'context' => $context,
137  ];
138  }
139  };
140  ‪$subject->setLogger($logger);
141 
142  GeneralUtility::setIndpEnv('TYPO3_REQUEST_URL', $originalUrl);
143  ‪$GLOBALS['BE_USER'] = null;
144 
145  $exception = new \Exception('message', 1476049365);
146  ob_start();
147  ‪$subject->‪echoExceptionWeb($exception);
148  // output is caught, so it does not pollute the test run
149  ob_end_clean();
150 
151  self::assertEquals('critical', $logger->records[0]['level']);
152  self::assertEquals($expectedUrl, $logger->records[0]['context']['request_url']);
153  }
154 }
‪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:32
‪TYPO3\CMS\Core\Tests\Unit\Error
Definition: DebugExceptionHandlerTest.php:18
‪TYPO3\CMS\Core\Error\Http\StatusException
Definition: StatusException.php:26
‪TYPO3\CMS\Core\Tests\Unit\Error\ProductionExceptionHandlerTest\$resetSingletonInstances
‪bool $resetSingletonInstances
Definition: ProductionExceptionHandlerTest.php:33
‪TYPO3\CMS\Core\Tests\Unit\Error\ProductionExceptionHandlerTest\echoExceptionWebEscapesExceptionMessage
‪echoExceptionWebEscapesExceptionMessage()
Definition: ProductionExceptionHandlerTest.php:58
‪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:74
‪$output
‪$output
Definition: annotationChecker.php:114
‪TYPO3\CMS\Core\Tests\Unit\Error\ProductionExceptionHandlerTest\$subject
‪ProductionExceptionHandler &MockObject $subject
Definition: ProductionExceptionHandlerTest.php:34
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Tests\Unit\Error\ProductionExceptionHandlerTest\exampleUrlsForTokenAnonymization
‪static exampleUrlsForTokenAnonymization()
Definition: ProductionExceptionHandlerTest.php:90
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Core\Tests\Unit\Error\ProductionExceptionHandlerTest\logEntriesContainAnonymousTokens
‪logEntriesContainAnonymousTokens(string $originalUrl, string $expectedUrl)
Definition: ProductionExceptionHandlerTest.php:122