Dismissed vulnerabilities without a dismissed_at date are possible

Summary

Related issue: #217600 (comment 422892249)

There are 9761 records in the database with state 2 (dismissed) where the dismissed_at attribute is null.

Steps to reproduce

How these records are dismissed without having a dismissed_at value?

  1. Vulnerability is dismissed, dismissal feedback is created in database for related finding.
  2. New vulnerability with finding is created in StoreReportService for new pipeline.
  3. New vulnerability with finding has same project_id, category, project_fingerprint as the one with dismissal feedback.
  4. New vulnerability is already dismissed, but no information about dismissed_at is saved (we are using fallback in https://gitlab.com/gitlab-org/gitlab/blob/1dcc4b0771fcc8d64faf508685a3ea8a204fc286/ee/app/models/ee/vulnerability.rb#L138) so it is not visible for the end user.

Example Project

https://gitlab.com/gitlab-org/gitlab/-/security/vulnerabilities/2431960

What is the current bug behavior?

Symptom 1: it's possible to dismiss a finding using the "pipeline security tab" without affecting the state of the existing vulnerability.

Symptom 2: dismissed_at is null for record in state 2 (dismissed).

EXPLAIN ANALYZE SELECT * FROM vulnerabilities WHERE state = 2 AND dismissed_at IS NULL
 Seq Scan on public.vulnerabilities  (cost=0.00..438562.84 rows=65243 width=280) (actual time=3.783..6154.420 rows=9761 loops=1)
   Filter: ((vulnerabilities.dismissed_at IS NULL) AND (vulnerabilities.state = 2))
   Rows Removed by Filter: 3664306
   Buffers: shared read=98162 dirtied=11625 written=7259
   I/O Timings: read=4417.535 write=206.369

What is the expected correct behavior?

Every dismissed vulnerability must have a dismissed_at date.

Relevant logs and/or screenshots

Output of checks

Possible fixes

See #217600 (comment 424693901)

Implementation plan

  • backend add to StoreReportService logic to populate dismissed_at and dismissed_by_id values for vulnerability when dismissal feedback is found,
  • backend implement the logic to dismiss the related vulnerability record when a finding gets dismissed via "pipeline security tab",
  • backend implement background migration to populate dismissed_at and dismissed_by_id values for all vulnerabilities that have vulnerability dismissal feedback (take created_at from dismissal feedback as value for dismissed_at),
  • backend remove the EE::Vulnerability::dismissed_by_id method as the very last thing
Edited by Mehmet Emin INAC