Skip to content

Commit

Permalink
Hide boot error (#2633)
Browse files Browse the repository at this point in the history
Completely redact boot error unless debug mode or display_errors is enabled. Attempt to use Flarum log file when possible. Fixes #2290
  • Loading branch information
clarkwinkelmann authored Mar 2, 2021
1 parent 56d7796 commit e37fdef
Showing 1 changed file with 54 additions and 8 deletions.
62 changes: 54 additions & 8 deletions src/Http/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@

namespace Flarum\Http;

use Flarum\Foundation\ErrorHandling\LogReporter;
use Flarum\Foundation\SiteInterface;
use Laminas\Diactoros\Response;
use Laminas\Diactoros\ServerRequest;
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
Expand Down Expand Up @@ -55,26 +57,70 @@ private function safelyBootAndGetHandler()
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 <<<ERROR
echo <<<ERROR
Flarum encountered a boot error ($type)<br />
<b>$message</b><br />
thrown in <b>$file</b> on line <b>$line</b>
<pre>$error</pre>
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.<br />';

// 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;
}
}

0 comments on commit e37fdef

Please sign in to comment.