From 830e152e966e7ebc91485b92c8f8dce2574ea467 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Tim=20D=C3=BCsterhus?= Date: Wed, 22 Sep 2021 14:23:35 +0200 Subject: [PATCH] Synchronize error handling in install.php with core.functions.php The previous error handling: a) was buggy. It failed during stack trace printing under certain circumstances. b) was outdated with regard to the design. --- wcfsetup/install.php | 850 ++++++++++++++++++++++++------------------- 1 file changed, 476 insertions(+), 374 deletions(-) diff --git a/wcfsetup/install.php b/wcfsetup/install.php index 5aac593184..1f2563977c 100644 --- a/wcfsetup/install.php +++ b/wcfsetup/install.php @@ -70,334 +70,6 @@ class SystemException extends \Exception { * This method is called by WCF::handleException(). */ public function show() { - /* - * A notice on the HTML used below: - * - * It might appear a bit weird to use

all over the place where semantically - * other elements would fit in way better. The reason behind this is that we avoid - * inheriting unwanted styles (e.g. exception displayed in an overlay) and that - * the output needs to be properly readable when copied & pasted somewhere. - * - * Besides the visual appearance, the output was built to provide a maximum of - * compatibility and readability when pasted somewhere else, e.g. a WYSIWYG editor - * without the potential of messing up the formatting and thus harming the readability. - */ -?> - - - - Fatal Error: <?php echo htmlentities($this->getMessage()); ?> - - - - -

-
-
-

An error has occurred

-
-
- -
-

System Information

-
    -
  • -

    PHP Version:

    -

    -
  • -
  • -

    WoltLab Suite Core:

    -

    5.3

    -
  • -
  • -

    Peak Memory Usage:

    -

    /

    -
  • -
  • -

    Request URI:

    -

    -
  • -
  • -

    Referrer:

    -

    -
  • -
  • -

    User Agent:

    -

    -
  • -
-
- - getTrace(); - if (isset($trace[0]['function']) && $trace[0]['function'] === 'handleException') { - // ignore repacked exception - continue; - } - - ?> -
-

getPrevious() && !$first) { echo "Original "; } else if ($e->getPrevious() && $first) { echo "Final "; } ?>Error

- getDescription()) { ?> -

getDescription(); ?>

- -
    -
  • -

    Error Type:

    -

    -
  • -
  • -

    Error Message:

    -

    getMessage()); ?>

    -
  • - getCode()) { ?> -
  • -

    Error Code:

    -

    getCode()); ?>

    -
  • - -
  • -

    File:

    -

    getFile()); ?> (getLine(); ?>)

    -
  • - -
  • -

    Stack Trace:

    -
      - getTrace(); - for ($i = 0, $max = count($trace); $i < $max; $i++) { - ?> -
    • -
    • - 5) return "[ ".count($keys)." items ]"; - return '[ '.implode(', ', array_map(function ($item) { - return $item.' => '; - }, $keys)).']'; - case 'object': - return get_class($item); - } - - throw new \LogicException('Unreachable'); - }, $trace[$i]['args'])); - echo ')
    • '; - } - ?> -
    -
  • -
