Scope remaining abuse report reads and writes to one organization

What does this MR do and why?

Follow-ups found while scoping AbuseReportsFinder. The first one is a bug, not a cleanup.

Cross-organization write (the important one)

Admin::AbuseReports::ModerateUserService#close_similar_open_reports calls:

abuse_report.similar_open_reports_for_user.update_all(status: 'closed')

similar_open_reports_for_user went through user.abuse_reports. A report's organization follows its reporter, not the reported user, so the same user can be reported in several organizations — which means an admin moderating a user silently closed that user's open reports in every other organization as well.

past_closed_reports_for_user had the matching read leak: Admin::AbuseReportDetailsEntity exposes both methods, so the admin details page showed another organization's report metadata.

Both are now scoped to the report's own organization_id, which is available on the record and needs no request context.

Reverting this fix fails the new spec in five separate moderation paths — ban, block, delete, trust and close-only — so the exposure was not limited to one action.

Sidebar pill count

Sidebars::Admin::Menus::AbuseReportsMenu#pill_count counted AbuseReport.open across every organization, so it disagreed with the list it links to once that list became organization-scoped.

lib/sidebars is not excluded from Gitlab/AvoidCurrentOrganization, so the organization is carried on Sidebars::Context instead of read from Current. The Explore panel already passes current_organization this way. Both places that build an admin panel context are updated — SidebarsHelper#super_sidebar_nav_panel and the gitlab:nav:dump_structure Rake task — because Sidebars::Context only defines readers for the keywords it is given, so missing one would raise NoMethodError.

GraphQL abuseReport query

Resolvers::AbuseReportResolver looked reports up with an unscoped find_by_id, returning a report from any organization. app/graphql is excluded from the cop, so Current.organization is read directly here rather than threaded through the resolver.

Database

No migration and no schema changes. Three existing reads gain a WHERE organization_id = ? predicate, and one update_all is narrowed by the same predicate. abuse_reports is table_size: small and organization_id is already indexed.

1. AbuseReport#past_closed_reports_for_user
SELECT abuse_reports.*
FROM abuse_reports
WHERE abuse_reports.user_id = :user_id
  AND abuse_reports.organization_id = :organization_id
  AND abuse_reports.status = 2
  AND abuse_reports.id != :report_id;

Query plan

2. AbuseReport#similar_open_reports_for_user
SELECT abuse_reports.*
FROM abuse_reports
WHERE abuse_reports.user_id = :user_id
  AND abuse_reports.organization_id = :organization_id
  AND abuse_reports.status = 1
  AND abuse_reports.category = :category
  AND abuse_reports.id != :report_id;

Query plan

3. update_all in ModerateUserService#close_similar_open_reports
UPDATE abuse_reports
SET status = 2
WHERE abuse_reports.user_id = :user_id
  AND abuse_reports.organization_id = :organization_id
  AND abuse_reports.status = 1
  AND abuse_reports.category = :category
  AND abuse_reports.id != :report_id;

Query plan

This is the query the cross-organization fix narrows: before this MR the organization_id predicate was absent, so moderating a user closed that user's open reports in every other organization.

4. Sidebars::Admin::Menus::AbuseReportsMenu#pill_count
SELECT COUNT(*)
FROM abuse_reports
WHERE abuse_reports.organization_id = :organization_id
  AND abuse_reports.status = 1;

Query plan

5. Resolvers::AbuseReportResolver
SELECT abuse_reports.*
FROM abuse_reports
WHERE abuse_reports.organization_id = :organization_id
  AND abuse_reports.id = :report_id
LIMIT 1;

Query plan

References

Closes gitlab-com/gl-infra/tenant-scale/cells-infrastructure/team#780 (closed)

Depends on the parent MR for the AbuseReport.in_organization scope.

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Edited by Eugie Limpin

Merge request reports

Loading
Loading