fix(explore): catch TemplateError when validating access for query-backed form_data

check_query_access() calls raise_for_access(query=query), which Jinja-renders
the query's SQL to resolve table references. A malformed template surfaces
as a raw jinja2.exceptions.TemplateError instead of a Superset exception,
leaking as an opaque 500 from the explore form_data endpoints (used by the
chart Explore/Drill-by cache) whenever datasource_type=query.

Wrap the call and re-raise as the existing SupersetTemplateException (422),
matching the same conversion already used in datasets/api.py, and map it to
a proper response in ExploreFormDataRestApi's four handlers.
This commit is contained in:
Elizabeth Thompson
2026-08-24 16:41:44 +00:00
parent 3f10d8b1cc
commit b4fac82c39
3 changed files with 43 additions and 2 deletions
+24 -1
View File
@@ -15,6 +15,7 @@
# specific language governing permissions and limitations
# under the License.
from flask_appbuilder.security.sqla.models import User
from jinja2.exceptions import TemplateSyntaxError
from pytest import raises # noqa: PT013
from pytest_mock import MockerFixture
@@ -30,7 +31,7 @@ from superset.commands.exceptions import (
DatasourceNotFoundValidationError,
QueryNotFoundValidationError,
)
from superset.exceptions import SupersetSecurityException
from superset.exceptions import SupersetSecurityException, SupersetTemplateException
from superset.utils.core import DatasourceType, override_user
dataset_find_by_id = "superset.daos.dataset.DatasetDAO.find_by_id"
@@ -340,6 +341,28 @@ def test_query_has_access(mocker: MockerFixture) -> None:
)
def test_query_malformed_jinja_template(mocker: MockerFixture) -> None:
"""
``raise_for_access(query=...)`` Jinja-renders the query's SQL to resolve
the tables it touches. A malformed template must surface as a
``SupersetTemplateException``, not the raw ``jinja2`` exception.
"""
from superset.explore.utils import check_datasource_access
from superset.models.sql_lab import Query
mocker.patch(query_find_by_id, return_value=Query())
mocker.patch(
raise_for_access,
side_effect=TemplateSyntaxError("unexpected end of template", lineno=1),
)
with raises(SupersetTemplateException): # noqa: PT012
check_datasource_access(
datasource_id=1,
datasource_type=DatasourceType.QUERY,
)
def test_query_no_access(mocker: MockerFixture, client) -> None:
from superset.connectors.sqla.models import SqlaTable
from superset.explore.utils import check_datasource_access