-
- getPrevious()); - ?> -
- - - -getTrace(); + + return array_map(function ($item) use ($ignorePaths) { + if (!isset($item['file'])) $item['file'] = '[internal function]'; + if (!isset($item['line'])) $item['line'] = '?'; + if (!isset($item['class'])) $item['class'] = ''; + if (!isset($item['type'])) $item['type'] = ''; + if (!isset($item['args'])) $item['args'] = []; + + try { + if (!empty($item['args'])) { + if ($item['class']) { + $function = new \ReflectionMethod($item['class'], $item['function']); + } + else { + $function = new \ReflectionFunction($item['function']); + } + + $parameters = $function->getParameters(); + $i = 0; + foreach ($parameters as $parameter) { + $isSensitive = false; + if ( + \method_exists($parameter, 'getAttributes') + && !empty($parameter->getAttributes(\wcf\SensitiveArgument::class)) + ) { + $isSensitive = true; + } + if (\preg_match( + '/(?:^(?:password|passphrase|secret)|(?:Password|Passphrase|Secret))/', + $parameter->getName() + )) { + $isSensitive = true; + } + + if ($isSensitive && isset($item['args'][$i])) { + $item['args'][$i] = '[redacted]'; + } + $i++; + } + + // strip database credentials + if ( + preg_match('~\\\\?wcf\\\\system\\\\database\\\\[a-zA-Z]*Database~', $item['class']) + || $item['class'] === 'PDO' + ) { + if ($item['function'] === '__construct') { + $item['args'] = array_map(function () { + return '[redacted]'; + }, $item['args']); + } + } + } + } catch (\Throwable $e) { + $item['args'] = array_map(function () { + return '[error_during_sanitization]'; + }, $item['args']); + } + + if (!$ignorePaths) { + $item['args'] = array_map(function ($item) { + if (!is_string($item)) return $item; + + if (preg_match('~^('.preg_quote($_SERVER['DOCUMENT_ROOT'], '~').'|'.preg_quote(WCF_DIR, '~').')~', $item)) { + $item = sanitizePath($item); + } + + return $item; + }, $item['args']); + + $item['file'] = sanitizePath($item['file']); + } + + return $item; + }, $trace); +} + +function printException($e) { + $exceptionTitle = 'An error has occurred'; + $exceptionSubtitle = ''; + $exceptionExplanation = ''; + $exceptionID = ''; + ?> + + + + + Fatal Error: <?php echo htmlspecialchars($e->getMessage()); ?> + + Fatal Error + + + + + +
+
+
+

+

+
+
+ +
+ +
+ +
+

System Information

+
    +
  • +

    PHP Version:

    +

    +
  • +
  • +

    WoltLab Suite Core:

    +

    n/a

    +
  • +
  • +

    Peak Memory Usage:

    +

    MiB

    +
  • +
  • +

    Request URI:

    +

    +
  • +
  • +

    Referrer:

    +

    +
  • +
  • +

    User Agent:

    +

    +
  • +
+
+ + getPrevious()); + + $e = array_pop($exceptions); + do { + ?> +
+

Error

+ getDescription()) { ?> +

getDescription(); ?>

+ +
    +
  • +

    Error Type:

    +

    +
  • +
  • +

    Error Message:

    +

    getMessage()); ?>

    +
  • + getCode()) { ?> +
  • +

    Error Code:

    +

    getCode()); ?>

    +
  • + +
  • +

    File:

    +

    getFile())); ?> (getLine(); ?>)

    +
  • + + show(); + ob_end_clean(); + + $reflection = new \ReflectionClass($e); + $property = $reflection->getProperty('information'); + $property->setAccessible(true); + if ($property->getValue($e)) { + throw new \Exception("Using the 'information' property of SystemException is not supported any more."); + } + } + if ($e instanceof IExtraInformationException) { + foreach ($e->getExtraInformation() as list($key, $value)) { + ?> +
  • +

    :

    +

    +
  • + +
  • +

    Template Context:

    +
    +
  • + +
  • +

    Stack Trace:

    +
      + +
    • +
    • + 5) return "[ ".count($keys)." items ]"; + return '[ '.implode(', ', array_map(function ($item) { + return $item.' => '; + }, $keys)).']'; + case 'object': + return get_class($item); + case 'resource': + return 'resource('.get_resource_type($item).')'; + case 'resource (closed)': + return 'resource (closed)'; + } + + throw new \LogicException('Unreachable'); + }, $trace[$i]['args'])); + echo ')
    • '; + } + ?> +
    +
  • +
