Skip to content

Commit 917f216

Browse files
authored
setup error handler to make (undefined array key) fatal (#356)
1 parent 34c6b8e commit 917f216

File tree

7 files changed

+29
-84
lines changed

7 files changed

+29
-84
lines changed

defaults/config.ini.default

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ allow_die = true ; internal use only
1717
enable_verbose_error_log = true ; internal use only
1818
enable_error_to_user = true ; internal use only
1919
enable_exception_handler = true ; internal use only
20+
enable_error_handler = true ; internal use only
2021

2122
[ldap]
2223
uri = "ldap://identity" ; URI of remote LDAP server

deployment/overrides/phpunit/config/config.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ allow_die = false
66
enable_verbose_error_log = false
77
enable_error_to_user = false
88
enable_exception_handler = false
9+
enable_error_handler = false

resources/init.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@
1717
set_exception_handler(["UnityWebPortal\lib\UnityHTTPD", "exceptionHandler"]);
1818
}
1919

20+
if (CONFIG["site"]["enable_error_handler"]) {
21+
set_error_handler(["UnityWebPortal\lib\UnityHTTPD", "errorHandler"]);
22+
}
23+
2024
session_start();
2125

2226
if (isset($GLOBALS["ldapconn"])) {

resources/lib/UnityHTTPD.php

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,17 +52,17 @@ public static function errorLog(
5252
$output["data"] = "data could not be JSON encoded: " . $e->getMessage();
5353
}
5454
}
55-
$output["REMOTE_USER"] = $_SERVER["REMOTE_USER"] ?? null;
56-
$output["REMOTE_ADDR"] = $_SERVER["REMOTE_ADDR"] ?? null;
57-
if (!is_null($errorid)) {
58-
$output["errorid"] = $errorid;
59-
}
6055
if (!is_null($error)) {
6156
$output["error"] = self::throwableToArray($error);
6257
} else {
6358
// newlines are bad for error log, but getTrace() is too verbose
6459
$output["trace"] = explode("\n", (new \Exception())->getTraceAsString());
6560
}
61+
$output["REMOTE_USER"] = $_SERVER["REMOTE_USER"] ?? null;
62+
$output["REMOTE_ADDR"] = $_SERVER["REMOTE_ADDR"] ?? null;
63+
if (!is_null($errorid)) {
64+
$output["errorid"] = $errorid;
65+
}
6666
error_log("$title: " . \jsonEncode($output));
6767
}
6868

@@ -132,6 +132,11 @@ public static function internalServerError(
132132
$errorid = uniqid();
133133
self::errorToUser("An internal server error has occurred.", 500, $errorid);
134134
self::errorLog("internal server error", $message, $errorid, $error, $data);
135+
if (!is_null($error) && ini_get("display_errors") && ini_get("html_errors")) {
136+
echo "<table>";
137+
echo $error->xdebug_message;
138+
echo "</table>";
139+
}
135140
self::die($message);
136141
}
137142

@@ -140,13 +145,20 @@ public static function exceptionHandler(\Throwable $e): void
140145
{
141146
ini_set("log_errors", true); // in case something goes wrong and error is not logged
142147
self::internalServerError("An internal server error has occurred.", error: $e);
143-
ini_set("log_errors", false); // error logged successfully
144148
}
145149

