]> git.ipfire.org Git - thirdparty/rspamd.git/commitdiff
[Fix] WebUI: route a malformed /stat response to the login dialog
authorAlexander Moisseev <moiseev@mezonplus.ru>
Fri, 10 Jul 2026 16:13:34 +0000 (19:13 +0300)
committerAlexander Moisseev <moiseev@mezonplus.ru>
Fri, 10 Jul 2026 16:13:34 +0000 (19:13 +0300)
The connect probe swallowed JSON parse errors (parseJsonSafe returned {}),
so a 2xx /stat with a non-JSON body (truncated by a proxy, a captive-portal
page, corruption) loaded an empty, half-loaded main UI with a "Cannot get
server status" alert and no login path. The former jQuery content-type
parse raised parsererror and fell through to the login form.

Parse the body explicitly and, on any failure, show the connect dialog —
matching the pre-migration behaviour and covering the broad case (any
non-JSON 2xx, not only application/json). Removes the now-unused
parseJsonSafe helper.

interface/js/app/rspamd.js

index c41cd288e5f0d56b45fbfd3ebf505398bec2dc1f..5afd2bafd6e7ffb315b078c25f5af9ba294939d9 100644 (file)
@@ -293,17 +293,6 @@ define(["app/common", "bootstrap", "visibility",
         });
     }
 
-
-    // Parse JSON, returning {} for an empty or non-JSON body (the /stat probe
-    // must not throw and strand the connect flow).
-    function parseJsonSafe(text) {
-        try {
-            return text ? JSON.parse(text) : {};
-        } catch (err) {
-            return {};
-        }
-    }
-
     // Show the connect (login) dialog and wire its form submission.
     function showConnectDialog() {
         const connectDialog = document.getElementById("connectDialog");
@@ -385,9 +374,16 @@ define(["app/common", "bootstrap", "visibility",
         if (ajaxTimeout > 0) xhr.timeout = ajaxTimeout;
         xhr.onload = () => {
             if (xhr.status >= 200 && xhr.status < 300) {
-                const data = parseJsonSafe(xhr.responseText);
-                sessionStorage.setItem("read_only", data.read_only);
-                displayUI();
+                try {
+                    const data = JSON.parse(xhr.responseText);
+                    sessionStorage.setItem("read_only", data.read_only);
+                    displayUI();
+                } catch (err) {
+                    // A 2xx /stat whose body isn't JSON means a broken response
+                    // (truncated by a proxy, a captive-portal page, etc.): show
+                    // the login dialog rather than an empty, half-loaded UI.
+                    showConnectDialog();
+                }
             } else {
                 showConnectDialog();
             }