Failed Conditions
Push — dependabot/npm_and_yarn/bootst... ( 2e86fb )
by
unknown
19:18 queued 14:34
created

includes/ExceptionHandler.php (1 issue)

1
<?php
2
/******************************************************************************
3
 * Wikipedia Account Creation Assistance tool                                 *
4
 * ACC Development Team. Please see team.json for a list of contributors.     *
5
 *                                                                            *
6
 * This is free and unencumbered software released into the public domain.    *
7
 * Please see LICENSE.md for the full licencing statement.                    *
8
 ******************************************************************************/
9
10
namespace Waca;
11
12
use ErrorException;
13
use Throwable;
14
15
class ExceptionHandler
16
{
17
    /**
18
     * Global exception handler
19
     *
20
     * Smarty would be nice to use, but it COULD BE smarty that throws the errors.
21
     * Let's build something ourselves, and hope it works.
22
     *
23
     * @param $exception
24
     *
25
     * @category Security-Critical - has the potential to leak data when exception is thrown.
26
     */
27
    public static function exceptionHandler(Throwable $exception)
28
    {
29
        /** @global $siteConfiguration SiteConfiguration */
30
        global $siteConfiguration;
31
32
        $errorDocument = <<<HTML
33
<!DOCTYPE html>
34
<html lang="en"><head>
35
<meta charset="utf-8">
36
<title>Oops! Something went wrong!</title>
37
<meta name="viewport" content="width=device-width, initial-scale=1.0">
38
<link href="{$siteConfiguration->getBaseUrl()}/resources/generated/bootstrap-main.css" rel="stylesheet">
39
<style>
40
  body {
41
    padding-top: 60px;
42
  }
43
</style>
44
</head><body><div class="container">
45
<h1>Oops! Something went wrong!</h1>
46
<p>We'll work on fixing this for you, so why not come back later?</p>
47
<p class="muted">If our trained monkeys ask, tell them this error ID: <code>$1$</code></p>
48
$2$
49
</div></body></html>
50
HTML;
51
52
        list($errorData, $errorId) = self::logExceptionToDisk($exception, $siteConfiguration, true);
53
54
        // clear and discard any content that's been saved to the output buffer
55
        if (ob_get_level() > 0) {
56
            ob_end_clean();
57
        }
58
59
        // push error ID into the document.
60
        $message = str_replace('$1$', $errorId, $errorDocument);
61
62
        if ($siteConfiguration->getDebuggingTraceEnabled()) {
63
            ob_start();
64
            var_dump($errorData);
1 ignored issue
show
Security Debugging Code introduced by
var_dump($errorData) looks like debug code. Are you sure you do not want to remove it?
Loading history...
65
            $textErrorData = ob_get_contents();
66
            ob_end_clean();
67
68
            $message = str_replace('$2$', $textErrorData, $message);
69
        }
70
        else {
71
            $message = str_replace('$2$', "", $message);
72
        }
73
74
        // While we *shouldn't* have sent headers by now due to the output buffering, PHPUnit does weird things.
75
        // This is "only" needed for the tests, but it's a good idea to wrap this anyway.
76
        if (!headers_sent()) {
77
            header('HTTP/1.1 500 Internal Server Error');
78
        }
79
80
        // output the document
81
        print $message;
82
    }
83
84
    /**
85
     * @param int    $errorSeverity The severity level of the exception.
86
     * @param string $errorMessage  The Exception message to throw.
87
     * @param string $errorFile     The filename where the exception is thrown.
88
     * @param int    $errorLine     The line number where the exception is thrown.
89
     *
90
     * @throws ErrorException
91
     */
92
    public static function errorHandler($errorSeverity, $errorMessage, $errorFile, $errorLine)
93
    {
94
        // call into the main exception handler above
95
        throw new ErrorException($errorMessage, 0, $errorSeverity, $errorFile, $errorLine);
96
    }
97
98
    /**
99
     * @param Throwable $exception
100
     *
101
     * @return null|array
102
     */
103
    public static function getExceptionData($exception)
104
    {
105
        if ($exception == null) {
106
            return null;
107
        }
108
109
        $array = array(
110
            'exception' => get_class($exception),
111
            'message'   => $exception->getMessage(),
112
            'stack'     => $exception->getTraceAsString(),
113
        );
114
115
        $array['previous'] = self::getExceptionData($exception->getPrevious());
116
117
        return $array;
118
    }
119
120
    public static function logExceptionToDisk(
121
        Throwable $exception,
122
        SiteConfiguration $siteConfiguration,
123
        bool $fromGlobalHandler = false
124
    ): array {
125
        $errorData = self::getExceptionData($exception);
126
        $errorData['server'] = $_SERVER;
127
        $errorData['get'] = $_GET;
128
        $errorData['post'] = $_POST;
129
130
        $redactions = ['password', 'newpassword', 'newpasswordconfirm', 'otp'];
131
        foreach ($redactions as $redaction) {
132
            if (isset($errorData['post'][$redaction])) {
133
                $errorData['post'][$redaction] = '<redacted>';
134
            }
135
        }
136
137
        if ($fromGlobalHandler) {
138
            $errorData['globalHandler'] = true;
139
        }
140
        else {
141
            $errorData['globalHandler'] = false;
142
        }
143
144
        $state = serialize($errorData);
145
        $errorId = sha1($state);
146
147
        // Save the error for later analysis
148
        file_put_contents($siteConfiguration->getErrorLog() . '/' . $errorId . '.log', $state);
149
150
        return array($errorData, $errorId);
151
    }
152
}
153