From 0862c19eb70981365084fd82560be5ca75fb6be0 Mon Sep 17 00:00:00 2001 From: Simon Leary Date: Thu, 11 Dec 2025 10:27:33 -0500 Subject: [PATCH 01/21] use message system in exception handler --- resources/lib/UnityHTTPD.php | 48 ++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/resources/lib/UnityHTTPD.php b/resources/lib/UnityHTTPD.php index 7e09cf42..f5ffcd4a 100644 --- a/resources/lib/UnityHTTPD.php +++ b/resources/lib/UnityHTTPD.php @@ -43,6 +43,24 @@ public static function redirect(?string $dest = null): never self::die(); } + public static function logThrowableAndMessageUser( + \Throwable $error, + string $log_title, + string $log_message, + string $user_message_title, + string $user_message_body, + ) { + $errorid = spl_object_hash($error); + $data = []; + self::errorLog($log_title, $log_message, error: $error, errorid: $errorid, data: $data); + if (strlen($user_message_body) == 0) { + $user_message_body = "error ID: $errorid"; + } else { + $user_message_body .= " error ID: $errorid"; + } + self::messageError($user_message_title, $user_message_body); + } + // $data must be JSON serializable public static function errorLog( string $title, @@ -72,6 +90,7 @@ public static function errorLog( } $output["REMOTE_USER"] = $_SERVER["REMOTE_USER"] ?? null; $output["REMOTE_ADDR"] = $_SERVER["REMOTE_ADDR"] ?? null; + $output["_REQUEST"] = $_REQUEST; if (!is_null($errorid)) { $output["errorid"] = $errorid; } @@ -136,27 +155,20 @@ public static function forbidden( self::die($message); } - public static function internalServerError( - string $message, - ?\Throwable $error = null, - ?array $data = null, - ): never { - $errorid = uniqid(); - self::errorToUser("An internal server error has occurred.", 500, $errorid); - self::errorLog("internal server error", $message, $errorid, $error, $data); - if (!is_null($error) && ini_get("display_errors") && ini_get("html_errors")) { - echo ""; - echo $error->xdebug_message; - echo "
"; - } - self::die($message); - } - // https://www.php.net/manual/en/function.set-exception-handler.php public static function exceptionHandler(\Throwable $e): void { - ini_set("log_errors", true); // in case something goes wrong and error is not logged - self::internalServerError("An internal server error has occurred.", error: $e); + // we disable log_errors before we enable this exception handler to avoid duplicate logging + // if this exception handler itself fails, information will be lost unless we re-enable it + ini_set("log_errors", true); + self::logThrowableAndMessageUser( + $e, + "uncaught exception", + strval($e), + "An internal server error has occurred.", + "", + ); + self::redirect(); } public static function errorHandler(int $severity, string $message, string $file, int $line) From 72cf0b7e34bb89b157d4995869de8a7174748ce3 Mon Sep 17 00:00:00 2001 From: Simon Leary Date: Thu, 11 Dec 2025 11:07:11 -0500 Subject: [PATCH 02/21] fall back on old error output for HTTP GET --- resources/lib/UnityHTTPD.php | 38 ++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/resources/lib/UnityHTTPD.php b/resources/lib/UnityHTTPD.php index f5ffcd4a..103cc738 100644 --- a/resources/lib/UnityHTTPD.php +++ b/resources/lib/UnityHTTPD.php @@ -155,20 +155,42 @@ public static function forbidden( self::die($message); } + public static function internalServerError( + string $message, + ?\Throwable $error = null, + ?array $data = null, + ): never { + $errorid = uniqid(); + self::errorToUser("An internal server error has occurred.", 500, $errorid); + self::errorLog("internal server error", $message, $errorid, $error, $data); + if (!is_null($error) && ini_get("display_errors") && ini_get("html_errors")) { + echo ""; + echo $error->xdebug_message; + echo "
"; + } + self::die($message); + } + // https://www.php.net/manual/en/function.set-exception-handler.php public static function exceptionHandler(\Throwable $e): void { // we disable log_errors before we enable this exception handler to avoid duplicate logging // if this exception handler itself fails, information will be lost unless we re-enable it ini_set("log_errors", true); - self::logThrowableAndMessageUser( - $e, - "uncaught exception", - strval($e), - "An internal server error has occurred.", - "", - ); - self::redirect(); + // if the user was doing HTTP POST, then make a pretty error and redirect + // else, a redirect may cause an infinite loop, so fall back on the old ugly error + if ($_SERVER["REQUEST_METHOD"] == "POST") { + self::logThrowableAndMessageUser( + $e, + "uncaught exception", + strval($e), + "An internal server error has occurred.", + "", + ); + self::redirect(); + } else { + self::internalServerError("", error: $e); + } } public static function errorHandler(int $severity, string $message, string $file, int $line) From 67b5a3b70b864088f854a4b035f4bc409e5b57b5 Mon Sep 17 00:00:00 2001 From: Simon Leary Date: Thu, 11 Dec 2025 11:11:17 -0500 Subject: [PATCH 03/21] use spl_error_id instead of spl_object_hash --- resources/lib/UnityHTTPD.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/lib/UnityHTTPD.php b/resources/lib/UnityHTTPD.php index 103cc738..66364c6c 100644 --- a/resources/lib/UnityHTTPD.php +++ b/resources/lib/UnityHTTPD.php @@ -50,7 +50,7 @@ public static function logThrowableAndMessageUser( string $user_message_title, string $user_message_body, ) { - $errorid = spl_object_hash($error); + $errorid = spl_object_id($error); $data = []; self::errorLog($log_title, $log_message, error: $error, errorid: $errorid, data: $data); if (strlen($user_message_body) == 0) { From f80a9f1ad2b4f8989cd0abc6cf7189b7135250ca Mon Sep 17 00:00:00 2001 From: Simon Leary Date: Thu, 11 Dec 2025 11:20:40 -0500 Subject: [PATCH 04/21] good error ID --- resources/lib/UnityHTTPD.php | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/resources/lib/UnityHTTPD.php b/resources/lib/UnityHTTPD.php index 66364c6c..2588a218 100644 --- a/resources/lib/UnityHTTPD.php +++ b/resources/lib/UnityHTTPD.php @@ -2,6 +2,7 @@ namespace UnityWebPortal\lib; +use stdClass; use UnityWebPortal\lib\exceptions\NoDieException; use UnityWebPortal\lib\exceptions\ArrayKeyException; use RuntimeException; @@ -43,6 +44,13 @@ public static function redirect(?string $dest = null): never self::die(); } + // assumes that a session is started and has a good unique session_id + public static function errorID(?\Throwable $e = null): string + { + $e ??= new stdClass(); + return md5(strval(session_id()) . spl_object_id($e)); + } + public static function logThrowableAndMessageUser( \Throwable $error, string $log_title, @@ -50,7 +58,7 @@ public static function logThrowableAndMessageUser( string $user_message_title, string $user_message_body, ) { - $errorid = spl_object_id($error); + $errorid = self::errorID($error); $data = []; self::errorLog($log_title, $log_message, error: $error, errorid: $errorid, data: $data); if (strlen($user_message_body) == 0) { @@ -91,8 +99,8 @@ public static function errorLog( $output["REMOTE_USER"] = $_SERVER["REMOTE_USER"] ?? null; $output["REMOTE_ADDR"] = $_SERVER["REMOTE_ADDR"] ?? null; $output["_REQUEST"] = $_REQUEST; - if (!is_null($errorid)) { - $output["errorid"] = $errorid; + if (!is_null($errorid) || !is_null($error)) { + $output["errorid"] = self::errorID($error); } error_log("$title: " . \jsonEncode($output)); } @@ -138,7 +146,7 @@ public static function badRequest( ?\Throwable $error = null, ?array $data = null, ): never { - $errorid = uniqid(); + $errorid = self::errorID($error); self::errorToUser("Invalid requested action or submitted data.", 400, $errorid); self::errorLog("bad request", $message, $errorid, $error, $data); self::die($message); @@ -149,7 +157,7 @@ public static function forbidden( ?\Throwable $error = null, ?array $data = null, ): never { - $errorid = uniqid(); + $errorid = self::errorID($error); self::errorToUser("Permission denied.", 403, $errorid); self::errorLog("forbidden", $message, $errorid, $error, $data); self::die($message); @@ -160,7 +168,7 @@ public static function internalServerError( ?\Throwable $error = null, ?array $data = null, ): never { - $errorid = uniqid(); + $errorid = self::errorID($error); self::errorToUser("An internal server error has occurred.", 500, $errorid); self::errorLog("internal server error", $message, $errorid, $error, $data); if (!is_null($error) && ini_get("display_errors") && ini_get("html_errors")) { From ea37a9e8ba7603f7e09f49e5d616ea6136c7a27e Mon Sep 17 00:00:00 2001 From: Simon Leary Date: Thu, 11 Dec 2025 11:53:14 -0500 Subject: [PATCH 05/21] fix bug --- resources/lib/UnityHTTPD.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/resources/lib/UnityHTTPD.php b/resources/lib/UnityHTTPD.php index 2588a218..f60e18a5 100644 --- a/resources/lib/UnityHTTPD.php +++ b/resources/lib/UnityHTTPD.php @@ -99,8 +99,11 @@ public static function errorLog( $output["REMOTE_USER"] = $_SERVER["REMOTE_USER"] ?? null; $output["REMOTE_ADDR"] = $_SERVER["REMOTE_ADDR"] ?? null; $output["_REQUEST"] = $_REQUEST; - if (!is_null($errorid) || !is_null($error)) { - $output["errorid"] = self::errorID($error); + if (is_null($errorid) && !is_null($error)) { + $errorid = self::errorID($error); + } + if (!is_null($errorid)) { + $output["errorid"] = $errorid; } error_log("$title: " . \jsonEncode($output)); } From 5484651e6828df8a02027f18388a81d8d3f9c8a6 Mon Sep 17 00:00:00 2001 From: Simon Leary Date: Thu, 11 Dec 2025 12:58:00 -0500 Subject: [PATCH 06/21] big refactor --- resources/lib/UnityHTTPD.php | 127 ++++++++++++++++++----------------- 1 file changed, 65 insertions(+), 62 deletions(-) diff --git a/resources/lib/UnityHTTPD.php b/resources/lib/UnityHTTPD.php index f60e18a5..b6944ed8 100644 --- a/resources/lib/UnityHTTPD.php +++ b/resources/lib/UnityHTTPD.php @@ -51,22 +51,48 @@ public static function errorID(?\Throwable $e = null): string return md5(strval(session_id()) . spl_object_id($e)); } - public static function logThrowableAndMessageUser( - \Throwable $error, + public static function gracefulDie( string $log_title, string $log_message, string $user_message_title, string $user_message_body, - ) { + ?\Throwable $error = null, + int $http_response_code = 200, + mixed $data = null, + ): never { $errorid = self::errorID($error); - $data = []; - self::errorLog($log_title, $log_message, error: $error, errorid: $errorid, data: $data); + $suffix = sprintf( + "Please notify a Unity admin at %s. Error ID: %s.", + CONFIG["mail"]["support"], + $errorid, + ); if (strlen($user_message_body) == 0) { - $user_message_body = "error ID: $errorid"; + $user_message_body = $suffix; + } else { + $user_message_body .= " $suffix"; + } + self::errorLog($log_title, $log_message, data: $data, error: $error, errorid: $errorid); + // if the user was doing HTTP POST, then make a pretty error and redirect + // else, a redirect may cause an infinite loop, so fall back on the old ugly error + if ($_SERVER["REQUEST_METHOD"] == "POST") { + self::messageError($user_message_title, $user_message_body); + self::redirect(); } else { - $user_message_body .= " error ID: $errorid"; + if (CONFIG["site"]["enable_error_to_user"]) { + if (!headers_sent()) { + http_response_code($http_response_code); + } + // text may not be shown in the webpage in an obvious way, so make a popup + self::alert("$user_message_title -- $user_message_body"); + echo "

