Reject scope=all on root issues REST API endpoints

What does this MR do and why?

The root GET /issues and GET /issues_statistics REST API endpoints allow scope=all without authentication. With no project or group to bound the query, IssuesFinder resolves the accessible projects to every visible project on the instance (Project.public_or_visible_to_user), so the query scans issues across all public projects and runs an unbounded COUNT for pagination. On a large instance such as GitLab.com this statement-times-out and returns 500 Internal Server Error, for both anonymous and authenticated requests.

This MR rejects scope=all on the two root endpoints with a clear 400 Bad Request instead of running the unbounded query. scope=all remains supported on the group and project issues endpoints, where the query is bounded by the parent.

The restriction applies to GitLab.com only. It is gated behind the restrict_global_issues_scope_all feature flag (gitlab_com_derisk, disabled by default), which is the rollout control and the kill switch on GitLab.com. GitLab Self-Managed and GitLab Dedicated keep the existing behavior whatever the flag says. The endpoint checks Gitlab.com? directly, with a rubocop disable marked as a temporary guard that goes away with the flag.

Closes #478744

Behavior

Request Flag off (default, today) Flag on (GitLab.com only)
GET /issues?scope=all 500 at scale / 200 small instance 400 with guidance message
GET /issues_statistics?scope=all 500 at scale / 200 400
GET /issues (no scope) 401 if anonymous unchanged
scope=created_by_me / assigned_to_me unchanged unchanged
/groups/:id/issues, /projects/:id/issues with scope=all unchanged unchanged
Any of the above on Self-Managed or Dedicated unchanged unchanged

Breaking change

On GitLab.com, when the flag is enabled, a previously documented scope value returns 400 on the root endpoints. This is a deliberate correction of behavior that currently returns 500. Callers needing a global list should use created_by_me/assigned_to_me or the group/project endpoints. Self-Managed and Dedicated are not affected.

Feature flag rollout

Rollout issue: #634324

  • Enable for gitlab-org first as a smoke test. The actor is current_user, so anonymous requests (nil actor) are only covered once the flag is fully enabled.
  • Enable on GitLab.com for everyone via ChatOps and confirm the endpoint 500 rate / SLO recovers.
  • Remove the flag together with the Gitlab.com? guard once stable, or decide at that point to keep the .com check as a SaaS-only behavior.

Testing

  • spec/requests/api/issues/issues_spec.rb, GitLab.com with the flag on: anonymous and authenticated scope=all on /issues and /issues_statistics return 400, assigned_to_me still returns 200, no scope still returns 401 for anonymous.
  • Same spec, GitLab.com with the flag off: scope=all returns 200.
  • Same spec, not GitLab.com with the flag on: scope=all returns 200.
  • spec/requests/api/issues/get_project_issues_spec.rb: the project endpoint with scope=all still returns 200 on GitLab.com with the flag on.

🤖 Generated with Claude Code

Edited by Alexandru Croitor

Merge request reports

Loading
Loading