From 614848f2ad7fa9a4422adf1a21c34e5046f6f156 Mon Sep 17 00:00:00 2001 From: sha174n Date: Thu, 25 Jun 2026 15:44:47 +0100 Subject: [PATCH 1/3] fix(explore): apply per-datasource access check on the legacy explore view Mirror the per-datasource access check the /api/v1/explore command already performs (security_manager.raise_for_access(datasource=...)) on the deprecated /superset/explore/ view, so both paths apply the same per-object datasource check before rendering datasource metadata. Guarded for the placeholder (missing-dataset) case the view already supports. Adds a regression test. --- superset/views/core.py | 5 +++++ tests/integration_tests/core_tests.py | 16 ++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/superset/views/core.py b/superset/views/core.py index 7648f4310e2e..02477056b77f 100755 --- a/superset/views/core.py +++ b/superset/views/core.py @@ -505,6 +505,11 @@ def explore( # noqa: C901 datasource_id, ) + # Apply the same per-datasource access check the explore command performs, + # so this view is consistent with it before rendering datasource metadata. + if datasource: + security_manager.raise_for_access(datasource=datasource) + datasource_name = datasource.name if datasource else _("[Missing Dataset]") viz_type = form_data.get("viz_type") if not viz_type and datasource and datasource.default_endpoint: diff --git a/tests/integration_tests/core_tests.py b/tests/integration_tests/core_tests.py index 4dea036e7a52..8d19cbe5457f 100644 --- a/tests/integration_tests/core_tests.py +++ b/tests/integration_tests/core_tests.py @@ -887,6 +887,22 @@ def test_explore_redirect(self, mock_command: mock.Mock): ) assert rv.headers["Location"] == f"/explore/?form_data_key={random_key}" + @pytest.mark.usefixtures("load_energy_table_with_slice") + @mock.patch("superset.security.SupersetSecurityManager.raise_for_access") + def test_explore_view_checks_datasource_access(self, mock_raise_for_access): + """The explore view runs the per-datasource access check on the loaded + datasource, consistent with the explore command, before rendering its + metadata.""" + self.login(ADMIN_USERNAME) + tbl_id = self.table_ids.get("energy_usage") + mock_raise_for_access.reset_mock() + + self.client.post(f"/superset/explore/table/{tbl_id}/") + + mock_raise_for_access.assert_called_once() + _, kwargs = mock_raise_for_access.call_args + assert kwargs["datasource"].id == tbl_id + @pytest.mark.usefixtures("load_birth_names_dashboard_with_slices") def test_has_table(self): if backend() in ("sqlite", "mysql"): From 8dd1f7b0183586a08bc682e076926c20459251c4 Mon Sep 17 00:00:00 2001 From: sha174n Date: Thu, 25 Jun 2026 22:04:54 +0100 Subject: [PATCH 2/3] test: add type annotations to explore view test method Co-Authored-By: Claude Opus 4.8 --- tests/integration_tests/core_tests.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/integration_tests/core_tests.py b/tests/integration_tests/core_tests.py index 8d19cbe5457f..79947e18c010 100644 --- a/tests/integration_tests/core_tests.py +++ b/tests/integration_tests/core_tests.py @@ -889,7 +889,9 @@ def test_explore_redirect(self, mock_command: mock.Mock): @pytest.mark.usefixtures("load_energy_table_with_slice") @mock.patch("superset.security.SupersetSecurityManager.raise_for_access") - def test_explore_view_checks_datasource_access(self, mock_raise_for_access): + def test_explore_view_checks_datasource_access( + self, mock_raise_for_access: mock.Mock + ) -> None: """The explore view runs the per-datasource access check on the loaded datasource, consistent with the explore command, before rendering its metadata.""" From d9c315a5c26af8151196cfaec4c5c56badf2d5ce Mon Sep 17 00:00:00 2001 From: sha174n Date: Fri, 26 Jun 2026 00:29:45 +0100 Subject: [PATCH 3/3] refactor: tighten explore view access-check comment Condense the explanatory comment on the per-datasource access check to match the file's terse style for security calls. Co-Authored-By: Claude Opus 4.8 --- superset/views/core.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/superset/views/core.py b/superset/views/core.py index 02477056b77f..e7ad61795c8a 100755 --- a/superset/views/core.py +++ b/superset/views/core.py @@ -505,8 +505,7 @@ def explore( # noqa: C901 datasource_id, ) - # Apply the same per-datasource access check the explore command performs, - # so this view is consistent with it before rendering datasource metadata. + # Enforce per-datasource access before rendering its metadata. if datasource: security_manager.raise_for_access(datasource=datasource)