$user_message_title

$user_message_body

"; + if (!is_null($error) && ini_get("display_errors") && ini_get("html_errors")) { + echo ""; + echo $error->xdebug_message; + echo "
"; + } + } + self::die(); } - self::messageError($user_message_title, $user_message_body); } // $data must be JSON serializable @@ -124,62 +150,52 @@ private static function throwableToArray(\Throwable $t): array return $output; } - private static function errorToUser( - string $msg, - int $http_response_code, - ?string $errorid = null, - ): void { - if (!CONFIG["site"]["enable_error_to_user"]) { - return; - } - $notes = "Please notify a Unity admin at " . CONFIG["mail"]["support"] . "."; - if (!is_null($errorid)) { - $notes = $notes . " Error ID: $errorid."; - } - if (!headers_sent()) { - http_response_code($http_response_code); - } - // text may not be shown in the webpage in an obvious way, so make a popup - self::alert("$msg $notes"); - echo "

$msg

$notes

"; - } - public static function badRequest( - string $message, + string $log_message, ?\Throwable $error = null, ?array $data = null, ): never { - $errorid = self::errorID($error); - self::errorToUser("Invalid requested action or submitted data.", 400, $errorid); - self::errorLog("bad request", $message, $errorid, $error, $data); - self::die($message); + self::gracefulDie( + "bad request", + $log_message, + "Invalid requested action or submitted data.", + "", + error: $error, + http_response_code: 400, + data: $data, + ); } public static function forbidden( - string $message, + string $log_message, ?\Throwable $error = null, ?array $data = null, ): never { - $errorid = self::errorID($error); - self::errorToUser("Permission denied.", 403, $errorid); - self::errorLog("forbidden", $message, $errorid, $error, $data); - self::die($message); + self::gracefulDie( + "forbidden", + $log_message, + "Permission denied.", + "", + error: $error, + http_response_code: 403, + data: $data, + ); } public static function internalServerError( - string $message, + string $log_message, ?\Throwable $error = null, ?array $data = null, ): never { - $errorid = self::errorID($error); - self::errorToUser("An internal server error has occurred.", 500, $errorid); - self::errorLog("internal server error", $message, $errorid, $error, $data); - if (!is_null($error) && ini_get("display_errors") && ini_get("html_errors")) { - echo ""; - echo $error->xdebug_message; - echo "
"; - } - self::die($message); + self::gracefulDie( + "internal server error", + $log_message, + "An internal server error has occurred.", + "", + error: $error, + http_response_code: 500, + data: $data, + ); } // https://www.php.net/manual/en/function.set-exception-handler.php @@ -188,20 +204,7 @@ public static function exceptionHandler(\Throwable $e): void // we disable log_errors before we enable this exception handler to avoid duplicate logging // if this exception handler itself fails, information will be lost unless we re-enable it ini_set("log_errors", true); - // if the user was doing HTTP POST, then make a pretty error and redirect - // else, a redirect may cause an infinite loop, so fall back on the old ugly error - if ($_SERVER["REQUEST_METHOD"] == "POST") { - self::logThrowableAndMessageUser( - $e, - "uncaught exception", - strval($e), - "An internal server error has occurred.", - "", - ); - self::redirect(); - } else { - self::internalServerError("", error: $e); - } + self::internalServerError("", error: $e); } public static function errorHandler(int $severity, string $message, string $file, int $line) From 07fa9aabbd65280e7bc8fdb70228a05e0e82f16d Mon Sep 17 00:00:00 2001 From: Simon Leary Date: Thu, 11 Dec 2025 13:05:11 -0500 Subject: [PATCH 07/21] update redirect --- resources/lib/UnityHTTPD.php | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/resources/lib/UnityHTTPD.php b/resources/lib/UnityHTTPD.php index b6944ed8..188f661b 100644 --- a/resources/lib/UnityHTTPD.php +++ b/resources/lib/UnityHTTPD.php @@ -40,7 +40,11 @@ public static function redirect(?string $dest = null): never $dest ??= pathJoin(CONFIG["site"]["prefix"], $_SERVER["REQUEST_URI"]); $dest = htmlspecialchars($dest); header("Location: $dest"); - self::errorToUser("Redirect failed, click here to continue.", 302); + http_response_code(302); + if (CONFIG["site"]["enable_error_to_user"]) { + echo "If you're reading this message, then your browser has failed to redirect you " . + "to the proper destination. click here to continue."; + } self::die(); } @@ -78,18 +82,16 @@ public static function gracefulDie( self::messageError($user_message_title, $user_message_body); self::redirect(); } else { - if (CONFIG["site"]["enable_error_to_user"]) { - if (!headers_sent()) { - http_response_code($http_response_code); - } - // text may not be shown in the webpage in an obvious way, so make a popup - self::alert("$user_message_title -- $user_message_body"); - echo "

