codeant-ai-for-open-source[bot] commented on code in PR #41133:
URL: https://github.com/apache/superset/pull/41133#discussion_r3593612819


##########
tests/integration_tests/dashboards/api_tests.py:
##########
@@ -3262,6 +3264,173 @@ def 
test_get_all_related_viewers_with_extra_filters(self):
         response = json.loads(rv.data.decode("utf-8"))
         assert response["count"] > 0
 
+    def test_export_xlsx_501_when_bucket_unset(self):
+        """Dashboard API: export_xlsx returns 501 when the S3 bucket is 
unset."""
+        admin = self.get_user("admin")
+        dashboard = self.insert_dashboard("xlsx-501", None, [admin.id])
+        self.login(ADMIN_USERNAME)
+        try:
+            rv = 
self.client.post(f"api/v1/dashboard/{dashboard.id}/export_xlsx/")
+            assert rv.status_code == 501
+        finally:
+            db.session.delete(dashboard)
+            db.session.commit()
+
+    @with_config({"EXCEL_EXPORT_S3_BUCKET": "exports"})
+    @patch("superset.dashboards.api.export_dashboard_excel")
+    def test_export_xlsx_404_for_missing_dashboard(self, mock_task):
+        """Dashboard API: export_xlsx returns 404 for an unknown dashboard."""
+        self.login(ADMIN_USERNAME)
+        rv = self.client.post("api/v1/dashboard/99999999/export_xlsx/")
+        assert rv.status_code == 404
+        mock_task.apply_async.assert_not_called()
+
+    @with_config({"EXCEL_EXPORT_S3_BUCKET": "exports"})
+    @patch("superset.dashboards.api.export_dashboard_excel")
+    def test_export_xlsx_400_for_empty_dashboard(self, mock_task):
+        """Dashboard API: export_xlsx returns 400 for a dashboard with no 
charts."""
+        admin = self.get_user("admin")
+        dashboard = self.insert_dashboard("xlsx-empty", None, [admin.id])
+        self.login(ADMIN_USERNAME)
+        try:
+            rv = 
self.client.post(f"api/v1/dashboard/{dashboard.id}/export_xlsx/")
+            assert rv.status_code == 400
+            mock_task.apply_async.assert_not_called()
+        finally:
+            db.session.delete(dashboard)
+            db.session.commit()
+
+    @pytest.mark.usefixtures("load_world_bank_dashboard_with_slices")
+    @with_config({"EXCEL_EXPORT_S3_BUCKET": "exports"})
+    @patch("superset.dashboards.api.AcquireDistributedLock")
+    @patch("superset.dashboards.api.export_dashboard_excel")
+    def test_export_xlsx_202_enqueues_task(self, mock_task, mock_acquire):
+        """Dashboard API: export_xlsx enqueues the task and returns 202 + 
job_id."""
+        self.login(ADMIN_USERNAME)
+        dashboard = 
db.session.query(Dashboard).filter_by(slug="world_health").first()
+        rv = self.client.post(
+            f"api/v1/dashboard/{dashboard.id}/export_xlsx/",
+            json={"active_data_mask": {}},
+        )
+        assert rv.status_code == 202
+        body = json.loads(rv.data.decode("utf-8"))
+        job_id = body["job_id"]
+        assert job_id
+        # The in-flight lock is acquired before the task is enqueued.
+        mock_acquire.return_value.run.assert_called_once()
+        mock_task.apply_async.assert_called_once()
+        _, kwargs = mock_task.apply_async.call_args
+        assert kwargs["task_id"] == job_id
+        assert kwargs["kwargs"]["dashboard_id"] == dashboard.id
+
+    @pytest.mark.usefixtures("load_world_bank_dashboard_with_slices")
+    @with_config({"EXCEL_EXPORT_S3_BUCKET": "exports"})
+    @patch("superset.dashboards.api.AcquireDistributedLock")
+    @patch("superset.dashboards.api.export_dashboard_excel")
+    def test_export_xlsx_202_when_export_already_in_progress(
+        self, mock_task, mock_acquire
+    ):
+        """Dashboard API: export_xlsx does not enqueue a second concurrent 
export."""
+        # An in-flight lock is already held for this user+dashboard.
+        mock_acquire.return_value.run.side_effect = 
LockAlreadyHeldException("held")
+        self.login(ADMIN_USERNAME)
+        dashboard = 
db.session.query(Dashboard).filter_by(slug="world_health").first()
+        rv = self.client.post(
+            f"api/v1/dashboard/{dashboard.id}/export_xlsx/",
+            json={"active_data_mask": {}},
+        )
+        assert rv.status_code == 202
+        assert "already in progress" in rv.data.decode("utf-8")
+        mock_task.apply_async.assert_not_called()
+
+    @with_config({"EXCEL_EXPORT_S3_BUCKET": "exports"})
+    @patch("superset.dashboards.api.export_dashboard_excel")
+    def test_export_xlsx_404_for_inaccessible_dashboard(self, mock_task):
+        """Dashboard API: export_xlsx returns 404 for a dashboard the user 
can't see."""
+        admin = self.get_user("admin")
+        dashboard = self.insert_dashboard(
+            "xlsx-private", None, [admin.id], published=False
+        )
+        self.login(GAMMA_USERNAME)
+        try:
+            rv = 
self.client.post(f"api/v1/dashboard/{dashboard.id}/export_xlsx/")
+            assert rv.status_code == 404
+            mock_task.apply_async.assert_not_called()
+        finally:
+            db.session.delete(dashboard)
+            db.session.commit()
+
+    @pytest.mark.usefixtures("load_world_bank_dashboard_with_slices")
+    @with_config({"EXCEL_EXPORT_S3_BUCKET": "exports"})
+    @patch("superset.dashboards.api.AcquireDistributedLock")
+    @patch("superset.dashboards.api.export_dashboard_excel")
+    @patch("superset.dashboards.api.security_manager.raise_for_access")
+    def test_export_xlsx_admitted_with_can_export_only(
+        self, mock_raise, mock_task, mock_acquire
+    ):

