Fix vulnerability report to show latest non-closed MR

What does this MR do?

The vulnerability report (list) and vulnerability details page disagreed about which merge request to show for a vulnerability that has multiple linked MRs:

  • The details page filters mergeRequestLinks for non-closed and picks the latest.
  • The report displayed vulnerability.mergeRequest, the GraphQL singular field, which returns the oldest linked MR — even if closed.

This MR makes the report match the details page.

Approach

  • Extract the rule (m.state !== 'closed', take the last) into a shared util latestOpenMergeRequest in ee/app/assets/javascripts/vue_shared/security_reports/utils.js — the single source of truth.
  • Have the details page (header.vue, footer.vue) consume the util. Behavior unchanged; this just deduplicates the inline filter.
  • Switch the vulnerability report's GraphQL fragment from the singular mergeRequest { ... } to mergeRequests { nodes { ... } } so the report has the data needed to apply the same rule.
  • vulnerability_list.vue derives the displayed MR via the util.

Steps to reproduce

The natural product path to hit this bug is to enable dependency_management_auto_remediation and let it create multiple auto-remediation MRs for a single vulnerability. That requires a fully-configured dependency-scanning pipeline, so for local verification we seed the fixture directly via Rails console instead.

  1. Import https://gitlab.com/gitlab-examples/security/security-reports/ into your local GDK and run its pipeline so that vulnerabilities get ingested. Then pick any of the resulting vulnerabilities.

  2. Open Rails console (gdk rails console) and paste (replacing '<your/project/path>' with the path you imported the project to):

    project = Project.find_by_full_path('<your/project/path>') or abort 'Project not found.'
    user    = project.first_owner || User.find_by_username('root') || User.admins.first
    vuln    = project.vulnerabilities.first
    abort 'No vulnerabilities on this project.' if vuln.nil?
    
    base = project.default_branch
    vuln.merge_request_links.destroy_all
    
    %w[mr-old-branch-600450 mr-new-branch-600450].each do |b|
      next if project.repository.branch_exists?(b)
      result = Branches::CreateService.new(project, user).execute(b, "refs/heads/#{base}")
      raise "Could not create #{b}: #{result[:message]}" if result[:status] == :error
    end
    
    build_mr = ->(branch, title) do
      result = MergeRequests::CreateService.new(
        project: project, current_user: user,
        params: { source_branch: branch, target_branch: base, title: title }
      ).execute
      result.is_a?(Hash) ? result[:merge_request] : result
    end
    
    old_mr = build_mr.call('mr-old-branch-600450', 'Old auto-remediation (closed) — #600450 repro')
    new_mr = build_mr.call('mr-new-branch-600450', 'New auto-remediation (open) — #600450 repro')
    raise "old_mr failed: #{old_mr.errors.full_messages}" unless old_mr.persisted?
    raise "new_mr failed: #{new_mr.errors.full_messages}" unless new_mr.persisted?
    
    old_mr.close! unless old_mr.closed?
    
    Vulnerabilities::MergeRequestLink.create!(vulnerability: vuln, merge_request: old_mr, created_at: 2.days.ago)
    Vulnerabilities::MergeRequestLink.create!(vulnerability: vuln, merge_request: new_mr, created_at: Time.current)
    
    puts "Vulnerability URL: #{Gitlab::Routing.url_helpers.project_security_vulnerability_url(project, vuln)}"

    Result: one closed MR (!1, older) and one open MR (!2, newer) linked to the same vulnerability.

  3. Before this MR (on master):

    • Open the project's vulnerability report. The MR badge on that vulnerability's row points to !1 (closed). ← bug.
    • Open the vulnerability details page. The "Related merge request" card shows !2 (open). ← inconsistency.
  4. After this MR (on this branch): both surfaces show !2.

How to validate locally

Run the affected Jest specs:

yarn jest ee/spec/frontend/vue_shared/security_reports/utils_spec.js \
          ee/spec/frontend/vulnerabilities/header_spec.js \
          ee/spec/frontend/vulnerabilities/footer_spec.js \
          ee/spec/frontend/security_dashboard/components/shared/vulnerability_report/vulnerability_list_spec.js \
          ee/spec/frontend/security_dashboard/components/shared/merge_request_badge_spec.js

Then walk through the reproduction steps above and confirm the report's badge follows the details page.

Screenshots

MR acceptance checklist

  • Frontend test coverage for the new util and the regression scenario.
  • Manual verification against the reproduction steps above.
  • Screenshots of the vulnerability report MR badge before/after.

Related to #600450

Edited by Savas Vedova

Merge request reports

Loading