Recognize agentic analyzer workload pipelines for security ingestion

Summary

Teach the security ingestion layer to recognize Duo AI external agent workload pipelines as default-branch scans, so their security findings are promoted into the Vulnerability Report.

Problem

Workload pipelines run on synthetic refs (refs/workloads/<hash>) with source: :duo_workflow. pipeline.default_branch? checks ref == project.default_branch, so it returns false for workload pipelines even when their SHA matches the default branch HEAD. The security ingestion gate rejects them and findings never reach the Vulnerability Report.

Solution

Add default_branch_security_scan? to EE::Ci::Pipeline — a dedicated predicate that extends default_branch? for security ingestion. For regular pipelines it delegates to default_branch?. For duo_workflow workload pipelines, it checks SHA equality with the default branch HEAD (feature-flag gated).

Replace pipeline.default_branch? with pipeline.default_branch_security_scan? in the security ingestion path.

Changes

  • ee/app/models/ee/ci/pipeline.rb (+19 lines) — default_branch_security_scan? method, memoized with SafeRequestStore
  • ee/app/services/security/ingestion.rb (1 line change) — use default_branch_security_scan?
  • ee/app/services/security/project_tracked_contexts/find_or_create_service.rb (+3 lines) — use default_branch_security_scan?, bind workload context to default branch name instead of synthetic ref
  • Feature flagagentic_analyzer_security_ingestion (WIP, off by default). Rollout: #606048

How to test

Prerequisites

  • GDK with rails-web, rails-background-jobs, and a CI runner
  • !245049 (merged) applied (adds artifacts_reports to WorkloadDefinition)
  • An external agent seeded with report_artifacts: { sast: [gl-sast-report.json] }
  • A project with a flow trigger configured for pipeline events

Steps

  1. Enable the feature flag in the GDK rails console (bundle exec rails console):

    Feature.enable(:agentic_analyzer_security_ingestion)
  2. Merge an MR into main on the test project. This triggers the external agent via the flow trigger.

  3. Wait for the workload pipeline to complete:

    Project.find(<id>).all_pipelines.where(source: :duo_workflow).order(id: :desc).first.status
  4. Verify the new method works:

    pipeline = Project.find(<id>).all_pipelines.where(source: :duo_workflow).order(id: :desc).first
    pipeline.default_branch?                  # => false (synthetic ref)
    pipeline.default_branch_security_scan?    # => true  (SHA matches)
    Security::Ingestion.ingest_pipeline?(pipeline)  # => true
  5. Verify findings were ingested:

    Vulnerability.where(project: Project.find(<id>)).each do |v|
      puts "#{v.severity} - #{v.title} (present_on_default_branch: #{v.present_on_default_branch})"
    end

    Expected: vulnerabilities with present_on_default_branch: true.

  6. Disable the flag and repeat — workload pipeline should be rejected by ingest_pipeline?.

  • Issue: #605465 / #603669
  • Rollout: #606048
  • Part 1 (workload report packaging): !245049 (merged)
  • Epic: &22756
Edited by Mher Tolpin

Merge request reports

Loading
Loading