$user_message_title

$user_message_body

"; - if (!is_null($error) && ini_get("display_errors") && ini_get("html_errors")) { - echo ""; - echo $error->xdebug_message; - echo "
"; - } + if (!headers_sent()) { + http_response_code($http_response_code); + } + // text may not be shown in the webpage in an obvious way, so make a popup + self::alert("$user_message_title -- $user_message_body"); + echo "

$user_message_title

$user_message_body

"; + if (!is_null($error) && ini_get("display_errors") && ini_get("html_errors")) { + echo ""; + echo $error->xdebug_message; + echo "
"; } self::die(); } From 05c23a0771388d5dfe19ae775db09d4596369c06 Mon Sep 17 00:00:00 2001 From: Simon Leary Date: Thu, 11 Dec 2025 13:10:38 -0500 Subject: [PATCH 08/21] allow REQUEST_METHOD unset --- resources/lib/UnityHTTPD.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/lib/UnityHTTPD.php b/resources/lib/UnityHTTPD.php index 188f661b..d2e684be 100644 --- a/resources/lib/UnityHTTPD.php +++ b/resources/lib/UnityHTTPD.php @@ -78,7 +78,7 @@ public static function gracefulDie( self::errorLog($log_title, $log_message, data: $data, error: $error, errorid: $errorid); // if the user was doing HTTP POST, then make a pretty error and redirect // else, a redirect may cause an infinite loop, so fall back on the old ugly error - if ($_SERVER["REQUEST_METHOD"] == "POST") { + if ($_SERVER["REQUEST_METHOD"] ?? "" == "POST") { self::messageError($user_message_title, $user_message_body); self::redirect(); } else { From 20709f02f2e7fa82256c05a0b57e438119f016c9 Mon Sep 17 00:00:00 2001 From: Simon Leary Date: Thu, 11 Dec 2025 13:10:44 -0500 Subject: [PATCH 09/21] === --- resources/lib/UnityHTTPD.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/lib/UnityHTTPD.php b/resources/lib/UnityHTTPD.php index d2e684be..4cdc843f 100644 --- a/resources/lib/UnityHTTPD.php +++ b/resources/lib/UnityHTTPD.php @@ -70,7 +70,7 @@ public static function gracefulDie( CONFIG["mail"]["support"], $errorid, ); - if (strlen($user_message_body) == 0) { + if (strlen($user_message_body) === 0) { $user_message_body = $suffix; } else { $user_message_body .= " $suffix"; From 9a3d1d518d04a4ee050cb77d6a775c744a8415be Mon Sep 17 00:00:00 2001 From: Simon Leary Date: Thu, 11 Dec 2025 13:13:22 -0500 Subject: [PATCH 10/21] comment --- resources/lib/UnityHTTPD.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/resources/lib/UnityHTTPD.php b/resources/lib/UnityHTTPD.php index 4cdc843f..83e29658 100644 --- a/resources/lib/UnityHTTPD.php +++ b/resources/lib/UnityHTTPD.php @@ -48,7 +48,10 @@ public static function redirect(?string $dest = null): never self::die(); } - // assumes that a session is started and has a good unique session_id + /* generate a unique ID that can be logged and also displayed to the user when something has + gone wrong. This way the user can give us the ID and we can find the exact line in the log + file that represents their issue. + assumes that a session is started and has a good unique session_id */ public static function errorID(?\Throwable $e = null): string { $e ??= new stdClass(); From 55649d52fbe96fdd7a63232679b4ae329a6dbb76 Mon Sep 17 00:00:00 2001 From: Simon Leary Date: Thu, 11 Dec 2025 13:19:03 -0500 Subject: [PATCH 11/21] comment --- resources/lib/UnityHTTPD.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/resources/lib/UnityHTTPD.php b/resources/lib/UnityHTTPD.php index 83e29658..73e69d9a 100644 --- a/resources/lib/UnityHTTPD.php +++ b/resources/lib/UnityHTTPD.php @@ -58,6 +58,14 @@ public static function errorID(?\Throwable $e = null): string return md5(strval(session_id()) . spl_object_id($e)); } + /* + generates a unique error ID, writes to error log, and then: + if the user is doing an HTTP POST: + registers a message in the user's session and issues a redirect to display that message + else: + prints a message to stdout, sets an HTTP response code, and dies + we can't always do a redirect or else we could risk an infinite loop. + */ public static function gracefulDie( string $log_title, string $log_message, @@ -79,8 +87,6 @@ public static function gracefulDie( $user_message_body .= " $suffix"; } self::errorLog($log_title, $log_message, data: $data, error: $error, errorid: $errorid); - // if the user was doing HTTP POST, then make a pretty error and redirect - // else, a redirect may cause an infinite loop, so fall back on the old ugly error if ($_SERVER["REQUEST_METHOD"] ?? "" == "POST") { self::messageError($user_message_title, $user_message_body); self::redirect(); From 45646a537573ce3f3b11a30d55477b07dcc6182e Mon Sep 17 00:00:00 2001 From: Simon Leary Date: Thu, 11 Dec 2025 13:19:23 -0500 Subject: [PATCH 12/21] whitespace --- resources/lib/UnityHTTPD.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/resources/lib/UnityHTTPD.php b/resources/lib/UnityHTTPD.php index 73e69d9a..631cc7c1 100644 --- a/resources/lib/UnityHTTPD.php +++ b/resources/lib/UnityHTTPD.php @@ -48,10 +48,12 @@ public static function redirect(?string $dest = null): never self::die(); } - /* generate a unique ID that can be logged and also displayed to the user when something has + /* + generate a unique ID that can be logged and also displayed to the user when something has gone wrong. This way the user can give us the ID and we can find the exact line in the log file that represents their issue. - assumes that a session is started and has a good unique session_id */ + assumes that a session is started and has a good unique session_id + */ public static function errorID(?\Throwable $e = null): string { $e ??= new stdClass(); From d91c66c7a0eae6941751c573c57125ba500314e3 Mon Sep 17 00:00:00 2001 From: Simon Leary Date: Thu, 11 Dec 2025 13:40:36 -0500 Subject: [PATCH 13/21] replace INI setting php_sapi_name --- defaults/config.ini.default | 1 - deployment/overrides/phpunit/config/config.ini | 1 - deployment/overrides/worker/config/config.ini | 2 -- resources/lib/UnityHTTPD.php | 18 ++++++++++++++---- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/defaults/config.ini.default b/defaults/config.ini.default index f38dc18b..1e021d6d 100644 --- a/defaults/config.ini.default +++ b/defaults/config.ini.default @@ -15,7 +15,6 @@ terms_of_service_url = "https://github.com" ; this can be external or a portal p account_policy_url = "https://github.com" ; this can be external or a portal page created with "content management" allow_die = true ; internal use only enable_verbose_error_log = true ; internal use only -enable_error_to_user = true ; internal use only enable_exception_handler = true ; internal use only enable_error_handler = true ; internal use only diff --git a/deployment/overrides/phpunit/config/config.ini b/deployment/overrides/phpunit/config/config.ini index 86de5039..bef67b48 100644 --- a/deployment/overrides/phpunit/config/config.ini +++ b/deployment/overrides/phpunit/config/config.ini @@ -4,6 +4,5 @@ custom_user_mappings_dir = "test/custom_user_mappings" [site] allow_die = false enable_verbose_error_log = false -enable_error_to_user = false enable_exception_handler = false enable_error_handler = false diff --git a/deployment/overrides/worker/config/config.ini b/deployment/overrides/worker/config/config.ini index 53ef3d6b..e69de29b 100644 --- a/deployment/overrides/worker/config/config.ini +++ b/deployment/overrides/worker/config/config.ini @@ -1,2 +0,0 @@ -[site] -enable_error_to_user = false diff --git a/resources/lib/UnityHTTPD.php b/resources/lib/UnityHTTPD.php index 631cc7c1..664cc534 100644 --- a/resources/lib/UnityHTTPD.php +++ b/resources/lib/UnityHTTPD.php @@ -35,13 +35,18 @@ public static function die(mixed $x = null, bool $show_user = false): never } } + /* + send HTTP heaer, set HTTP response code, + print a message just in case the browser fails to redirect if PHP is not being run from the CLI, + and then die + */ public static function redirect(?string $dest = null): never { $dest ??= pathJoin(CONFIG["site"]["prefix"], $_SERVER["REQUEST_URI"]); $dest = htmlspecialchars($dest); header("Location: $dest"); http_response_code(302); - if (CONFIG["site"]["enable_error_to_user"]) { + if (php_sapi_name() !== "cli") { echo "If you're reading this message, then your browser has failed to redirect you " . "to the proper destination. click here to continue."; } @@ -62,10 +67,13 @@ public static function errorID(?\Throwable $e = null): string /* generates a unique error ID, writes to error log, and then: - if the user is doing an HTTP POST: + if PHP is being run in the CLI: + prints a message to stdout and dies + else, if the user is doing an HTTP POST: registers a message in the user's session and issues a redirect to display that message else: - prints a message to stdout, sets an HTTP response code, and dies + prints an HTML message to stdout, sets an HTTP response code, and dies + we don't want HTML formatted output in the CLI we can't always do a redirect or else we could risk an infinite loop. */ public static function gracefulDie( @@ -89,7 +97,9 @@ public static function gracefulDie( $user_message_body .= " $suffix"; } self::errorLog($log_title, $log_message, data: $data, error: $error, errorid: $errorid); - if ($_SERVER["REQUEST_METHOD"] ?? "" == "POST") { + if (php_sapi_name() == "cli") { + self::die("$user_message_title -- $user_message_body"); + } elseif ($_SERVER["REQUEST_METHOD"] ?? "" == "POST") { self::messageError($user_message_title, $user_message_body); self::redirect(); } else { From c2976de1ae7d406d97bb6b2be4e15efa3992b17f Mon Sep 17 00:00:00 2001 From: Simon Leary Date: Thu, 11 Dec 2025 13:46:18 -0500 Subject: [PATCH 14/21] pemdas --- resources/lib/UnityHTTPD.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/lib/UnityHTTPD.php b/resources/lib/UnityHTTPD.php index 664cc534..aac7407e 100644 --- a/resources/lib/UnityHTTPD.php +++ b/resources/lib/UnityHTTPD.php @@ -99,7 +99,7 @@ public static function gracefulDie( self::errorLog($log_title, $log_message, data: $data, error: $error, errorid: $errorid); if (php_sapi_name() == "cli") { self::die("$user_message_title -- $user_message_body"); - } elseif ($_SERVER["REQUEST_METHOD"] ?? "" == "POST") { + } elseif (($_SERVER["REQUEST_METHOD"] ?? "") == "POST") { self::messageError($user_message_title, $user_message_body); self::redirect(); } else { From 5f9d48c04266634bb2c8ce2010c6fe543a3d7df0 Mon Sep 17 00:00:00 2001 From: Simon Leary Date: Thu, 11 Dec 2025 13:50:37 -0500 Subject: [PATCH 15/21] use html_errors ini setting instead of php_sapi_name --- resources/lib/UnityHTTPD.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/lib/UnityHTTPD.php b/resources/lib/UnityHTTPD.php index aac7407e..f0e729e3 100644 --- a/resources/lib/UnityHTTPD.php +++ b/resources/lib/UnityHTTPD.php @@ -67,13 +67,12 @@ public static function errorID(?\Throwable $e = null): string /* generates a unique error ID, writes to error log, and then: - if PHP is being run in the CLI: + if "html_errors" is disabled in the PHP config file: prints a message to stdout and dies else, if the user is doing an HTTP POST: registers a message in the user's session and issues a redirect to display that message else: prints an HTML message to stdout, sets an HTTP response code, and dies - we don't want HTML formatted output in the CLI we can't always do a redirect or else we could risk an infinite loop. */ public static function gracefulDie( @@ -97,7 +96,7 @@ public static function gracefulDie( $user_message_body .= " $suffix"; } self::errorLog($log_title, $log_message, data: $data, error: $error, errorid: $errorid); - if (php_sapi_name() == "cli") { + if (!ini_get("html_errors")) { self::die("$user_message_title -- $user_message_body"); } elseif (($_SERVER["REQUEST_METHOD"] ?? "") == "POST") { self::messageError($user_message_title, $user_message_body); @@ -109,7 +108,8 @@ public static function gracefulDie( // text may not be shown in the webpage in an obvious way, so make a popup self::alert("$user_message_title -- $user_message_body"); echo "

