You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by el...@apache.org on 2026/09/03 22:07:30 UTC
(superset) branch master updated: fix(security): catch JSONDecodeError on stored query_context in query_context_modified (#43795)
This is an automated email from the ASF dual-hosted git repository.
eschutho 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 3258f5fa3f6 fix(security): catch JSONDecodeError on stored query_context in query_context_modified (#43795)
3258f5fa3f6 is described below
commit 3258f5fa3f6b1eaa87624e7716606a1f6933abd3
Author: Elizabeth Thompson <es...@gmail.com>
AuthorDate: Thu Sep 3 15:07:24 2026 -0700
fix(security): catch JSONDecodeError on stored query_context in query_context_modified (#43795)
Co-authored-by: Claude Opus 4.8 <no...@anthropic.com>
---
superset/security/manager.py | 24 +++++++++++++++++++-----
tests/unit_tests/security/manager_test.py | 25 +++++++++++++++++++++++++
2 files changed, 44 insertions(+), 5 deletions(-)
diff --git a/superset/security/manager.py b/superset/security/manager.py
index 36b1262352a..786642640e9 100644
--- a/superset/security/manager.py
+++ b/superset/security/manager.py
@@ -1630,11 +1630,25 @@ def query_context_modified(query_context: "QueryContext") -> bool:
)
return True
- stored_query_context = (
- json.loads(cast(str, stored_chart.query_context))
- if stored_chart.query_context
- else None
- )
+ try:
+ stored_query_context = (
+ json.loads(cast(str, stored_chart.query_context))
+ if stored_chart.query_context
+ else None
+ )
+ except (json.JSONDecodeError, TypeError):
+ # A stored query_context that fails to parse cannot be compared against
+ # the guest payload, so it is treated as modified/tampered (returning
+ # True triggers the SupersetSecurityException 403 in raise_for_access),
+ # consistent with the other rejection branches below. Malformed
+ # query_context can be persisted by the query-context-only update path
+ # (see ChartUpdateCommand._validate_query_context_datasource).
+ logger.warning(
+ "Guest chart payload rejected for slice %s: stored query_context "
+ "is not valid JSON",
+ stored_chart.id,
+ )
+ return True
# A rejected guest load is most often a chart whose saved query_context is
# NULL or stale rather than genuine tampering, and the generic 403 gives no
diff --git a/tests/unit_tests/security/manager_test.py b/tests/unit_tests/security/manager_test.py
index c78ec9dd674..ed3934510ff 100644
--- a/tests/unit_tests/security/manager_test.py
+++ b/tests/unit_tests/security/manager_test.py
@@ -1380,6 +1380,31 @@ def test_query_context_modified_tampered(
assert query_context_modified(query_context)
+def test_query_context_modified_malformed_stored_query_context(
+ mocker: MockerFixture,
+ stored_metrics: list[AdhocMetric],
+) -> None:
+ """
+ A stored ``query_context`` that is not valid JSON (which the query-context-only
+ chart update path can persist) must be treated as modified/tampered rather than
+ crashing with a raw ``JSONDecodeError``. Returning ``True`` lets
+ ``raise_for_access`` deny the guest with the intended 403.
+ """
+ query_context = mocker.MagicMock()
+ query_context.slice_.id = 42
+ query_context.slice_.query_context = "not valid json"
+ query_context.slice_.params_dict = {
+ "metrics": stored_metrics,
+ }
+
+ query_context.form_data = {
+ "slice_id": 42,
+ "metrics": stored_metrics,
+ }
+ query_context.queries = [QueryObject(metrics=stored_metrics)] # type: ignore
+ assert query_context_modified(query_context)
+
+
def test_query_context_modified_singular_metric_param(
mocker: MockerFixture,
) -> None: