Validate flow trigger filter against allowlist

What does this MR do and why?

This MR addresses #601346 (closed)

As part of the triggers work, we introduced actions in the workers that are passed as filter_data, which FilterEvaluator then compares against the trigger's stored filter rules (set on the frontend).

There was a gap where a trigger saved with an invalid filter value would save cleanly but silently never fire. This MR validates the flow trigger filter against an allowlist, so an invalid filter action now fails with an explicit error rather than failing silently.

Verification (rails console)

  1. Start rails console: gdk rails c

# Check the flag is off by default
  project = Project.first
  Feature.enabled?(:validate_flow_trigger_filter_actions, project)
  # => false

  # Build a trigger with an invalid action — should be valid (flag off)
  trigger = Ai::FlowTrigger.new(
    project: project,
    event_types: [Ai::FlowTrigger::EVENT_TYPES[:merge_request]],
    filter: {
      'merge_request' => {
        'match' => 'all',
        'rules' => [{ 'field' => 'action', 'operator' => 'eq', 'value' => 'bad_value' }]
      }
    }
  )
  trigger.valid?
  trigger.errors[:filter]
  # => [] (no filter errors)

  # Enable the flag
  Feature.enable(:validate_flow_trigger_filter_actions)

  # Now the same trigger should fail validation
  trigger.valid?
  trigger.errors[:filter]
  # => ["contains invalid action values for merge_request: bad_value. Allowed values are: approved"]

 # --- Valid cases ---

 # merge_request with 'approved' action
  t = Ai::FlowTrigger.new(
    project: project,
    event_types: [Ai::FlowTrigger::EVENT_TYPES[:merge_request]],
    filter: { 'merge_request' => { 'match' => 'all', 'rules' => [{ 'field' => 'action', 'operator' => 'eq', 'value' => 'approved' }] } }
  )
  t.valid?
  puts "Valid approved action: #{t.errors[:filter].empty?}" # expect true

  # work_item with 'created' action
  t = Ai::FlowTrigger.new(
    project: project,
    event_types: [Ai::FlowTrigger::EVENT_TYPES[:work_item]],
    filter: { 'work_item' => { 'match' => 'all', 'rules' => [{ 'field' => 'action', 'operator' => 'eq', 'value' => 'created' }] } }
  )
  t.valid?
  puts "Valid created action: #{t.errors[:filter].empty?}" # expect true

  # --- Invalid cases ---

   # merge_request Typo: 'approveed'
  t = Ai::FlowTrigger.new(
    project: project,
    event_types: [Ai::FlowTrigger::EVENT_TYPES[:merge_request]],
    filter: { 'merge_request' => { 'match' => 'all', 'rules' => [{ 'field' => 'action', 'operator' => 'eq', 'value' => 'approveed' }] } }
  )
  t.valid?
  puts "Typo rejected: #{t.errors[:filter].join}" # expect error message

  # Invalid work_item action
  t = Ai::FlowTrigger.new(
    project: project,
    event_types: [Ai::FlowTrigger::EVENT_TYPES[:work_item]],
    filter: { 'work_item' => { 'match' => 'all', 'rules' => [{ 'field' => 'action', 'operator' => 'in', 'value' => %w[created invalid_action] }] } }
  )
  t.valid?
  puts "invalid_action in array rejected: #{t.errors[:filter].join}" # expect error message

  # Invalid action in nested group
  t = Ai::FlowTrigger.new(
    project: project,
    event_types: [Ai::FlowTrigger::EVENT_TYPES[:merge_request]],
    filter: { 'merge_request' => { 'match' => 'all', 'rules' => [{ 'type' => 'group', 'match' => 'any', 'rules' => [{ 'field' => 'action', 'operator' => 'eq', 'value' => 'invalid_action' }] }] } }
  )
  t.valid?
  puts "Nested group rejected: #{t.errors[:filter].join}" # expect error message

  # --- No-op cases (should not interfere) ---
  
  # Empty filter
  t = Ai::FlowTrigger.new(
    project: project,
    event_types: [Ai::FlowTrigger::EVENT_TYPES[:merge_request]],
    filter: {}
  )
  t.valid?
  puts "Empty filter OK: #{t.errors[:filter].empty?}" # expect true

Feature flag rollout issue

References

Screenshots or screen recordings

Before After

How to set up and validate locally

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Edited by Shola Quadri

Merge request reports

Loading