146-
public static function getPostData(...$keys): mixed
150+
public static function errorHandler(int $severity, string $message, string $file, int $line)
151+
{
152+
if (str_contains($message, "Undefined array key")) {
153+
throw new ArrayKeyException($message);
154+
}
155+
return false;
156+
}
157+
158+
public static function getPostData(string $key): mixed
147159
{
148160
try {
149-
return \arrayGet($_POST, ...$keys);
161+
return $_POST[$key];
150162
} catch (ArrayKeyException $e) {
151163
self::badRequest('failed to get $_POST data', $e, [
152164
'$_POST' => $_POST,
@@ -160,7 +172,7 @@ public static function getUploadedFileContents(
160172
string $encoding = "UTF-8",
161173
): string {
162174
try {
163-
$tmpfile_path = \arrayGet($_FILES, $filename, "tmp_name");
175+
$tmpfile_path = $_FILES[$filename]["tmp_name"];
164176
} catch (ArrayKeyException $e) {
165177
self::badRequest("no such uploaded file", $e, [
166178
'$_FILES' => $_FILES,

resources/lib/utils.php

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,6 @@
66
use UnityWebPortal\lib\exceptions\EncodingConversionException;
77
use phpseclib3\Crypt\PublicKeyLoader;
88

9-
function arrayGet(array $array, mixed ...$keys): mixed
10-
{
11-
$cursor = $array;
12-
$keysTraversed = [];
13-
foreach ($keys as $key) {
14-
array_push($keysTraversed, $key);
15-
if (!isset($cursor[$key])) {
16-
throw new ArrayKeyException(
17-
"key not found: \$array" .
18-
// [1, 2, "foo"] => [1][2]["foo"]
19-
implode("", array_map(fn($x) => jsonEncode([$x]), $keysTraversed)),
20-
);
21-
}
22-
$cursor = $cursor[$key];
23-
}
24-
return $cursor;
25-
}
26-
279
// like assert() but not subject to zend.assertions config
2810
function ensure(bool $condition, ?string $message = null): void
2911
{

test/unit/UtilsTest.php

Lines changed: 0 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -6,62 +6,6 @@
66

77
class UtilsTest extends TestCase
88
{
9-
public function testArrayGetReturnsValueWhenKeyExists()
10-
{
11-
$array = [
12-
"a" => [
13-
"b" => [
14-
"c" => 123,
15-
],
16-
],
17-
];
18-
$result = \arrayGet($array, "a", "b", "c");
19-
$this->assertSame(123, $result);
20-
}
21-
22-
public function testArrayGetReturnsArrayWhenTraversingPartially()
23-
{
24-
$array = [
25-
"foo" => [
26-
"bar" => "baz",
27-
],
28-
];
29-
$result = \arrayGet($array, "foo");
30-
$this->assertSame(["bar" => "baz"], $result);
31-
}
32-
33-
public function testArrayGetThrowsOnMissingKeyFirstLevel()
34-
{
35-
$array = ["x" => 1];
36-
$this->expectException(ArrayKeyException::class);
37-
$this->expectExceptionMessage('$array["y"]');
38-
\arrayGet($array, "y");
39-
}
40-
41-
public function testArrayGetThrowsOnMissingKeyNested()
42-
{
43-
$array = ["a" => []];
44-
$this->expectException(ArrayKeyException::class);
45-
// Should include both levels
46-
$this->expectExceptionMessage('$array["a"]["b"]');
47-
\arrayGet($array, "a", "b");
48-
}
49-
50-
public function testArrayGetThrowsWhenValueIsNullButKeyNotSet()
51-
{
52-
$array = ["a" => null];
53-
$this->expectException(ArrayKeyException::class);
54-
$this->expectExceptionMessage('$array["a"]');
55-
\arrayGet($array, "a");
56-
}
57-
58-
public function testArrayGetReturnsValueWhenValueIsFalsyButSet()
59-
{
60-
$array = ["a" => 0];
61-
$result = \arrayGet($array, "a");
62-
$this->assertSame(0, $result);
63-
}
64-
659
public static function SSHKeyProvider()
6610
{
6711
global $HTTP_HEADER_TEST_INPUTS;

tools/docker-dev/web/Dockerfile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ RUN sed -i '/zend.assertions/c\zend.assertions = 1' /etc/php/8.3/cli/php.ini
2929
RUN sed -i '/memory_limit/c\memory_limit = -1' /etc/php/8.3/apache2/php.ini
3030
RUN sed -i '/zend.exception_ignore_args/c\zend.exception_ignore_args = 0' /etc/php/8.3/apache2/php.ini
3131
RUN sed -i '/zend.exception_ignore_args/c\zend.exception_ignore_args = 0' /etc/php/8.3/cli/php.ini
32-
RUN echo 'xdebug.mode=coverage' >> /etc/php/8.3/cli/php.ini
32+
RUN echo 'xdebug.mode=develop,coverage,debug,gcstats,profile,trace' >> /etc/php/8.3/cli/php.ini
33+
RUN echo 'xdebug.mode=develop,coverage,debug,gcstats,profile,trace' >> /etc/php/8.3/apache2/php.ini
3334

3435
# Start apache2 server
3536
EXPOSE 80

0 commit comments

Comments
 (0)