Skip to content

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

Open
eschutho wants to merge 1 commit into
masterfrom
fix-templateerror-explore-query-access
Open

fix(explore): catch TemplateError when validating access for query-backed form_data#43470
eschutho wants to merge 1 commit into
masterfrom
fix-templateerror-explore-query-access

Conversation

@eschutho

Copy link
Copy Markdown
Member

SUMMARY

ExploreFormDataRestApi (the cache backing chart Explore / Drill-by state) lets a client reference a SQL Lab query as the chart's datasource (datasource_type=query). Access to that datasource is validated via superset.explore.utils.check_query_access, which calls security_manager.raise_for_access(query=query). When the query hasn't been executed yet (or was edited after last execution), that call falls back to Jinja-rendering the query's raw SQL (process_jinja_sql) to resolve the tables it references — the exact same rendering step already known to raise jinja2.exceptions.TemplateError for malformed templates (see #42366, #42401, and the more recent #43423/#43433 in the tag-access-check family).

Unlike those command-layer call sites, this one had no except TemplateError at all, so the raw jinja2 exception propagated straight through CreateFormDataCommand/GetFormDataCommand/UpdateFormDataCommand/DeleteFormDataCommand (none of which catch anything but SQLAlchemyError) up to ExploreFormDataRestApi, landing as an opaque, unclassified 500 from all four POST/PUT/GET/DELETE /api/v1/explore/form_data handlers.

PROBLEM

A user-controlled input (a saved SQL Lab query with malformed Jinja templating, referenced as a chart's datasource) can trigger a raw jinja2.exceptions.TemplateError that surfaces as an unclassified 500, instead of a proper 4xx SupersetException.

FIX

  • superset/explore/utils.py::check_query_access now catches TemplateError around the raise_for_access(query=query) call and re-raises it as SupersetTemplateException (existing, 422 — no new exception class needed), preserving the original message.
  • superset/explore/form_data/api.py maps SupersetTemplateException to a proper ex.status response in all four ExploreFormDataRestApi handlers, mirroring the existing except SupersetTemplateException as ex: return self.response(ex.status, message=str(ex)) idiom already used in superset/datasets/api.py.

No behavior change for any other exception type or datasource type.

TESTING INSTRUCTIONS

  • New unit test tests/unit_tests/explore/utils_test.py::test_query_malformed_jinja_template: mocks raise_for_access to raise jinja2.exceptions.TemplateSyntaxError and asserts check_datasource_access now raises SupersetTemplateException instead. Verified this test fails on pre-fix code (raw TemplateSyntaxError propagates) and passes post-fix.
  • ruff check / ruff format --check clean on all changed files.
  • Full tests/unit_tests/explore/ and tests/unit_tests/commands/explore/ suites pass (24 tests).

ADDITIONAL INFORMATION

  • Has associated issue:
  • Required feature flags:
  • Changes UI
  • Includes DB Migration
  • Introduces new feature or API
  • Removes existing feature or API

…cked 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.
@dosubot dosubot Bot added change:backend Requires changing the backend explore:error Related to general errors of Explore labels Aug 24, 2026
@bito-code-review

bito-code-review Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #544c6c

Actionable Suggestions - 0
Review Details
  • Files reviewed - 3 · Commit Range: b4fac82..b4fac82
    • superset/explore/form_data/api.py
    • superset/explore/utils.py
    • tests/unit_tests/explore/utils_test.py
  • Files skipped - 0
  • Tools
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers an incremental AI Review.

  • /review full - Manually triggers a full AI Review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@github-actions github-actions Bot added api Related to the REST API preset-io labels Aug 24, 2026
@eschutho
eschutho requested a review from rebenitez1802 August 24, 2026 16:49

@rebenitez1802 rebenitez1802 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approve — minimal, correct fix that matches the established datasets/api.py idiom and the wider "catch TemplateError from raise_for_access → re-raise a classified Superset exception" prior art. The except SupersetTemplateException clause is reachable in all four handlers (sibling of CommandException, not shadowed by the ValidationError/TemporaryCache* clauses above it), the commands catch only SQLAlchemyError so the exception genuinely propagates, and this is pure 500→422 re-classification with no authorization decision skipped.

One non-blocking ask before/after merge:

🟡 Test coverage — the api.py handler change is untested. test_query_malformed_jinja_template exercises the real check_query_access (good, covers utils.py), but none of the four except SupersetTemplateException handler clauses in superset/explore/form_data/api.py is covered by any test. An integration harness already exists at tests/integration_tests/explore/form_data/api_tests.py but wasn't extended, so deleting any of the four clauses would silently regress the endpoint back to a 500 with no failing test. Suggest adding one endpoint-level test asserting a 422 + message round-trip (or extending that api_tests harness) so the handler mapping can't silently regress.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api Related to the REST API change:backend Requires changing the backend explore:error Related to general errors of Explore preset-io size/M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants