ErrorHandlerTrait.php 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. <?php
  2. namespace Tools\Error;
  3. use Cake\Controller\Exception\AuthSecurityException;
  4. use Cake\Controller\Exception\MissingActionException;
  5. use Cake\Controller\Exception\SecurityException;
  6. use Cake\Core\Configure;
  7. use Cake\Datasource\Exception\InvalidPrimaryKeyException;
  8. use Cake\Datasource\Exception\RecordNotFoundException;
  9. use Cake\Http\Exception\BadRequestException;
  10. use Cake\Http\Exception\ConflictException;
  11. use Cake\Http\Exception\GoneException;
  12. use Cake\Http\Exception\InvalidCsrfTokenException;
  13. use Cake\Http\Exception\MethodNotAllowedException;
  14. use Cake\Http\Exception\MissingControllerException;
  15. use Cake\Http\Exception\NotAcceptableException;
  16. use Cake\Http\Exception\NotFoundException;
  17. use Cake\Http\Exception\UnauthorizedException;
  18. use Cake\Http\Exception\UnavailableForLegalReasonsException;
  19. use Cake\Routing\Exception\MissingRouteException;
  20. use Cake\View\Exception\MissingTemplateException;
  21. use Cake\View\Exception\MissingViewException;
  22. use InvalidArgumentException;
  23. use Psr\Http\Message\ServerRequestInterface;
  24. use Throwable;
  25. /**
  26. * @property array $_config
  27. */
  28. trait ErrorHandlerTrait {
  29. /**
  30. * List of exceptions that are actually be treated as external 404s.
  31. * They should not go into the normal error log, but a separate 404 one.
  32. *
  33. * @var array<string>
  34. */
  35. protected static array $blacklist = [
  36. InvalidPrimaryKeyException::class,
  37. InvalidArgumentException::class,
  38. NotFoundException::class,
  39. MethodNotAllowedException::class,
  40. NotAcceptableException::class,
  41. RecordNotFoundException::class,
  42. BadRequestException::class,
  43. GoneException::class,
  44. ConflictException::class,
  45. InvalidCsrfTokenException::class,
  46. UnauthorizedException::class,
  47. MissingControllerException::class,
  48. MissingActionException::class,
  49. MissingRouteException::class,
  50. MissingViewException::class,
  51. MissingTemplateException::class,
  52. UnavailableForLegalReasonsException::class,
  53. SecurityException::class,
  54. AuthSecurityException::class,
  55. ];
  56. /**
  57. * By design, these exceptions are also 404 with a valid internal referer.
  58. *
  59. * @var array<string>
  60. */
  61. protected static array $evenWithReferer = [
  62. AuthSecurityException::class,
  63. ];
  64. /**
  65. * @param \Throwable $exception
  66. * @param \Psr\Http\Message\ServerRequestInterface|null $request
  67. * @return bool
  68. */
  69. protected function is404(Throwable $exception, ?ServerRequestInterface $request = null): bool {
  70. $blacklist = static::$blacklist;
  71. if (isset($this->_config['log404'])) {
  72. $blacklist = (array)$this->_config['log404'];
  73. }
  74. if (!$blacklist) {
  75. return false;
  76. }
  77. $class = get_class($exception);
  78. if (!$this->isBlacklisted($class, $blacklist)) {
  79. return false;
  80. }
  81. if (!$request || $this->isBlacklistedEvenWithReferer($class)) {
  82. return true;
  83. }
  84. $referer = $request->getHeaderLine('Referer');
  85. $baseUrl = Configure::read('App.fullBaseUrl');
  86. if (str_starts_with($referer, $baseUrl) && $baseUrl . $request->getRequestTarget() !== $referer) {
  87. return false;
  88. }
  89. return true;
  90. }
  91. /**
  92. * @param string $class
  93. * @param array<string> $blacklist
  94. * @return bool
  95. */
  96. protected function isBlacklisted(string $class, array $blacklist): bool {
  97. // Quick string comparison first
  98. if (in_array($class, $blacklist, true)) {
  99. return true;
  100. }
  101. // Deep instance of checking
  102. foreach ($blacklist as $blacklistedClass) {
  103. if ($class instanceof $blacklistedClass) {
  104. return true;
  105. }
  106. }
  107. return false;
  108. }
  109. /**
  110. * Is a 404 even with referer present.
  111. *
  112. * @param string $class
  113. * @return bool
  114. */
  115. protected function isBlacklistedEvenWithReferer(string $class): bool {
  116. return $this->isBlacklisted($class, static::$evenWithReferer);
  117. }
  118. }