[BE] Bulk status change for findings in the pipeline security tab for "resolved" and "confirmed" states

Problem

The pipeline Security tab only supports changing a finding's status to Dismissed or back to Needs triage. The Vulnerability Report supports all four states. This gap dates from the migration of the page to GraphQL, where the requirement was feature parity only.

This particularly affects secret detection (see thread). Secret detection findings are deliberately never auto-resolved. Even once the secret is removed from the branch, the exposed token still needs rotating, so GitLab requires active human triage instead of treating it as fixed. The intended remedy is for someone to manually mark it Resolved, but the pipeline Security tab offers no way to do that, so these findings sit at "Needs triage" indefinitely.

Proposal

Add Confirmed and Resolved as status options in the pipeline Security tab, for both single findings and bulk selection.

Implementation

Backend

Add two GraphQL mutations, for confirming and resolving a security finding, alongside the existing dismiss and revert-to-detected ones, plus a service to back them. Both take the finding's UUID and an optional comment, and require the same permission as dismissal.

A finding has no state of its own, so triaging one means finding or creating the underlying vulnerability record and setting it to the target state. The shared service that already does this is state-agnostic, so it handles both creating a vulnerability directly in the target state and transitioning an existing one, including recording the state transition, author, comment and system note. The new path is simpler than the dismiss service, since dismissal is the only state carrying legacy feedback records.

This would make four state-change paths on the finding side with no common abstraction. Dismiss and severity override each hand-roll the flow, and revert-to-detected has no service at all. Worth extracting a shared base rather than adding two more one-offs.

No changes needed to the finding model or its GraphQL type: a finding's status is already read through from its vulnerability, so the tab displays and filters the new statuses automatically. Verified against a finding whose vulnerability was confirmed elsewhere, which already renders "Status: Confirmed" in the finding modal.

Frontend

The bulk status-change component is already shared between the Vulnerability Report and the pipeline Security tab, and decides which statuses to offer on the pipeline tab by checking whether a finding-level mutation exists for each one. So the work is adding the two mutation documents, registering them against the confirmed and resolved statuses, and extending the specs. No component logic changes.

The finding modal needs no changes, since it renders whatever status comes back. Adding Confirm/Resolve as actions within the modal is a larger UI change and should be a separate issue.

Notes

  • Resolved will persist for genuinely fixed findings. Ingestion reverts a resolved vulnerability to detected only when the scanner reports it again in a new scan. A fixed secret isn't in the report, so the reversion never fires. Confirmed and dismissed are never overwritten by ingestion.
  • Expected exception: if a finding is resolved from a non-default-branch pipeline and that code later reaches the default branch, ingestion will promote the vulnerability and revert it to detected. This is correct behavior, since it isn't fixed there, but worth documenting so it isn't later reported as a bug.
  • The who/when metadata for a state change is currently only exposed for dismissed findings, so confirmed/resolved won't show it. This is consistent with how detected behaves today and acceptable for now. The comment is still stored and visible on the vulnerability details page, so it can be revisited without a migration.
  • Should Resolve require a comment? Dismiss requires a reason and comment; Confirm and Resolve on the Vulnerability Report require neither.
Edited by 🤖 GitLab Bot 🤖