Security Full Data Set - Backend
<!--IssueSummary start--> <details> <summary> Everyone can contribute. [Help move this issue forward](https://handbook.gitlab.com/handbook/marketing/developer-relations/contributor-success/community-contributors-workflows/#contributor-links) while earning points, leveling up and collecting rewards. </summary> - [Label this issue](https://contributors.gitlab.com/manage-issue?action=label&projectId=278964&issueIid=596809) - [Close this issue](https://contributors.gitlab.com/manage-issue?action=close&projectId=278964&issueIid=596809) </details> <!--IssueSummary end--> ## TL;DR Remove the `MAX_FINDINGS_COUNT = 25` cap in the MR security report comparer and expose the full set of `added`/`fixed` findings per report type through cursor-paginated GraphQL connections on `ComparedSecurityReport`, so the new MR Reports tab is unblocked from the 25-finding truncation. ## Scope ### In Scope - Add a feature flag for rollout - Remove `MAX_FINDINGS_COUNT = 25` cap so the cache contains all findings per report type - Convert `added` and `fixed` as independent cursor-paginated GraphQL connections on findingReportsComparer.report - Provide accurate `count` per connection (supersedes the FE "25+" workaround) - Validate Redis/ReactiveCaching payload behavior under realistic volumes ### Out of Scope - FE widget changes and new Reports-tab page - Severity-breakdown aggregation - Moving pagination to DB/finder level — Option B, requires reworking ReactiveCaching; tracked separately on work_items/18313 - Ingestion / storage changes for findings ## Phase WIP ## Problem Statement The MR security widget currently caps `added` and `fixed` findings at 25 each via `MAX_FINDINGS_COUNT`. The FE shows `"25+" `when the cap is hit, and users cannot see or triage findings beyond that. We need a paginated backend interface per report type, per diff side (`added`/`fixed`), without discarding the existing `ReactiveCaching` performance model. ## Proposed Approach **Option A - In-memory pagination over cached data** * Keep `ReactiveCaching` intact * Remove the `MAX_FINDINGS_COUNT` cap; all findings enter the cache * Convert `added` and `fixed` from `[FindingType]` to `FindingType.connection_type` * Paginate `added` and `fixed` independently; both map 1:1 to existing cached arrays * `count` comes for free via `CountableConnectionType` * Example graphql query: ```graphql query FindingReportsComparer( $fullPath: ID! $iid: String! $reportType: ComparableSecurityReportType! $scanMode: ScanModeEnum $first: Int $after: String ) { project(fullPath: $fullPath) { id mergeRequest(iid: $iid) { id findingReportsComparer(reportType: $reportType, scanMode: $scanMode) { status report { added(first: $first, after: $afterAdded) { nodes { ...FindingFields } pageInfo { hasNextPage endCursor } count } fixed(first: $first, after: $afterFixed) { nodes { ...FindingFields } pageInfo { hasNextPage endCursor } count } } } } } } ``` **Option B (DB-level pagination via reworked `ReactiveCaching`)** This option is too much scope/risk for Q2 2026 target at the moment. (see: https://gitlab.com/gitlab-org/gitlab/-/work_items/594166#note_3253916782) ## Feature Flag - [ ] Required - [ ] **Flag name:** `security_mr_report_full_finding_set` - [ ] **Default state:** Disabled - [ ] **Rollout plan:** (WIP - Create rollout issue) ## Success Criteria - [ ] Changes live behind feature flag - [ ] `MAX_FINDINGS_COUNT` is removed from `SecurityFindingsReportsComparer` and the comparer returns the complete undismissed added/fixed set. - [ ] `ComparedSecurityReport.added` and `ComparedSecurityReport.fixed` each accept independent `first`/`after` arguments and expose `nodes`, `pageInfo`, and `count`. - [ ] Paginated query returns stable, complete results on an MR with \>100 findings per report type (no duplicates, no missing findings across pages). - [ ] GraphQL reference docs regenerate with the new field shapes under the `experiment` marker. ## Dependencies - **Blocked by:** none known. - **Blocks:** ~frontend of https://gitlab.com/groups/gitlab-org/-/work_items/20760+ ## Implementation Notes _Example graphql query:_ ```graphql query FindingReportsComparer( $fullPath: ID! $iid: String! $reportType: ComparableSecurityReportType! $scanMode: ScanModeEnum $first: Int $after: String ) { project(fullPath: $fullPath) { id mergeRequest(iid: $iid) { id findingReportsComparer(reportType: $reportType, scanMode: $scanMode) { status report { added(first: $first, after: $afterAdded) { nodes { ...FindingFields } pageInfo { hasNextPage endCursor } count } fixed(first: $first, after: $afterFixed) { nodes { ...FindingFields } pageInfo { hasNextPage endCursor } count } } } } } } ``` ## Open Questions | Question | Owner | Status | |----------|-------|--------| | | | | | | | | | | | | | | | | ## Resources - **Parent epic:** https://gitlab.com/groups/gitlab-org/-/work_items/20760+s - **Spike:** https://gitlab.com/gitlab-org/gitlab/-/work_items/594166+ * **Backend investigation :thread:** : https://gitlab.com/gitlab-org/gitlab/-/work_items/594166#note_3253916782+ - **Main Project epic:** https://gitlab.com/groups/gitlab-org/-/work_items/18313+
issue