$user_message_title

$user_message_body

"; - if (!is_null($error) && ini_get("display_errors") && ini_get("html_errors")) { + // display_errors should not be enabled in production + if (!is_null($error) && ini_get("display_errors")) { echo ""; echo $error->xdebug_message; echo "
"; From 22647aaeb1e725edf9b73e485e4c2e5f93392a0d Mon Sep 17 00:00:00 2001 From: Simon Leary Date: Thu, 11 Dec 2025 13:59:11 -0500 Subject: [PATCH 16/21] fix checks for ini values --- resources/lib/UnityHTTPD.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/lib/UnityHTTPD.php b/resources/lib/UnityHTTPD.php index f0e729e3..e6fc7f29 100644 --- a/resources/lib/UnityHTTPD.php +++ b/resources/lib/UnityHTTPD.php @@ -96,7 +96,7 @@ public static function gracefulDie( $user_message_body .= " $suffix"; } self::errorLog($log_title, $log_message, data: $data, error: $error, errorid: $errorid); - if (!ini_get("html_errors")) { + if (ini_get("html_errors") !== "1") { self::die("$user_message_title -- $user_message_body"); } elseif (($_SERVER["REQUEST_METHOD"] ?? "") == "POST") { self::messageError($user_message_title, $user_message_body); @@ -109,7 +109,7 @@ public static function gracefulDie( self::alert("$user_message_title -- $user_message_body"); echo "

