ref(saved-queries): align list endpoint access checks with detail#115379
Merged
Conversation
The Discover and Explore saved-query list endpoints filtered results only by organization, while their corresponding detail endpoints already ran an object-level project-access check via `check_object_permissions`. Apply the same project-access logic at the queryset level in the list handlers so the two endpoints stay consistent. Behaviour is unchanged for actors that can already see every query in the org (Open Membership, Managers/Owners) and for the creator of an unprojected query. Co-Authored-By: Claude <noreply@anthropic.com>
…of project access Prebuilt Explore saved queries are product-level content (no user data, no project scope), but the existing access logic — both `has_object_permission` on the detail endpoint and the new list-level filter — treated them as "no-projects" queries authored by no one, which dropped them for any member in a closed-membership org who is not a Manager/Owner. Allow rows with `prebuilt_id IS NOT NULL` through both checks so prebuilts behave the same for every org member. Co-Authored-By: Claude <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 8b7151d. Configure here.
… exclude too `has_object_permission` short-circuits prebuilt explore queries before any project check, but the list filter's "exclude queries with an inaccessible project" step still applied to them — so a prebuilt with a project the actor couldn't access would 200 on detail and disappear from the list, breaking the mirroring contract between the two checks. Centralise the prebuilt exemption in a single `is_prebuilt` predicate and apply it to both the exclude and the include step. Add a regression test that pins this contract end-to-end. Co-Authored-By: Claude <noreply@anthropic.com>
`PREBUILT_SAVED_QUERIES[0]["prebuilt_id"]` is typed `object`, which mypy rejected when passed to `ExploreSavedQuery.objects.create(prebuilt_id=..., prebuilt_version=...)`. Switch to literals (1, 1) with a comment explaining the coupling to the canonical prebuilt list. If anyone bumps the canonical version of the first entry, the test fails on the name assertion and the literal can be bumped alongside. Co-Authored-By: Claude <noreply@anthropic.com>
geoffg-sentry
approved these changes
May 19, 2026
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.

Apply the same project-access logic the Discover and Explore detail saved-query endpoints already enforce to their corresponding list endpoints, so both report the same view of the org for any given actor.
The detail endpoints have been calling
check_object_permissionssince #72159 / #78830 — the list handlers were not updated alongside, and when the Explore endpoints landed (#86578, #86888) they inherited the same shape. This PR closes the gap on both Discover and Explore in one place.Implementation: a small
filter_to_accessible_*_queries(request, queryset)helper in each app'sendpoints/bases.pythat mirrors the existinghas_object_permissionlogic at the queryset level (short-circuits for Open Membership /org:write, thenExists()subqueries on the through-tables for the project-scoped and unprojected cases). The list endpoints call it once after building the base queryset; theall=1branch on Discover is covered by the same filter.Behaviour is unchanged for any actor who could already see every saved query in the org.
Fixes EXP-942