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

eschutho pushed a commit to branch 
fix-query-context-raise-for-access-template-error
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 16f7ff747b9c6b8e1726cc1e3618ac6bbc5f749e
Author: Elizabeth Thompson <[email protected]>
AuthorDate: Tue Sep 8 16:43:31 2026 +0000

    fix(query_context): raise 422 not 500 on malformed Jinja in Query 
datasource access check
    
    QueryContextProcessor.raise_for_access() calls
    security_manager.raise_for_access(query=...) when the datasource is a
    SQL Lab Query. That method Jinja-renders the query's SQL to resolve the
    tables it touches; a malformed template raises a raw
    jinja2.TemplateError that was not caught, producing an opaque 500.
    
    Wrap the call in a try/except that converts TemplateError to
    SupersetTemplateException (status 422), matching the identical pattern
    already used in explore/utils.py::check_query_access().
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
---
 superset/common/query_context_processor.py         |  7 ++++-
 .../common/test_query_context_processor.py         | 30 ++++++++++++++++++++++
 2 files changed, 36 insertions(+), 1 deletion(-)

diff --git a/superset/common/query_context_processor.py 
b/superset/common/query_context_processor.py
index 2c27fb9fb06..1f845cf9a45 100644
--- a/superset/common/query_context_processor.py
+++ b/superset/common/query_context_processor.py
@@ -26,6 +26,7 @@ import pandas as pd
 import pyarrow as pa
 from flask import current_app
 from flask_babel import gettext as _
+from jinja2.exceptions import TemplateError
 from pandas.api.types import infer_dtype
 
 from superset.common.chart_data import ChartDataResultFormat
@@ -45,6 +46,7 @@ from superset.daos.chart import ChartDAO
 from superset.exceptions import (
     QueryObjectValidationError,
     SupersetException,
+    SupersetTemplateException,
 )
 from superset.explorables.base import Explorable
 from superset.extensions import cache_manager, security_manager
@@ -933,7 +935,10 @@ class QueryContextProcessor:
         # come first to avoid rendering caller-supplied input for a resource 
the
         # caller is not allowed to access.
         if self._qc_datasource.type == DatasourceType.QUERY:
-            security_manager.raise_for_access(query=self._qc_datasource)
+            try:
+                security_manager.raise_for_access(query=self._qc_datasource)
+            except TemplateError as ex:
+                raise SupersetTemplateException(str(ex)) from ex
         else:
             
security_manager.raise_for_access(query_context=self._query_context)
 
diff --git a/tests/unit_tests/common/test_query_context_processor.py 
b/tests/unit_tests/common/test_query_context_processor.py
index 5dc21d12b85..777c188787d 100644
--- a/tests/unit_tests/common/test_query_context_processor.py
+++ b/tests/unit_tests/common/test_query_context_processor.py
@@ -2220,6 +2220,36 @@ def 
test_raise_for_access_evaluates_access_before_validate():
     query.validate.assert_not_called()
 
 
+def test_raise_for_access_wraps_template_error_for_query_datasource():
+    """
+    When the datasource is a SQL Lab Query and raise_for_access() Jinja-renders
+    malformed SQL, the raw jinja2 TemplateError must be wrapped in
+    SupersetTemplateException (422) instead of leaking as an unhandled 500.
+    """
+    from jinja2.exceptions import TemplateError, TemplateSyntaxError
+
+    from superset.exceptions import SupersetTemplateException
+    from superset.utils.core import DatasourceType
+
+    query = MagicMock()
+    query_context = MagicMock()
+    query_context.queries = [query]
+    query_context.datasource.type = DatasourceType.QUERY
+
+    processor = QueryContextProcessor(query_context)
+
+    with patch(
+        
"superset.common.query_context_processor.security_manager.raise_for_access",
+        side_effect=TemplateSyntaxError("unexpected end of template", 
lineno=1),
+    ):
+        with pytest.raises(SupersetTemplateException) as exc:
+            processor.raise_for_access()
+
+    assert exc.value.status == 422
+    assert isinstance(exc.value.__cause__, TemplateError)
+    query.validate.assert_not_called()
+
+
 def test_grouping_sets_fallback_handles_adhoc_and_physical_columns() -> None:
     """
     The fallback used on engines without native GROUPING SETS support must

Reply via email to