Anti-flipping safeguard for vulnerability management policies

Summary

When a vulnerability appears in scan results inconsistently, it can continually transition between detected and resolved states. This can cause these problems:

  1. Unclear vulnerability status
  2. Continual growth of notes and vulnerability_state_transitions records, which will eventually cause performance and storage problems

We should implement "anti-flipping" safeguards to mitigate this problem. After a vulnerability has automatically transitioned between detected and resolved a certain number of times, we should flag that vulnerability as problematic (when I worked at WhiteHat Security, such vulnerabilities were called "flappers"). If a vulnerability has been flagged as problematic then it should be ignored by any further automatic status changes, and should be marked by an indicator in the UI. This will allow someone to manually review, set the correct status, and correct the projects pipeline configuration if necessary.

Implementation

Phase 1
  • Create a new table: vulnerability_flip_guards
  • Add new model: Vulnerabilities::FlipGuard
  • Add specs for all the above
Phase 2
  • Introduce a new feature flag: vulnerability_anti_flip_safeguard which is (default: disabled)
  • Add has_one :flip_guard association to the Vulnerability model
  • Update AutoResolveService to check and track guards
  • Update MarkResolvedAsDetected to check and track guards
  • Add logging for blocked transitions
  • Add specs for all the above
Guard logic details
  • The guard should trigger after 5 automatic transitions within a 30-day window.
  • Once a vulnerability hits the threshold, we will set the state to needs_triage
  • All the further automatic transitions should be blocked, until the vulnerability has been manually reviewed.
Schema proposal:
CREATE TABLE vulnerability_flip_guards (
  id bigserial PRIMARY KEY,
  vulnerability_id bigint NOT NULL,
  project_id bigint NOT NULL,

  automated_transition_count smallint DEFAULT 1,
  first_automatic_transition_at timestamp with time zone NOT NULL,  # first time the vulnerability's state was changed
  last_automatic_transition_at timestamp with time zone NOT NULL, # most recent state change
  is_guarded boolean DEFAULT false NOT NULL,

  created_at timestamp with time zone NOT NULL,
  updated_at timestamp with time zone NOT NULL
);
Edited by Charlie Kroon