Review Comment:
   **Suggestion:** This test patches out `security_manager.raise_for_access`, 
which bypasses a real authorization check in the endpoint and can let the test 
pass even if access-control behavior regresses. Remove this patch (or assert it 
is called with the expected dashboard) so the test validates the true 
integration of permission mapping plus dashboard access logic. [incomplete 
implementation]
   
   <details>
   <summary><b>Severity Level:</b> Major ⚠️</summary>
   
   ```mdx
   ⚠️ Excel export auth regressions may go undetected by tests.
   ⚠️ Test passes even if raise_for_access call removed.
   ```
   </details>
   <details>
   <summary><b>Steps of Reproduction ✅ </b></summary>
   
   ```mdx
   1. Inspect the dashboard Excel export endpoint implementation at
   `superset/dashboards/api.py:1588-1619` and 
`superset/dashboards/api.py:1600-1675`. The
   `DashboardRestApi.export_xlsx` method (decorated with 
`@expose("/<pk>/export_xlsx/",
   methods=("POST",))` and `@protect()`) calls
   `security_manager.raise_for_access(dashboard=dashboard)` inside a `try/except
   SupersetSecurityException` block to enforce a 403 when the user lacks access.
   
   2. Inspect the integration test 
`test_export_xlsx_admitted_with_can_export_only` in
   `tests/integration_tests/dashboards/api_tests.py:164-193` (corresponding to 
diff lines
   3363-3393). The test is decorated with
   `@patch("superset.dashboards.api.security_manager.raise_for_access")` and 
receives
   `mock_raise` but never asserts on it; `raise_for_access` is replaced with a 
MagicMock
   during the test run.
   
   3. Run the test suite (e.g. `pytest
   
tests/integration_tests/dashboards/api_tests.py::TestDashboardApi::test_export_xlsx_admitted_with_can_export_only`).
   During this test, the real authorization logic inside 
`security_manager.raise_for_access`
   (implemented in `superset/security/manager.py` and exercised by
   `tests/unit_tests/subjects/test_raise_for_access.py`) is completely bypassed 
because the
   patched MagicMock never raises `SupersetSecurityException`, regardless of 
actual dashboard
   access rules.
   
   4. To see the false-positive behavior concretely, deliberately break the 
endpoint’s access
   check by removing or commenting out the
   `security_manager.raise_for_access(dashboard=dashboard)` call in
   `superset/dashboards/api.py:72-75` (inside `export_xlsx`) and rerun
   `test_export_xlsx_admitted_with_can_export_only`. The test still passes 
(asserting only
   status 202 and `export_dashboard_excel.apply_async` being called) even 
though the endpoint
   no longer performs the dashboard access check, proving that the current 
patching strategy
   lets authorization regressions slip through.
   ```
   </details>
   
   [![Fix in 
Cursor](https://new-codeant-butcket.s3.us-west-1.amazonaws.com/badges/fix-in-cursor-flat.svg)](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=5f7405b38ae547c4bd36a85bdca521a7&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
 [![Fix in VSCode 
Claude](https://new-codeant-butcket.s3.us-west-1.amazonaws.com/badges/fix-in-vscode-claude-flat.svg)](https://app.codeant.ai/fix-in-ide?tool=vscode-claude&prompt_id=5f7405b38ae547c4bd36a85bdca521a7&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
   
   *(Use Cmd/Ctrl + Click for best experience)*
   <details>
   <summary><b>Prompt for AI Agent 🤖 </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** tests/integration_tests/dashboards/api_tests.py
   **Line:** 3367:3370
   **Comment:**
        *Incomplete Implementation: This test patches out 
`security_manager.raise_for_access`, which bypasses a real authorization check 
in the endpoint and can let the test pass even if access-control behavior 
regresses. Remove this patch (or assert it is called with the expected 
dashboard) so the test validates the true integration of permission mapping 
plus dashboard access logic.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   Once fix is implemented, also check other comments on the same PR, and ask 
user if the user wants to fix the rest of the comments as well. if said yes, 
then fetch all the comments validate the correctness and implement a minimal fix
   ```
   </details>
   <a 
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F41133&comment_hash=cf19d27cc6071ac22e53ee19f9a98f89a8d1a28d1b36953f419304bd578b25fd&reaction=like'>👍</a>
 | <a 
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F41133&comment_hash=cf19d27cc6071ac22e53ee19f9a98f89a8d1a28d1b36953f419304bd578b25fd&reaction=dislike'>👎</a>



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to