Skip to content

fix(api): tighten published mutation scopes#113261

Closed
dcramer wants to merge 25 commits into
masterfrom
dcramer/fix/api-write-scope-mutations-unstacked
Closed

fix(api): tighten published mutation scopes#113261
dcramer wants to merge 25 commits into
masterfrom
dcramer/fix/api-write-scope-mutations-unstacked

Conversation

@dcramer

@dcramer dcramer commented Apr 17, 2026

Copy link
Copy Markdown
Member

Tighten published mutation scope enforcement and annotate the remaining published mutations that intentionally still accept readonly or member scopes.

This supersedes #113120 and #113194. The old stacked PR and the clean rebuild had diverged; this PR carries the validated branch state directly against master, including the compatibility shims, the follow-up 404 fixes, and the published-mutation audit notes.

Behavioral scope changes

  • Alerts: OrganizationAlertRulePermission and OrganizationDetectorPermission now route alert mutations through OrganizationAlertingMutationPermission, whose POST / PUT / DELETE scope map is alerts:write.
  • Alerts: alert-rule and detector detail flows resolve the target project before object-permission checks so project-scoped alerts:write tokens work without regressing missing alert-rule or detector IDs from 404 to 403.
  • Alerts: deprecated public metric-alert create, update, and delete flows keep temporary API-token compatibility for org:write only. org:read, org:admin, and project:read are not retained for those mutation paths.
  • Alerts: workflow and detector mutation flows now accept alerts:write consistently. Workflow and detector endpoints still retain their previous org:write / org:admin API-token compatibility where those scopes were already accepted.
  • Alerts: uptime alert preview, uptime assertion suggestions, Seer anomaly preview, and cron monitor create/update now follow alerts:write semantics. They keep temporary org:write compatibility only; readonly organization scopes are no longer accepted.
  • Event data export: POST now requires event:write or event:admin; event:read no longer authorizes export creation.
  • Issues: project user issue creation now requires event:write or event:admin; event:read no longer authorizes creating a derived issue occurrence.
  • Replays: replay delete now requires event:admin; project:read, project:write, and event:write no longer authorize deletion. Replay summary generation stays read-like and is explicitly documented as such.

Published mutation note coverage

  • Search and member preference surfaces: added readonly_mutation_scope_exceptions notes for pinned searches, saved searches, recent searches, dashboards, dashboard starring, Discover and Explore saved queries, insights segment starring, and group search view preference endpoints.
  • Personal/member-state writes that still rely on readonly org or member scopes now carry TODO(api-scope-personalization) comments. These are intentionally left as follow-up taxonomy work, not treated as semantically ideal scopes.
  • Onboarding and project setup helpers: added notes for onboarding continuation email, onboarding task updates, repo path parsing, organization project experiment create, relaxed project detail updates, and dashboard generation.
  • Membership, integrations, and request helpers: added notes for organization change requests, member invite and invite-request flows, team membership helpers, relaxed member delete behavior, bulk code mapping suggestions, Codecov token and repo sync helpers, conduit demo credentials, org auth tokens, and webhook signing secret helpers.
  • Read-like helper POSTs: added notes for Seer explorer chat and update, Seer RPC, issue title generation, trace summary, trace explorer AI setup, preprod comparison helpers, Sentry App external issue creation, replay summary generation, and notification action helper endpoints.
  • Audit guard: added permission tests that fail any published mutation endpoint that still accepts readonly scopes unless the endpoint or permission class carries a justification note, and fail alert mutation legacy maps that reintroduce readonly scopes.

Future legacy compat to remove

  • TODO(api-write-scope-compat) in OrganizationAlertRuleIndexEndpoint.POST: drop temporary org:write.
  • TODO(api-write-scope-compat) in OrganizationAlertRuleDetailsEndpoint.PUT and .DELETE: drop temporary org:write.
  • TODO(api-write-scope-compat) in alert-rule project validation: drop temporary org:write project-field fallback.
  • TODO(api-write-scope-compat) in workflow and detector endpoints: drop temporary org:write / org:admin compatibility after public clients migrate to alerts:write.
  • TODO(api-write-scope-compat) in uptime preview, uptime assertion suggestions, Seer anomaly preview, and cron monitor endpoints: drop temporary org:write.
  • TODO(api-scope-personalization) on personal/member-state writes: introduce a narrow personal or member preference write scope, grant it to normal Sentry member RBAC roles, and move these endpoints off readonly scopes.
  • The temporary compat allowances and the regression tests that cover them all carry grepable TODO markers so the eventual cleanup is easy to find.

