Make `fallback_behavior` support async pipeline creation

What does this MR do and why?

Fixes fail-open approval policy rules getting prematurely unblocked during MR creation when the async_mr_pipeline_creation feature flag is enabled.

fallback_behavior: { fail: open } controls what happens when scan reports are unavailable. If a pipeline is created but lacks the required security reports (e.g., scanner misconfiguration or pipeline failure), the approval rule is unblocked. If no pipeline is created at all, the rule is also unblocked. If the reports are available, the rule is evaluated normally and requires approval when violations are found. The default fail: closed keeps the rule enforced in all cases where reports are missing.

When pipeline creation is async, schedule_policy_synchronization runs before CreatePipelineWorker has created the pipeline. It sees diff_head_pipeline as nil, incorrectly concludes no pipeline will ever exist, and enqueues UnenforceablePolicyRulesNotificationWorker. This worker deletes the running violation and marks the fail_open rule as passed, making the MR mergeable while the pipeline is still being created and running.

With this change, when an async pipeline creation is in flight, the premature unblock is skipped.

References

Code references

The premature unblock:

How to set up and validate locally

Setup

  1. Create a new project

  2. Add a second Owner member to the project

  3. Commit a .gitlab-ci.yml to the default branch:

    variables:
      AST_ENABLE_MR_PIPELINES: "true"
      CS_IMAGE: "nginx:1"
    
    test:
      image: busybox
      # For checking violation state while pipeline is running
      script: sleep 120
      rules:
        - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    
    include:
      - template: Jobs/Container-Scanning.gitlab-ci.yml
        rules:
          - if: $CI_PIPELINE_SOURCE == "merge_request_event"
  4. Navigate to Secure > Policies and create the following approval policy:

    type: approval_policy
    name: Container Scanning Fail Open
    enabled: true
    fallback_behavior:
      fail: open
    rules:
    - type: scan_finding
      scanners:
      - container_scanning
      vulnerabilities_allowed: 0
      severity_levels: []
      vulnerability_states: []
      branch_type: protected
    actions:
    - type: require_approval
      approvals_required: 1
      role_approvers:
        - owner
  5. Enable the feature flag in Rails console:

    Feature.enable(:async_mr_pipeline_creation)

Reproduce the bug on master

  1. Open a new MR (add any file change)

  2. Immediately check the violation status in Rails console and verify it was prematurely set to warn (mergeable):

    mr = MergeRequest.last
    mr.scan_result_policy_violations.pluck(:id, :status)
    # => [[<id>, "warn"]]
  3. Note the MR can be merged while the pipeline is still running

Verify the fix

  1. Repeat steps 7-8 on this branch

  2. The violation should remain running:

    mr = MergeRequest.last
    mr.scan_result_policy_violations.pluck(:id, :status)
    # => [[<id>, "running"]]
  3. The MR should NOT be mergeable while the pipeline is running

  4. After the pipeline completes and container scanning reports findings, the rule should require approval (violation transitions to failed)

  5. After the pipeline completes without findings, the rule should be unblocked (violation deleted, approvals_required set to 0)

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 Dominic Bauer

Merge request reports

Loading