Ensure VAC default context exists before store phase

What does this MR do and why?

Durable fix for #612930, where a VAC project's first default-branch pipeline drops the whole SAST slice on ingestion.

The store phase resolves the tracked context find-only and gets nil when no default context exists yet, so it stores findings with context-unaware (v1) UUIDs. Ingestion then find-or-creates the default context (v2) and re-parses with context-aware (v2) UUIDs, the join misses on every finding, and the slice is dropped with an IngestionError.

This ensures the default-branch tracked context exists before the store phase runs, so both phases resolve the same v2 context and agree on the UUID scheme. The default branch stays on v2, which is the intended end state. No stored data changes and the versioning rule is untouched.

Gated behind vac_ensure_default_context_before_store. This supersedes the find-only approach in !250927 (closed), which is being closed. Rollout tracked in #613547.

How to set up and validate locally

  1. Enable VAC and the flag for a brand-new project with no tracked contexts:
    Feature.enable(:vulnerabilities_across_contexts)
    Feature.enable(:vac_ensure_default_context_before_store)
  2. Run the first pipeline on the default branch with a SAST report that produces findings.
  3. Confirm the SAST findings ingest (no IngestionError on pipeline.security_scans), and that the default-branch Security::ProjectTrackedContext was created with uuid_version: 2.

Test coverage

ee/spec/services/security/store_scans_service_spec.rb: 32 examples, 0 failures. Covers creating the context, creating it as a v2 default context, idempotency when it already exists, non-default-branch pipelines, VAC disabled, and feature flag disabled. RuboCop clean.

Edited by Gregory Havenga

Merge request reports

Loading
Loading