From 70cbb688054a337567b8ebf7644851a8f58a3510 Mon Sep 17 00:00:00 2001 From: Edward Gou Date: Thu, 27 Nov 2025 10:39:09 -0500 Subject: [PATCH] Add edit and delete guard to backend dashboards endpoint --- .../organization_dashboard_details.py | 6 +++++ .../test_organization_dashboard_details.py | 23 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/sentry/dashboards/endpoints/organization_dashboard_details.py b/src/sentry/dashboards/endpoints/organization_dashboard_details.py index c0657aad9138ab..aa05b7c578686f 100644 --- a/src/sentry/dashboards/endpoints/organization_dashboard_details.py +++ b/src/sentry/dashboards/endpoints/organization_dashboard_details.py @@ -127,6 +127,9 @@ def delete( num_dashboards = Dashboard.objects.filter(organization=organization).count() num_tombstones = DashboardTombstone.objects.filter(organization=organization).count() + if isinstance(dashboard, Dashboard) and dashboard.prebuilt_id is not None: + return self.respond({"Cannot delete prebuilt Dashboards."}, status=409) + if isinstance(dashboard, dict): if num_dashboards > 0: DashboardTombstone.objects.get_or_create( @@ -170,6 +173,9 @@ def put( self.check_object_permissions(request, dashboard) + if isinstance(dashboard, Dashboard) and dashboard.prebuilt_id is not None: + return self.respond({"Cannot edit prebuilt Dashboards."}, status=409) + tombstone = None if isinstance(dashboard, dict): tombstone = dashboard["id"] diff --git a/tests/sentry/dashboards/endpoints/test_organization_dashboard_details.py b/tests/sentry/dashboards/endpoints/test_organization_dashboard_details.py index ec276bee818812..4d440a46dc0493 100644 --- a/tests/sentry/dashboards/endpoints/test_organization_dashboard_details.py +++ b/tests/sentry/dashboards/endpoints/test_organization_dashboard_details.py @@ -9,6 +9,7 @@ from django.urls import reverse from django.utils import timezone +from sentry.dashboards.endpoints.organization_dashboards import PrebuiltDashboardId from sentry.discover.models import DatasetSourcesTypes from sentry.explore.translation.dashboards_translation import translate_dashboard_widget from sentry.models.dashboard import ( @@ -3410,6 +3411,28 @@ def test_does_not_update_if_linked_dashboard_does_not_appear_in_fields(self) -> assert response.status_code == 400, response.data assert b"Linked dashboard does not appear in the fields of the query" in response.content + def test_cannot_delete_prebuilt_insights_dashboard(self) -> None: + dashboard = Dashboard.objects.create( + title="Frontend Session Health", + organization=self.organization, + prebuilt_id=PrebuiltDashboardId.FRONTEND_SESSION_HEALTH, + ) + response = self.do_request("delete", self.url(dashboard.id)) + assert response.status_code == 409 + assert "Cannot delete prebuilt Dashboards." in response.content.decode() + + def test_cannot_edit_prebuilt_insights_dashboard(self) -> None: + dashboard = Dashboard.objects.create( + title="Frontend Session Health", + organization=self.organization, + prebuilt_id=PrebuiltDashboardId.FRONTEND_SESSION_HEALTH, + ) + response = self.do_request( + "put", self.url(dashboard.id), data={"title": "Frontend Session Health Edited"} + ) + assert response.status_code == 409 + assert "Cannot edit prebuilt Dashboards." in response.content.decode() + class OrganizationDashboardDetailsOnDemandTest(OrganizationDashboardDetailsTestCase): widget_type = DashboardWidgetTypes.TRANSACTION_LIKE