Vulnerability Management Policy Auto-resolve Problem: Vulnerabilities Incorrectly Reverting to "DETECTED" State

Vulnerability Management Policy Auto-resolve Problem: Vulnerabilities Incorrectly Reverting to "DETECTED" State

Summary

A vulnerability management policy configured to auto-resolve vulnerabilities is experiencing unexpected behavior. Vulnerabilities that are properly auto-resolved to "RESOLVED" state are unexpectedly reverting back to "DETECTED" state with null author and null comment. This particularly affects vulnerabilities that were previously manually dismissed as false positives, creating unnecessary noise in vulnerability reports.

Steps to reproduce

  1. Manually dismiss a vulnerability as a false positive
  2. Configure a vulnerability management policy to auto-resolve vulnerabilities that are no longer detected
  3. Run a security scan where the dismissed vulnerability is not detected
  4. Verify the vulnerability is auto-resolved (status: "RESOLVED")
  5. Experience a failed Gemnasium scan pipeline
  6. Run another security scan
  7. Observe that the vulnerability has reverted to "DETECTED" state with null author and null comment in the state transition history

Example Project

This was reported by a customer in https://gitlab.zendesk.com/agent/tickets/619413 (internal use only)

What is the current bug behavior?

Our customer reported that 1,000 vulnerabilities that were previously auto-resolved to "RESOLVED" state have reverted back to "DETECTED" state. These state transitions show a null author and null comment. The issue particularly affects vulnerabilities that were previously manually dismissed as false positives, forcing security teams to re-triage vulnerabilities they had already addressed.

What is the expected correct behavior?

Vulnerabilities that are auto-resolved by the policy should remain in the "RESOLVED" state unless they are actually detected again in a subsequent scan. Vulnerabilities that were previously manually dismissed as false positives should either remain dismissed or at least maintain proper attribution in state transitions, so they can be automatically returned to their previous state.

Relevant logs and/or screenshots

GraphQL data shows this pattern:

Vulnerability properly auto-resolved with comment "Auto-resolved by the vulnerability management policy..."
One day later: Vulnerability unexpectedly reverted to "DETECTED" with null author and null comment

Possible fixes

The issue appears to be in several files:

  1. ee/app/services/security/ingestion/tasks/ingest_vulnerabilities/mark_resolved_as_detected.rb - Should include proper author attribution and check previous vulnerability state
def update_vulnerability_records
  ::Vulnerability.id_in(redetected_vulnerability_ids)
    .update_all(
      state: :detected,
      resolved_at: nil,
      resolved_by_id: nil
    )
end

def state_transitions
  current_time = Time.now.utc

  redetected_vulnerability_ids.map do |vulnerability_id|
    ::Vulnerabilities::StateTransition.new(
      vulnerability_id: vulnerability_id,
      from_state: :resolved,
      to_state: :detected,
      created_at: current_time,
      updated_at: current_time
      # Missing author_id and comment
    )
  end
end
  1. ee/app/services/vulnerabilities/auto_resolve_service.rb - Should exclude vulnerabilities that were manually dismissed from auto-resolution

  2. ee/app/services/vulnerabilities/revert_to_detected_service.rb - Contains REVERT_PARAMS which sets all attribution fields to nil

Fix implementation:

  1. Modify the update_vulnerability_records method to check if a vulnerability was previously in a "DISMISSED" state before auto-resolution
  2. Add proper author attribution to state transitions
  3. Create a database migration to fix already affected vulnerabilities

Here's a diagram showing the issue flow:

flowchart TD
    A[Vulnerability in 'DISMISSED' state] -->|Pipeline scan runs| B[Scan doesn't detect vulnerability]
    B --> C["Security::Ingestion::MarkAsResolvedService (mark_as_resolved_service.rb)\nmarks as no longer detected"]
    C --> D["Vulnerabilities::AutoResolveService (auto_resolve_service.rb)\nchanges state to 'RESOLVED'\nwith auto_resolved=true"]
    D --> E[Vulnerability now in 'RESOLVED' state\nwith security policy bot attribution]
    
    F[Failed pipeline scan occurs] -->|Subsequent scan runs| G[System doesn't recognize\nproper resolution context]
    G --> H["Security::Ingestion::Tasks::IngestVulnerabilities\n::MarkResolvedAsDetected (mark_resolved_as_detected.rb)\nCreates transition without author_id"]
    H --> I[State transition created with\nnull author and null comment]
    I --> J[Vulnerability incorrectly reverted to 'DETECTED' state]
    
    subgraph "Bug: Missing validation in state transition"
        H
        I["Code issue: State transitions missing author\n(mark_resolved_as_detected.rb line ~30)"]
        J["Code issue: Unconditional update_all to DETECTED\n(mark_resolved_as_detected.rb line ~20)"]
    end
    
    K[Expected behavior] -->|Dismissed vulnerabilities| L[Should remain in dismissed state\neven after auto-resolve policy runs]
    
    style H fill:#f96,stroke:#333,stroke-width:2px
    style I fill:#f96,stroke:#333,stroke-width:2px
    style J fill:#f96,stroke:#333,stroke-width:2px