From e7949d43938203d36a1368cb348f07d09448b052 Mon Sep 17 00:00:00 2001 From: Eli Bosley Date: Mon, 13 Jul 2026 23:55:53 -0400 Subject: [PATCH] fix(csrf): return 403 on CSRF rejection instead of silent empty 200 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When local_prepend.php rejects a POST for a bad/missing CSRF token it logged the error and `exit`ed with no status code, so the client received an empty HTTP 200. A fetch-based caller can't tell that apart from a genuine empty result — e.g. the Unraid API Status panel renders "Not Running" (service down) when the request was actually just blocked by the CSRF gate. Return an explicit 403 with a small JSON body so the rejection is observable and can't masquerade as a real response. Validation logic is unchanged — the gate still rejects and still logs; only the response shape of a rejected request changes. --- emhttp/plugins/dynamix/include/local_prepend.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/emhttp/plugins/dynamix/include/local_prepend.php b/emhttp/plugins/dynamix/include/local_prepend.php index d2d7dfd4e2..4bc9b02f0b 100644 --- a/emhttp/plugins/dynamix/include/local_prepend.php +++ b/emhttp/plugins/dynamix/include/local_prepend.php @@ -16,7 +16,14 @@ function csrf_terminate($reason) { exec('logger -t webGUI -- '.escapeshellarg("error: {$_SERVER['REQUEST_URI']} - {$reason} csrf_token")); - exit; + // Reject with an explicit 403 instead of a silent empty 200 so callers can + // tell a CSRF failure apart from a genuine empty result (e.g. a fetch-based + // status read must not render as "service down" when it was simply blocked). + if (!headers_sent()) { + http_response_code(403); + header('Content-Type: application/json'); + } + exit(json_encode(['error' => "{$reason} csrf_token"])); } putenv('PATH=.:/usr/local/sbin:/usr/sbin:/sbin:/usr/local/bin:/usr/bin:/bin');