Browse Source

fix(security): deny publish and delete of integrations for manager role (#62227)

Currently, a member with the Manager role can delete an integration
and/or make a publish request by forging a request to the backend,
bypassing the frontend.

This PR matches the backend validation with what the frontend says.
According to
[this](https://github.com/getsentry/sentry/blob/master/static/app/views/settings/organizationDeveloperSettings/sentryApplicationRow/sentryApplicationRowButtons.tsx#L24),
`org:admin` scope is required:
<img width="295" alt="image"
src="https://github.com/getsentry/sentry/assets/1127549/0a19890e-23ed-404b-82b5-bc330c9892f1">
Alexander Tarasov 1 year ago
parent
commit
9bc46463bf

+ 4 - 4
src/sentry/api/bases/sentryapps.py

@@ -177,15 +177,15 @@ class SentryAppPermission(SentryPermission):
     unpublished_scope_map = {
         "GET": ("org:read", "org:integrations", "org:write", "org:admin"),
         "PUT": ("org:write", "org:admin"),
-        "POST": ("org:write", "org:admin"),  # used for publishing an app
-        "DELETE": ("org:write", "org:admin"),
+        "POST": ("org:admin",),  # used for publishing an app
+        "DELETE": ("org:admin",),
     }
 
     published_scope_map = {
         "GET": PARANOID_GET,
         "PUT": ("org:write", "org:admin"),
-        "POST": ("org:write", "org:admin"),
-        "DELETE": ("org:admin"),
+        "POST": ("org:admin",),
+        "DELETE": ("org:admin",),
     }
 
     @property

+ 9 - 0
tests/sentry/api/endpoints/test_sentry_app_details.py

@@ -509,3 +509,12 @@ class DeleteSentryAppDetailsTest(SentryAppDetailsTest):
         self.published_app.update(metadata={"partnership_restricted": True})
         response = self.client.delete(self.url)
         assert response.status_code == 403
+
+    def test_cannot_delete_by_manager(self):
+        self.user_manager = self.create_user("manager@example.com", is_superuser=False)
+        self.create_member(user=self.user_manager, organization=self.org, role="manager", teams=[])
+        self.login_as(self.user_manager)
+
+        url = reverse("sentry-api-0-sentry-app-details", args=[self.internal_integration.slug])
+        response = self.client.delete(url)
+        assert response.status_code == 403

+ 9 - 0
tests/sentry/api/endpoints/test_sentry_app_publish_request.py

@@ -116,3 +116,12 @@ class SentryAppPublishRequestTest(APITestCase):
             == "Must upload an icon for issue and stack trace linking integrations."
         )
         send_mail.asssert_not_called()
+
+    def test_cannot_publish_by_manager(self):
+        self.user_manager = self.create_user("manager@example.com", is_superuser=False)
+        self.create_member(user=self.user_manager, organization=self.org, role="manager", teams=[])
+        self.login_as(self.user_manager)
+
+        url = reverse("sentry-api-0-sentry-app-publish-request", args=[self.sentry_app.slug])
+        response = self.client.post(url, format="json")
+        assert response.status_code == 403