From e37fdef709fffcfb7d7ec975466df294bfa68537 Mon Sep 17 00:00:00 2001 From: Clark Winkelmann Date: Tue, 2 Mar 2021 15:57:06 +0100 Subject: [PATCH] Hide boot error (#2633) Completely redact boot error unless debug mode or display_errors is enabled. Attempt to use Flarum log file when possible. Fixes #2290 --- src/Http/Server.php | 62 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 54 insertions(+), 8 deletions(-) diff --git a/src/Http/Server.php b/src/Http/Server.php index 88697f01d..e7faf3b17 100644 --- a/src/Http/Server.php +++ b/src/Http/Server.php @@ -9,6 +9,7 @@ namespace Flarum\Http; +use Flarum\Foundation\ErrorHandling\LogReporter; use Flarum\Foundation\SiteInterface; use Laminas\Diactoros\Response; use Laminas\Diactoros\ServerRequest; @@ -16,6 +17,7 @@ use Laminas\Diactoros\ServerRequestFactory; use Laminas\HttpHandlerRunner\Emitter\SapiEmitter; use Laminas\HttpHandlerRunner\RequestHandlerRunner; use Laminas\Stratigility\Middleware\ErrorResponseGenerator; +use Psr\Log\LoggerInterface; use Throwable; class Server @@ -55,26 +57,70 @@ class Server try { return $this->site->bootApp()->getRequestHandler(); } catch (Throwable $e) { - exit($this->formatBootException($e)); + // Apply response code first so whatever happens, it's set before anything is printed + http_response_code(500); + + try { + $this->cleanBootExceptionLog($e); + } catch (Throwable $e) { + // Ignore errors in logger. The important goal is to log the original error + } + + $this->fallbackBootExceptionLog($e); } } /** - * Display the most relevant information about an early exception. + * Attempt to log the boot exception in a clean way and stop the script execution. + * This means looking for debug mode and/or our normal error logger. + * There is always a risk for this to fail, + * for example if the container bindings aren't present + * or if there is a filesystem error. + * @param Throwable $error */ - private function formatBootException(Throwable $error): string + private function cleanBootExceptionLog(Throwable $error) { - $message = $error->getMessage(); - $file = $error->getFile(); - $line = $error->getLine(); - $type = get_class($error); + if (app()->has('flarum.config') && app('flarum.config')->inDebugMode()) { + // If the application booted far enough for the config to be available, we will check for debug mode + // Since the config is loaded very early, it is very likely to be available from the container + $message = $error->getMessage(); + $file = $error->getFile(); + $line = $error->getLine(); + $type = get_class($error); - return << $message
thrown in $file on line $line
$error
ERROR; + exit(1); + } elseif (app()->has(LoggerInterface::class)) { + // If the application booted far enough for the logger to be available, we will log the error there + // Considering most boot errors are related to database or extensions, the logger should already be loaded + // We check for LoggerInterface binding because it's a constructor dependency of LogReporter, + // then instantiate LogReporter through the container for automatic dependency injection + app(LogReporter::class)->report($error); + + echo 'Flarum encountered a boot error. Details have been logged to the Flarum log file.'; + exit(1); + } + } + + /** + * If the clean logging doesn't work, then we have a last opportunity. + * Here we need to be extra careful not to include anything that might be sensitive on the page. + * @param Throwable $error + * @throws Throwable + */ + private function fallbackBootExceptionLog(Throwable $error) + { + echo 'Flarum encountered a boot error. Details have been logged to the system PHP log file.
'; + + // Throwing the exception ensures it will be visible with PHP display_errors=On + // but invisible if that feature is turned off + // PHP will also automatically choose a valid place to log it based on the system settings + throw $error; } }