fix(issues): Enforce project access on event ID lookup endpoint#115784
Merged
Conversation
The eventids endpoint inherits from OrganizationEndpoint, whose permission class only checks organization membership. The handler then iterated every project in the organization, bypassing the project-level team-access check that sibling endpoints (e.g. shortid lookup) apply. An org member who is not on a project's owning team could resolve any event ID from that project, returning the full event payload — including the case of a team member whose access was revoked but who retained event IDs from prior legitimate access. Route the project lookup through get_projects(..., include_all_accessible =True) so the eventstore search is scoped to projects the caller can access. include_all_accessible preserves the previous behaviour for org owners, managers, and superusers (their global scope grants access to every org project) while applying has_project_access for everyone else. Return 404 when the caller has access to no projects in the org so the empty project_ids list does not reach Snuba as an UnqualifiedQueryError.
geoffg-sentry
approved these changes
May 19, 2026
cvxluo
approved these changes
May 19, 2026
|
|
||
| def test_access_non_member_project(self) -> None: | ||
| # Org member who is not on the project's owning team must not be able | ||
| # to resolve event IDs from that project, even with Open Membership off. |
Contributor
There was a problem hiding this comment.
might be worth adding a test that with open membership on, you can access projects you don't access.
Add a regression test verifying that with Open Membership on (the default), an org member who is not on the project's owning team can still resolve event IDs from that project. This pins down the intended product behaviour so a future tightening of the endpoint does not accidentally break it.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The
EventIdLookupEndpoint(GET /api/0/organizations/{org}/eventids/{event_id}/) inherits fromOrganizationEndpoint, whose permission class only checks organization membership. The handler then queriedProject.objects.filter(organization=organization)directly — every project in the org — and used that as the search scope for the eventstore lookup. As a result, any org member could resolve an event ID from a project whose owning team they were not on, including the case of a team member whose access was revoked but who retained event IDs collected during prior legitimate access. The siblingShortIdLookupEndpointalready enforces project-level access viaGroupPermission→ProjectPermission.has_object_permission; the eventid endpoint did not.This switches the project lookup to
self.get_projects(request, organization, include_all_accessible=True).include_all_accessiblekeeps the previous behaviour for org owners, managers, staff, and superusers (their global scope returnsTruefromhas_project_accessfor every org project) while applyinghas_project_accessper project for everyone else. An earlyResourceDoesNotExistis raised when the caller has access to no projects in the org, since the previous handler would otherwise pass an emptyproject_idslist to Snuba and surface as anUnqualifiedQueryError(500).Adds a regression test mirroring
test_access_non_member_projecton the shortid endpoint: org member,allow_joinleave=False, no team memberships, expects 404 on event-id lookup.Fixes VULN-52