$user_message_title

$user_message_body

"; // display_errors should not be enabled in production - if (!is_null($error) && ini_get("display_errors")) { + if (!is_null($error) && ini_get("display_errors") === "1") { echo ""; echo $error->xdebug_message; echo "
"; From 2d16c27d61bdc975f07887a7e92541f011266b14 Mon Sep 17 00:00:00 2001 From: Simon Leary Date: Thu, 11 Dec 2025 14:04:53 -0500 Subject: [PATCH 17/21] remove fancy error ID that seems to make duplicates --- resources/lib/UnityHTTPD.php | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/resources/lib/UnityHTTPD.php b/resources/lib/UnityHTTPD.php index e6fc7f29..c58d8eb0 100644 --- a/resources/lib/UnityHTTPD.php +++ b/resources/lib/UnityHTTPD.php @@ -53,18 +53,6 @@ public static function redirect(?string $dest = null): never self::die(); } - /* - generate a unique ID that can be logged and also displayed to the user when something has - gone wrong. This way the user can give us the ID and we can find the exact line in the log - file that represents their issue. - assumes that a session is started and has a good unique session_id - */ - public static function errorID(?\Throwable $e = null): string - { - $e ??= new stdClass(); - return md5(strval(session_id()) . spl_object_id($e)); - } - /* generates a unique error ID, writes to error log, and then: if "html_errors" is disabled in the PHP config file: @@ -84,7 +72,7 @@ public static function gracefulDie( int $http_response_code = 200, mixed $data = null, ): never { - $errorid = self::errorID($error); + $errorid = uniqid(); $suffix = sprintf( "Please notify a Unity admin at %s. Error ID: %s.", CONFIG["mail"]["support"], @@ -148,9 +136,6 @@ public static function errorLog( $output["REMOTE_USER"] = $_SERVER["REMOTE_USER"] ?? null; $output["REMOTE_ADDR"] = $_SERVER["REMOTE_ADDR"] ?? null; $output["_REQUEST"] = $_REQUEST; - if (is_null($errorid) && !is_null($error)) { - $errorid = self::errorID($error); - } if (!is_null($errorid)) { $output["errorid"] = $errorid; } From ac90edd0cab45616c89b7715a87dd973ecbe4070 Mon Sep 17 00:00:00 2001 From: simonLeary42 <71396965+simonLeary42@users.noreply.github.com> Date: Thu, 11 Dec 2025 14:08:06 -0500 Subject: [PATCH 18/21] Update resources/lib/UnityHTTPD.php Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- resources/lib/UnityHTTPD.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/lib/UnityHTTPD.php b/resources/lib/UnityHTTPD.php index c58d8eb0..fc149185 100644 --- a/resources/lib/UnityHTTPD.php +++ b/resources/lib/UnityHTTPD.php @@ -36,7 +36,7 @@ public static function die(mixed $x = null, bool $show_user = false): never } /* - send HTTP heaer, set HTTP response code, + send HTTP header, set HTTP response code, print a message just in case the browser fails to redirect if PHP is not being run from the CLI, and then die */ From 8213daf42ae84aa569d5ff840a3d6dbad1366900 Mon Sep 17 00:00:00 2001 From: Simon Leary Date: Thu, 11 Dec 2025 14:11:15 -0500 Subject: [PATCH 19/21] htmlspecialchars --- resources/lib/UnityHTTPD.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resources/lib/UnityHTTPD.php b/resources/lib/UnityHTTPD.php index fc149185..d4708690 100644 --- a/resources/lib/UnityHTTPD.php +++ b/resources/lib/UnityHTTPD.php @@ -78,6 +78,8 @@ public static function gracefulDie( CONFIG["mail"]["support"], $errorid, ); + $user_message_title = htmlspecialchars($user_message_title); + $user_message_body = htmlspecialchars($user_message_body); if (strlen($user_message_body) === 0) { $user_message_body = $suffix; } else { From 68947c88409f90c05b5bf3db2c6cfff9a5d26241 Mon Sep 17 00:00:00 2001 From: Simon Leary Date: Thu, 11 Dec 2025 14:15:42 -0500 Subject: [PATCH 20/21] put back config setting to be less magic --- defaults/config.ini.default | 1 + deployment/overrides/phpunit/config/config.ini | 1 + deployment/overrides/worker/config/config.ini | 2 ++ resources/lib/UnityHTTPD.php | 2 +- 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/defaults/config.ini.default b/defaults/config.ini.default index 1e021d6d..a505140f 100644 --- a/defaults/config.ini.default +++ b/defaults/config.ini.default @@ -15,6 +15,7 @@ terms_of_service_url = "https://github.com" ; this can be external or a portal p account_policy_url = "https://github.com" ; this can be external or a portal page created with "content management" allow_die = true ; internal use only enable_verbose_error_log = true ; internal use only +enable_redirect_message = true ; internal use only enable_exception_handler = true ; internal use only enable_error_handler = true ; internal use only diff --git a/deployment/overrides/phpunit/config/config.ini b/deployment/overrides/phpunit/config/config.ini index bef67b48..9ad2ae13 100644 --- a/deployment/overrides/phpunit/config/config.ini +++ b/deployment/overrides/phpunit/config/config.ini @@ -4,5 +4,6 @@ custom_user_mappings_dir = "test/custom_user_mappings" [site] allow_die = false enable_verbose_error_log = false +enable_redirect_message = false enable_exception_handler = false enable_error_handler = false diff --git a/deployment/overrides/worker/config/config.ini b/deployment/overrides/worker/config/config.ini index e69de29b..981ce101 100644 --- a/deployment/overrides/worker/config/config.ini +++ b/deployment/overrides/worker/config/config.ini @@ -0,0 +1,2 @@ +[site] +enable_redirect_message = false diff --git a/resources/lib/UnityHTTPD.php b/resources/lib/UnityHTTPD.php index d4708690..a445feb0 100644 --- a/resources/lib/UnityHTTPD.php +++ b/resources/lib/UnityHTTPD.php @@ -46,7 +46,7 @@ public static function redirect(?string $dest = null): never $dest = htmlspecialchars($dest); header("Location: $dest"); http_response_code(302); - if (php_sapi_name() !== "cli") { + if (CONFIG["site"]["enable_redirect_message"]) { echo "If you're reading this message, then your browser has failed to redirect you " . "to the proper destination. click here to continue."; } From 028d4ce75a3bb0e58cd723a22fb8e4f474387c82 Mon Sep 17 00:00:00 2001 From: Simon Leary Date: Thu, 11 Dec 2025 14:17:02 -0500 Subject: [PATCH 21/21] remove unused import --- resources/lib/UnityHTTPD.php | 1 - 1 file changed, 1 deletion(-) diff --git a/resources/lib/UnityHTTPD.php b/resources/lib/UnityHTTPD.php index a445feb0..bed4bb95 100644 --- a/resources/lib/UnityHTTPD.php +++ b/resources/lib/UnityHTTPD.php @@ -2,7 +2,6 @@ namespace UnityWebPortal\lib; -use stdClass; use UnityWebPortal\lib\exceptions\NoDieException; use UnityWebPortal\lib\exceptions\ArrayKeyException; use RuntimeException;