The OpenAPI scope diff is expected on this PR because the documented security requirements intentionally change.

Refs getsentry/getsentry#19897

@github-actions github-actions Bot added the Scope: Backend Automatically applied to PRs that change backend components label Apr 17, 2026
Comment thread src/sentry/seer/endpoints/organization_events_anomalies.py Outdated
Comment thread src/sentry/workflow_engine/endpoints/organization_detector_details.py Outdated
Comment thread src/sentry/seer/endpoints/organization_events_anomalies.py
Comment thread src/sentry/workflow_engine/endpoints/validators/utils.py Outdated
@github-actions

github-actions Bot commented Apr 17, 2026

Copy link
Copy Markdown
Contributor

Backend Test Failures

Failures on aedcdad in this run:

tests/sentry/workflow_engine/endpoints/test_organization_detector_index.py::OrganizationDetectorIndexPutTest::test_update_detectors_member_permission_denied_for_non_user_created_detectorlog
[gw1] linux -- Python 3.13.1 /home/runner/work/sentry/sentry/.venv/bin/python3
tests/sentry/workflow_engine/endpoints/test_organization_detector_index.py:1038: in test_update_detectors_member_permission_denied_for_non_user_created_detector
    self.get_error_response(
src/sentry/testutils/cases.py:663: in get_error_response
    assert_status_code(response, status_code)
src/sentry/testutils/asserts.py:46: in assert_status_code
    assert minimum <= response.status_code < maximum, response
E   AssertionError: <Response status_code=200, "application/json">
E   assert 403 <= 200
E    +  where 200 = <Response status_code=200, "application/json">.status_code
tests/sentry/workflow_engine/endpoints/test_organization_detector_index.py::OrganizationDetectorIndexPutTest::test_update_detectors_mixed_permissionslog
[gw1] linux -- Python 3.13.1 /home/runner/work/sentry/sentry/.venv/bin/python3
tests/sentry/workflow_engine/endpoints/test_organization_detector_index.py:1095: in test_update_detectors_mixed_permissions
    self.get_error_response(
src/sentry/testutils/cases.py:663: in get_error_response
    assert_status_code(response, status_code)
src/sentry/testutils/asserts.py:46: in assert_status_code
    assert minimum <= response.status_code < maximum, response
E   AssertionError: <Response status_code=200, "application/json">
E   assert 403 <= 200
E    +  where 200 = <Response status_code=200, "application/json">.status_code
tests/sentry/api/test_permissions.py::PublishedMutationScopeTest::test_published_mutation_endpoints_require_readonly_scope_noteslog
[gw1] linux -- Python 3.13.1 /home/runner/work/sentry/sentry/.venv/bin/python3
tests/sentry/api/test_permissions.py:314: in test_published_mutation_endpoints_require_readonly_scope_notes
    assert not missing_notes, "\n".join(missing_notes)
E   AssertionError: sentry.api.endpoints.prompts_activity.PromptsActivityEndpoint PUT accepts readonly scopes ['org:read'] via sentry.api.endpoints.prompts_activity.PromptsActivityPermission. Remove the readonly scopes or add readonly_mutation_scope_exceptions[method] with a justification note.
E   assert not ["sentry.api.endpoints.prompts_activity.PromptsActivityEndpoint PUT accepts readonly scopes ['org:read'] via sentry.ap...tyPermission. Remove the readonly scopes or add readonly_mutation_scope_exceptions[method] with a justification note."]

Comment thread src/sentry/api/bases/organization.py
@github-actions

Copy link
Copy Markdown
Contributor

Backend Test Failures

Failures on b35b2e6 in this run:

tests/sentry/api/test_permissions.py::PublishedMutationScopeTest::test_published_mutation_endpoints_require_readonly_scope_noteslog
[gw0] linux -- Python 3.13.1 /home/runner/work/sentry/sentry/.venv/bin/python3
tests/sentry/api/test_permissions.py:312: in test_published_mutation_endpoints_require_readonly_scope_notes
    assert not missing_notes, "\n".join(missing_notes)
E   AssertionError: sentry.issues.endpoints.project_user_issue.ProjectUserIssueEndpoint POST accepts readonly scopes ['event:read'] via sentry.issues.endpoints.project_user_issue.ProjectUserIssuePermission. Remove the readonly scopes or add readonly_mutation_scope_exceptions[method] with a justification note.
E     sentry.replays.endpoints.project_replay_details.ProjectReplayDetailsEndpoint DELETE accepts readonly scopes ['project:read'] via sentry.replays.endpoints.project_replay_details.ReplayDetailsPermission. Remove the readonly scopes or add readonly_mutation_scope_exceptions[method] with a justification note.
E   assert not ["sentry.issues.endpoints.project_user_issue.ProjectUserIssueEndpoint POST accepts readonly scopes ['event:read'] via ...lsPermission. Remove the readonly scopes or add readonly_mutation_scope_exceptions[method] with a justification note."]

@github-actions

Copy link
Copy Markdown
Contributor

Backend Test Failures

Failures on 9bfc6d6 in this run:

tests/sentry/api/test_permissions.py::PublishedMutationScopeTest::test_published_mutation_endpoints_require_readonly_scope_noteslog
[gw0] linux -- Python 3.13.1 /home/runner/work/sentry/sentry/.venv/bin/python3
tests/sentry/api/test_permissions.py:312: in test_published_mutation_endpoints_require_readonly_scope_notes
    assert not missing_notes, "\n".join(missing_notes)
E   AssertionError: sentry.issues.endpoints.project_user_issue.ProjectUserIssueEndpoint POST accepts readonly scopes ['event:read'] via sentry.issues.endpoints.project_user_issue.ProjectUserIssuePermission. Remove the readonly scopes or add readonly_mutation_scope_exceptions[method] with a justification note.
E     sentry.replays.endpoints.project_replay_details.ProjectReplayDetailsEndpoint DELETE accepts readonly scopes ['project:read'] via sentry.replays.endpoints.project_replay_details.ReplayDetailsPermission. Remove the readonly scopes or add readonly_mutation_scope_exceptions[method] with a justification note.
E   assert not ["sentry.issues.endpoints.project_user_issue.ProjectUserIssueEndpoint POST accepts readonly scopes ['event:read'] via ...lsPermission. Remove the readonly scopes or add readonly_mutation_scope_exceptions[method] with a justification note."]

Comment thread src/sentry/workflow_engine/endpoints/organization_workflow_index.py Outdated
Comment thread src/sentry/workflow_engine/endpoints/organization_workflow_index.py
dcramer and others added 11 commits April 20, 2026 09:54
Add a guardrail for published mutation endpoints that still accept readonly scopes.

Previously, write methods could keep readonly scopes in scope_map without any explicit marker in code, which made the policy debt hard to audit and easy to expand accidentally.

Require those endpoints to carry a readonly_mutation_scope_exceptions note, and fail the invariant test when a published mutation endpoint accepts readonly scopes without that note.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Tighten published mutation endpoints that already have an obvious existing write-capable scope.

Previously, several write methods accepted readonly scopes like org:read, project:read, or event:read even though they mutate server-side state.

Require the existing write scope for those surfaces instead. Keep session behavior intact, including team-scoped alert access, but stop exposing readonly token writes through these endpoints.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Drop the member-session surfaces that were not actually uncontroversial from this PR.

Dashboards, code mappings, repo path parsing, key transactions, and org auth token updates still rely on broader session behavior today, so tightening them here broke existing member flows. Revert those paths in this branch and keep the safe mutation tightening for incidents, alerts, user issue creation, data export, and replay summary.

Also keep replay delete in the project scope domain so delete remains consistent with the rest of the replay permission class.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Add an explicit owner for static/app/components/markdownTextArea.tsx so the branch clears the CODEOWNERS coverage check.

The file was missing ownership coverage entirely, which caused the fresh PR run to fail even though the API changes on this branch are backend-only.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Keep the replay permission maps aligned with the current repo style of listing implied scopes explicitly.

This does not change the effective permissions on these endpoints, but it removes a local inconsistency in this PR where replay endpoints relied on hierarchy expansion while nearby scope maps did not.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Replay delete is a public mutation, so it should not depend on a project read-style contract. Move the DELETE permission to event write/admin so member sessions keep working while token auth requires a real write scope.

Add direct endpoint tests covering the token contract: event:read is denied and event:write is allowed for replay delete.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Remove the incident permission change from the existing-write-scope pass. Incident mutation has an older member-access contract tied to project access, so tightening it cleanly needs a dedicated scope decision instead of being folded into the uncontroversial cleanup.

Also drop an unrelated CODEOWNERS hunk so this branch stays focused on backend mutation scope changes.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Keep replay summary POST on read-level replay scopes and mark it as an explicit readonly-mutation exception, since it derives summary data from existing replay/event data.

Document the preview endpoints that intentionally keep write-aligned permissions because they are part of alert and monitor authoring flows, even though they use POST helper endpoints.

Add replay summary token tests covering the read-scope contract.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Add endpoint-level regression tests for the scope changes on this branch so each permission update is covered directly.

Cover the write-scope transitions for data export, alert rule creation, project user issues, replay delete, detector updates, and the alert helper endpoints. This keeps the branch from relying on indirect role coverage alone and makes the replay summary readonly exception explicit next to direct token tests.

Refs getsentry/getsentry#19897
Co-Authored-By: OpenAI Codex <noreply@openai.com>
Use supported token auth for data export, give alert-rule token tests a valid member project setup, and return a real anomaly payload in the Seer regression test.

These tests were asserting the new permission contracts, but two of them were exercising invalid success paths and one was using an auth shape that the endpoint does not serialize correctly.

Co-Authored-By: Codex <noreply@openai.com>
Alert and monitor mutations now accept alerts:write at the endpoint level, but the nested project validators still required project:read. That let the request through permission checks and then failed token-based create and update flows with a 400 during validation.

Allow those serializers to validate project selection against the same write-capable scopes the endpoints already advertise, and add direct token regression tests for alert rule updates and monitor creation so the contract stays enforced.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
dcramer and others added 12 commits April 20, 2026 09:55
Limit org-scoped alert and detector mutations to either the target project's alerts:write access or an explicit endpoint opt-in for the older team-scoped fallback. This closes the cross-team mutation path on alert rule and detector details, and adds project-scoped checks for the other shared consumers that still relied on the broad organization permission.

Align replay deletion with the rest of the event delete surface by requiring event:admin instead of event:write. Add regression coverage for the scope changes and the cross-project authorization cases.

Co-Authored-By: Codex <noreply@openai.com>
Normalize RpcUserOrganizationContext to the underlying organization before resolving target projects for alert-rule and detector mutation permissions. Without that, the new project-scoped alerts:write path breaks during organization object-permission checks because those hooks run before convert_args swaps in the concrete organization.

Add team-admin session regressions that force the project-scoped alerts:write authorization path for alert rule and detector updates and deletes, including the workflow-engine fake-detector alert-rule route.

Co-Authored-By: Codex <noreply@openai.com>
Annotate the published mutation endpoints that intentionally accept readonly or member scopes so the permission-note audit can distinguish expected behavior from real scope gaps.

Tighten the newer alert, detector, workflow, uptime, and anomaly helpers to use alerts:write, but keep temporary org:write compatibility on the deprecated metric alert and cron monitor mutation paths. Add regression coverage for both the temporary compat and the stricter deny cases.

Refs getsentry/getsentry#19897
Co-Authored-By: Codex <codex@openai.com>
Unwrap RPC organization context before resolving anomaly preview projects and stop swallowing PermissionDenied from that lookup.

The earlier helper could hide project-resolution failures and fall back to the broader any-team alerts:write check. Keep invalid project_id parsing local, but preserve the existing permission path for real lookup errors and add regression coverage for both cases.

Refs getsentry/getsentry#19897
Co-Authored-By: Codex <codex@openai.com>
Centralize the RPC-aware organization id helper used by alert-rule and detector detail permission lookups.

Detector detail mutations also need the same missing-object fallback as alert rules so project-scoped alerts:write callers still reach convert_args() and return 404 for stale detector ids instead of failing permission checks early.

Refs getsentry/getsentry#19897
Co-Authored-By: Codex <codex@openai.com>
Remove duplicated handler-level permission checks and share the legacy alert project scope tuple between the alert-rule and monitor validators.

Keep anomaly preview project validation and the original system-detector edit contract intact, add the missing detector-index delete regression, and annotate the prompts activity readonly mutation note that the permission audit now enforces.

Refs getsentry/getsentry#19897
Co-Authored-By: Codex <codex@openai.com>
Restore legacy write-scope compatibility for published and experimental API mutations that this branch tightened, and add regression coverage for the token and session-role paths those endpoints rely on.

Preserve current client behavior while keeping the temporary compat shims clearly marked for later removal once callers have migrated.

Co-Authored-By: Codex <noreply@openai.com>
Keep the write-scope compatibility follow-up clean under our backend type checker.

Annotate the workflow permission override to match the base permission contract and align the workflow API-key test helpers with the DRF client stubs so the touched endpoint coverage stays type-safe.

Co-Authored-By: Codex <codex@openai.com>
Add the missing readonly mutation scope notes for the project user issue and replay delete endpoints so the published-mutation permission audit reflects their existing behavior.

While touching the alert permission code, extract the shared alert-mutation permission logic used by alert rules and detectors to keep future scope changes from drifting between the two classes.

Co-Authored-By: Codex <codex@openai.com>
Rename the shared alerting mutation permission to reflect the broader alerting surface and reuse it for workflow endpoints.

This removes the remaining duplicated workflow permission logic without changing behavior, since workflow endpoints do not define the extra project or team fallbacks handled by the shared implementation.

Co-Authored-By: Codex <codex@openai.com>
Restore the legacy org:* compatibility scopes for workflow detail mutations while keeping the shared alerting permission base in place.

Without this, API keys that still rely on org:write or org:admin could create or bulk update workflows through the index endpoint but would get 403 on detail PUT and DELETE. Add detail endpoint regression tests for both legacy and alerts:write API key auth paths.

Co-Authored-By: Codex <codex@openai.com>
Keep the detector details endpoint lint-clean after the rebase conflict resolution. Ruff removed the duplicate SnubaQuery import, so capture that hook-driven cleanup as a standalone follow-up instead of leaving the branch dirty.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
@dcramer dcramer force-pushed the dcramer/fix/api-write-scope-mutations-unstacked branch from 775b963 to c214378 Compare April 20, 2026 17:10

@saponifi3d saponifi3d 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.

did a quick pass on the alerts / detector / workflow endpoints that would all use the alerts:* scopes.

Comment on lines +100 to +103
legacy_alert_mutation_scope_map = {
"PUT": ("org:write", "org:admin"),
"DELETE": ("org:write", "org:admin"),
}

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.

these should also include alerts:write

Comment on lines +150 to +152
"POST": ("org:write", "org:admin"),
"PUT": ("org:write", "org:admin"),
"DELETE": ("org:write", "org:admin"),

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.

another alerts:write -- this allows non-org level owners to view, if the alerts override is checked.

legacy_alert_mutation_scope_map = {
"PUT": ("org:read", "org:write", "org:admin"),
"DELETE": ("org:read", "org:write", "org:admin"),
}

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.

alerts:write for both

Comment on lines +131 to +134
legacy_alert_mutation_scope_map = {
"PUT": ("org:read", "org:write", "org:admin"),
"DELETE": ("org:read", "org:write", "org:admin"),
}

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.

these should probably remove org:read and add alerts:write

# TODO(api-write-scope-compat): Remove legacy org:* support once uptime
# preview clients have migrated to alerts:write.
legacy_alert_mutation_scope_map = {
"POST": ("org:read", "org:write", "org:admin"),

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.

no org:read -- since these are alerts now, we should probably allow for alerts:write as well

Comment on lines +97 to +98
"POST": ("org:write", "org:admin"),
"PUT": ("org:write", "org:admin"),

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.

alerts:write for these

}
# TODO(api-write-scope-compat): Remove legacy org:write support once public
# metric alert clients have migrated to alerts:write.
legacy_alert_mutation_scope_map = {"POST": ("org:write",)}

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.

do these use the permission_classes? i wonder if we could just update the OrganizationAlertRulePermission to be: org:write, org:admin, and alerts:write to simplify the definitions.

Comment on lines +352 to +353
"DELETE": ("org:write",),
"PUT": ("org:write",),

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.

alerts:write

@dcramer

dcramer commented Apr 27, 2026

Copy link
Copy Markdown
Member Author

digging back into this one this week

Move alert authoring compatibility away from readonly scopes and align related preview, workflow, detector, and monitor endpoints on alerts:write. Keep only explicit legacy write/admin compatibility where needed.

Require event write/admin for user-created issues and event admin for replay deletion so event read and project read scopes no longer authorize these published mutations.

Refs getsentry/getsentry#19897

Co-Authored-By: OpenAI Codex <codex@openai.com>
Add a grepable TODO to personal preference and member-state mutation exceptions that still use readonly org or member scopes. These remain compatibility notes for this PR, but should move to a narrow personal write scope once the API scope taxonomy supports one.

Refs getsentry/getsentry#19897

Co-Authored-By: OpenAI Codex <codex@openai.com>

@cursor cursor Bot 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.

Cursor Bugbot has reviewed your changes and found 3 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 42fcd49. Configure here.

# TODO(api-write-scope-compat): Remove legacy org:write support for
# detector edits once public detector clients have migrated to alerts:write.
SYSTEM_CREATED_DETECTOR_EDIT_SCOPES = frozenset({"org:write", "alerts:write"})
USER_CREATED_DETECTOR_EDIT_SCOPES = frozenset({"org:write", "alerts:write"})

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.

System detector edit scopes silently widened

High Severity

The SYSTEM_CREATED_DETECTOR_EDIT_SCOPES and USER_CREATED_DETECTOR_EDIT_SCOPES are now identical. This allows alerts:write scope to edit system-created detectors (like Error or Performance types), which previously required org:write. This unintentionally expands edit permissions for team admins, removing a security boundary.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 42fcd49. Configure here.

return False

teams = Team.objects.filter(id__in=request.access.team_ids_with_membership)
return any(request.access.has_team_scope(team, scope) for team in teams)

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.

Team scope fallback ignores target project

Medium Severity

_has_any_team_scope returns True whenever the requester has alerts:write on any team they belong to, regardless of whether that team is associated with the targeted resource's project. When get_alert_mutation_projects returns None (e.g. multi-project AlertRule, missing kwargs, or unhandled methods like POST on detail endpoints), has_object_permission falls through to this team-wide check, granting the request even though body-level enforcement may not exist or may not match this codepath.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 42fcd49. Configure here.

Project.DoesNotExist,
Project.MultipleObjectsReturned,
):
return None

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.

AlertRule project resolution silently degrades on multi-project rules

Medium Severity

OrganizationAlertRuleEndpoint.get_alert_mutation_projects calls alert_rule.projects.get() and treats MultipleObjectsReturned (and Project.MultipleObjectsReturned) the same as "not found", returning None. Because None causes has_object_permission to fall through to the broad _has_any_team_scope fallback, alert rules legitimately associated with more than one project bypass project-level enforcement entirely instead of being checked against each of the rule's projects.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 42fcd49. Configure here.

@getsantry

getsantry Bot commented May 19, 2026

Copy link
Copy Markdown
Contributor

This issue has gone three weeks without activity. In another week, I will close it.

But! If you comment or otherwise update it, I will reset the clock, and if you remove the label Waiting for: Community, I will leave it alone ... forever!


"A weed is but an unloved flower." ― Ella Wheeler Wilcox 🥀

@getsantry getsantry Bot added the Stale label May 19, 2026
@getsantry getsantry Bot closed this May 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Scope: Backend Automatically applied to PRs that change backend components Stale

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants