You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by mi...@apache.org on 2026/09/21 21:14:39 UTC

(superset) branch master updated: fix(security): skip session checks for health probes (#43786)

This is an automated email from the ASF dual-hosted git repository.

aminghadersohi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new 782b7ae924c fix(security): skip session checks for health probes (#43786)
782b7ae924c is described below

commit 782b7ae924cb6508d0ba04a3098ad0725ae6a119
Author: Amin Ghadersohi <am...@gmail.com>
AuthorDate: Tue Sep 22 07:14:31 2026 +1000

    fix(security): skip session checks for health probes (#43786)
---
 superset/security/session_invalidation.py              | 16 +++++++++++++++-
 tests/unit_tests/security/test_session_invalidation.py | 18 ++++++++++++++++++
 2 files changed, 33 insertions(+), 1 deletion(-)

diff --git a/superset/security/session_invalidation.py b/superset/security/session_invalidation.py
index a15a8b9b14d..670aed3bae9 100644
--- a/superset/security/session_invalidation.py
+++ b/superset/security/session_invalidation.py
@@ -38,7 +38,7 @@ import math
 from datetime import datetime, timezone
 from typing import Any, Optional
 
-from flask import flash, session
+from flask import flash, has_request_context, request, session
 from flask_babel import gettext as __
 from flask_login import current_user, logout_user
 from sqlalchemy import event, inspect, or_
@@ -50,6 +50,12 @@ logger = logging.getLogger(__name__)
 #: Session key holding the epoch-seconds timestamp of when the session logged in.
 SESSION_LOGIN_AT_KEY = "_login_at"
 
+# Health checks are deliberately independent of authentication and the metadata
+# database, so they must not resolve ``current_user`` or perform session checks.
+# Every probe rule registered by ``health()`` in ``superset/views/health.py``
+# resolves to this single endpoint.
+_HEALTH_CHECK_ENDPOINT = "health.health"
+
 
 def _utcnow() -> datetime:
     return datetime.now(timezone.utc)
@@ -108,6 +114,14 @@ def enforce_session_validity() -> Optional[Response]:
     Fails open — any error here logs a warning and allows the request rather
     than risk locking everyone out on a bug in the check.
     """
+    # Do this before touching current_user: it is a LocalProxy whose resolution
+    # may query the metadata DB. Probes do not need session invalidation
+    # enforcement. Flask matches the URL in ``RequestContext.push()``, before
+    # ``preprocess_request()`` runs the ``before_request`` funcs, so
+    # ``request.endpoint`` is already resolved here.
+    if has_request_context() and request.endpoint == _HEALTH_CHECK_ENDPOINT:
+        return None
+
     try:
         user = current_user
         if not user or not getattr(user, "is_authenticated", False):
diff --git a/tests/unit_tests/security/test_session_invalidation.py b/tests/unit_tests/security/test_session_invalidation.py
index 85f5e1c70fc..9c5574d6dfc 100644
--- a/tests/unit_tests/security/test_session_invalidation.py
+++ b/tests/unit_tests/security/test_session_invalidation.py
@@ -17,6 +17,7 @@
 
 from datetime import datetime, timedelta, timezone
 from types import SimpleNamespace
+from typing import Any
 from unittest.mock import MagicMock, patch
 
 from sqlalchemy.exc import IntegrityError
@@ -95,6 +96,23 @@ def test_enforce_skips_unauthenticated_user() -> None:
         logout.assert_not_called()
 
 
+def test_enforce_skips_health_check_before_resolving_user(app: Any) -> None:
+    """Health probes must not resolve current_user or touch metadata."""
+    rules = [str(r) for r in app.url_map.iter_rules() if r.endpoint == "health.health"]
+    assert rules, "no health probe routes registered"
+    for rule in rules:
+        with app.test_request_context(rule):
+            with (
+                patch(f"{MODULE}.current_user") as current,
+                patch(f"{MODULE}.logger.warning") as warning,
+                patch(f"{MODULE}.logout_user") as logout,
+            ):
+                assert enforce_session_validity() is None
+                current.__bool__.assert_not_called()
+                warning.assert_not_called()
+                logout.assert_not_called()
+
+
 def test_enforce_skips_guest_user() -> None:
     """Guest (embedded) users have their own revocation path and are skipped."""
     with (