+
+ + +
+ + + show(); exit; } // repacking - (new SystemException($e->getMessage(), $e->getCode(), '', $e))->show(); + printException($e); exit; } catch (\Throwable $exception) { @@ -480,26 +599,13 @@ function handleException($e) { } } -/** - * Catches php errors and throws instead a system exception. - * - * @param int $errorNo - * @param string $message - * @param string $filename - * @param int $lineNo - * @throws SystemException - */ -function handleError($errorNo, $message, $filename, $lineNo) { - if (!(error_reporting() & $errorNo)) return; - $type = 'error'; - switch ($errorNo) { - case 2: $type = 'warning'; - break; - case 8: $type = 'notice'; - break; +function handleError($severity, $message, $file, $line) { + // this is necessary for the shut-up operator + if (!(\error_reporting() & $severity)) { + return; } - - throw new SystemException('PHP '.$type.' in file '.$filename.' ('.$lineNo.'): '.$message, 0); + + throw new ErrorException($message, 0, $severity, $file, $line); } /** @noinspection PhpMultipleClassesDeclarationsInOneFile */ @@ -518,11 +624,8 @@ class BasicFileUtil { /** * Tries to find the temp folder. - * - * @return string - * @throws SystemException */ - public static function getTempFolder() { + public static function getTempFolder(): string { // use tmp folder in document root by default if (!empty($_SERVER['DOCUMENT_ROOT'])) { if (strpos($_SERVER['DOCUMENT_ROOT'], 'strato') !== false) { @@ -533,7 +636,7 @@ class BasicFileUtil { try { self::makeWritable($_SERVER['DOCUMENT_ROOT'].'/tmp/'); } - catch (SystemException $e) {} + catch (\Exception $e) {} } } if (@file_exists($_SERVER['DOCUMENT_ROOT'].'/tmp') && @is_writable($_SERVER['DOCUMENT_ROOT'].'/tmp')) { @@ -566,16 +669,14 @@ class BasicFileUtil { return $path; } else { - throw new SystemException('There is no access to the system temporary folder due to an unknown reason and no user specific temporary folder exists in '.INSTALL_SCRIPT_DIR.'! This is a misconfiguration of your webserver software! Please create a folder called '.$path.' using your favorite ftp program, make it writable and then retry this installation.'); + throw new \Exception('There is no access to the system temporary folder due to an unknown reason and no user specific temporary folder exists in '.INSTALL_SCRIPT_DIR.'! This is a misconfiguration of your webserver software! Please create a folder called '.$path.' using your favorite ftp program, make it writable and then retry this installation.'); } } /** * Returns the temp folder for the installation. - * - * @return string */ - public static function getInstallTempFolder() { + public static function getInstallTempFolder(): string { $dir = self::getTempFolder() . TMP_FILE_PREFIX . '/'; @mkdir($dir); self::makeWritable($dir); @@ -588,7 +689,7 @@ class BasicFileUtil { * permissions and goes up until 0666 for files and 0777 for directories. * * @param string $filename - * @throws SystemException + * @throws \Exception */ public static function makeWritable($filename) { if (!file_exists($filename)) { @@ -641,7 +742,7 @@ class BasicFileUtil { } if (!is_writable($filename)) { - throw new SystemException("Unable to make '".$filename."' writable. This is a misconfiguration of your server, please contact your system administrator or hosting provider."); + throw new \Exception("Unable to make '".$filename."' writable. This is a misconfiguration of your server, please contact your system administrator or hosting provider."); } } @@ -649,9 +750,8 @@ class BasicFileUtil { * Adds a trailing slash to the given path. * * @param string $path - * @return string */ - public static function addTrailingSlash($path) { + public static function addTrailingSlash($path): string { return rtrim($path, '/').'/'; } @@ -661,9 +761,8 @@ class BasicFileUtil { * necessary. * * @param string $path - * @return bool */ - public static function makePath($path) { + public static function makePath($path): bool { // directory already exists, abort if (file_exists($path)) { return false; @@ -1201,7 +1300,7 @@ if (!file_exists(TMP_DIR . 'install/files/lib/system/WCFSetup.class.php')) { $tar = new Tar(SETUP_FILE); $contentList = $tar->getContentList(); if (empty($contentList)) { - throw new SystemException("Cannot unpack 'WCFSetup.tar.gz'. File is probably broken."); + throw new \Exception("Cannot unpack 'WCFSetup.tar.gz'. File is probably broken."); } foreach ($contentList as $file) { @@ -1228,8 +1327,11 @@ if (!file_exists(TMP_DIR . 'install/files/lib/system/WCFSetup.class.php')) { BasicFileUtil::makeWritable(TMP_DIR . 'setup/template/compiled/'); } -if (!class_exists('wcf\system\WCFSetup')) { - throw new SystemException("Cannot find class 'WCFSetup'"); +if (!class_exists(\wcf\system\WCFSetup::class)) { + throw new \Exception(\sprintf( + "Cannot find class '%s'", + \wcf\system\WCFSetup::class + )); } // Composer autoloader -- 2.20.1