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 16namespace TYPO3\CMS\Core\Error; 17 18use TYPO3\CMS\Core\Authentication\BackendUserAuthentication; 19use TYPO3\CMS\Core\Controller\ErrorPageController; 20use TYPO3\CMS\Core\Error\Http\AbstractClientErrorException; 21use TYPO3\CMS\Core\Messaging\AbstractMessage; 22use TYPO3\CMS\Core\Utility\GeneralUtility; 23 24/** 25 * An exception handler which catches any exception and 26 * renders an error page without backtrace (Web) or a slim 27 * message on CLI. 28 */ 29class ProductionExceptionHandler extends AbstractExceptionHandler 30{ 31 /** 32 * Default title for error messages 33 * 34 * @var string 35 */ 36 protected $defaultTitle = 'Oops, an error occurred!'; 37 38 /** 39 * Default message for error messages 40 * 41 * @var string 42 */ 43 protected $defaultMessage = ''; 44 45 /** 46 * Constructs this exception handler - registers itself as the default exception handler. 47 */ 48 public function __construct() 49 { 50 $callable = [$this, 'handleException']; 51 if (is_callable($callable)) { 52 set_exception_handler($callable); 53 } 54 } 55 56 /** 57 * Echoes an exception for the web. 58 * 59 * @param \Throwable $exception The throwable object. 60 */ 61 public function echoExceptionWeb(\Throwable $exception) 62 { 63 $this->sendStatusHeaders($exception); 64 $this->writeLogEntries($exception, self::CONTEXT_WEB); 65 echo GeneralUtility::makeInstance(ErrorPageController::class)->errorAction( 66 $this->getTitle($exception), 67 $this->getMessage($exception), 68 AbstractMessage::ERROR, 69 $this->discloseExceptionInformation($exception) ? $exception->getCode() : 0, 70 503 71 ); 72 } 73 74 /** 75 * Echoes an exception for the command line. 76 * 77 * @param \Throwable $exception The throwable object. 78 */ 79 public function echoExceptionCLI(\Throwable $exception) 80 { 81 $filePathAndName = $exception->getFile(); 82 $exceptionCodeNumber = $exception->getCode() > 0 ? '#' . $exception->getCode() . ': ' : ''; 83 $this->writeLogEntries($exception, self::CONTEXT_CLI); 84 echo LF . 'Uncaught TYPO3 Exception ' . $exceptionCodeNumber . $exception->getMessage() . LF; 85 echo 'thrown in file ' . $filePathAndName . LF; 86 echo 'in line ' . $exception->getLine() . LF . LF; 87 die(1); 88 } 89 90 /** 91 * Determines, whether Exception details should be outputted 92 * 93 * @param \Throwable $exception The throwable object. 94 * @return bool 95 */ 96 protected function discloseExceptionInformation(\Throwable $exception) 97 { 98 // Allow message to be shown in production mode if the exception is about 99 // trusted host configuration. By doing so we do not disclose 100 // any valuable information to an attacker but avoid confusions among TYPO3 admins 101 // in production context. 102 if ($exception->getCode() === 1396795884) { 103 return true; 104 } 105 // Show client error messages 40x in every case 106 if ($exception instanceof AbstractClientErrorException) { 107 return true; 108 } 109 // Only show errors if a BE user is authenticated 110 $backendUser = $this->getBackendUser(); 111 if ($backendUser instanceof BackendUserAuthentication) { 112 return ($backendUser->user['uid'] ?? 0) > 0; 113 } 114 return false; 115 } 116 117 /** 118 * Returns the title for the error message 119 * 120 * @param \Throwable $exception The throwable object. 121 * @return string 122 */ 123 protected function getTitle(\Throwable $exception) 124 { 125 if ($this->discloseExceptionInformation($exception) && method_exists($exception, 'getTitle') && $exception->getTitle() !== '') { 126 return $exception->getTitle(); 127 } 128 return $this->defaultTitle; 129 } 130 131 /** 132 * Returns the message for the error message 133 * 134 * @param \Throwable $exception The throwable object. 135 * @return string 136 */ 137 protected function getMessage(\Throwable $exception) 138 { 139 if ($this->discloseExceptionInformation($exception)) { 140 return $exception->getMessage(); 141 } 142 return $this->defaultMessage; 143 } 144} 145