BUG: Scan and Pipeline Execution Policies do not work correctly when repos have no or blank .gitlab-ci.yaml

Summary

When using a combination of Scan Execution and Pipeline Execution policies, scan jobs do not get run when a Repo has no .gitlab-ci.yaml file

When using a Pipeline Execution policy, pipelines do not get execution when Repo has a blank .gitlab-ci.yaml file

Steps to reproduce

Scenario 1:

  1. Setup a Repo with no .gitlab-ci.yaml file
  2. Add a Pipeline Execution policy with inject_ci as it's strategy to the Group
  3. Add a Scan Execution policy defining some scans to the Group
  4. Run Pipeline on Repo -> BUG Only jobs in Pipeline Execution Policy get executed
  5. Add a .gitlab-ci.yaml file to Repo with a "Hello World" job. -> Both Pipeline Execution and Scan Execution policy jobs get run

Scenario 2:

  1. Setup a Repo with an empty .gitlab-ci.yaml file
  2. Add a Pipeline Execution policy with inject_ci as it's strategy to the Group
  3. Run pipeline on Repo -> BUG Pipeline fails with error Included file .gitlab-ci.yml does not have valid YAML syntax!

What is the expected correct behavior?

Scenario 1: Scan Execution jobs appear in pipelines along with Pipeline Execution jobs

Scenario 2: Pipeline Execution jobs should still be run if .gitlab-ci.yml is blank, otherwise users could bypass security controls

Implementation plan

Scenario 1:

We currently clear the whole pipeline with the pipeline source pipeline_execution_policy_forced. This removes also the SEP jobs. Rough patch to address it (we should probably only clear the DUMMY job instead of the .pre stage):

diff --git a/ee/lib/ee/gitlab/ci/pipeline/chain/pipeline_execution_policies/merge_jobs.rb b/ee/lib/ee/gitlab/ci/pipeline/chain/pipeline_execution_policies/merge_jobs.rb
index 306f0fd8c71b..ff6c83fd29a8 100644
--- a/ee/lib/ee/gitlab/ci/pipeline/chain/pipeline_execution_policies/merge_jobs.rb
+++ b/ee/lib/ee/gitlab/ci/pipeline/chain/pipeline_execution_policies/merge_jobs.rb
@@ -44,12 +44,13 @@ def clear_project_pipeline
                 # the DUMMY job to enforce the pipeline without project CI configuration.
                 # 2. any policy uses `override_project_ci` strategy.
                 # It means that we need to ignore the project CI configuration.
-                unless pipeline.pipeline_execution_policy_forced? ||
-                    command.pipeline_policy_context.has_overriding_execution_policy_pipelines?
-                  return
+                if pipeline.pipeline_execution_policy_forced?
+                  pipeline.stages = pipeline.stages.reject do |stage|
+                    stage.name == ::Gitlab::Ci::Config::EdgeStagesInjector::PRE_PIPELINE
+                  end
+                elsif command.pipeline_policy_context.has_overriding_execution_policy_pipelines?
+                  pipeline.stages = []
                 end
-
-                pipeline.stages = []
               end
 
               def merge_policy_jobs
diff --git a/ee/spec/services/ci/create_pipeline_service/pipeline_execution_policy_spec.rb b/ee/spec/services/ci/create_pipeline_service/pipeline_execution_policy_spec.rb
index ec87f803094e..7618d6b984e2 100644
--- a/ee/spec/services/ci/create_pipeline_service/pipeline_execution_policy_spec.rb
+++ b/ee/spec/services/ci/create_pipeline_service/pipeline_execution_policy_spec.rb
@@ -569,6 +569,33 @@
       expect(stages.find_by(name: 'build').builds.map(&:name)).to contain_exactly('namespace_policy_job')
       expect(stages.find_by(name: 'test').builds.map(&:name)).to contain_exactly('project_policy_job')
     end
+
+    context 'when both Scan Execution Policy and Pipeline Execution Policy are applied on the project' do
+      let(:scan_execution_policy) do
+        build(:scan_execution_policy, actions: [{ scan: 'secret_detection' }])
+      end
+
+      let(:project_policy_yaml) do
+        build(:orchestration_policy_yaml,
+          pipeline_execution_policy: [project_policy],
+          scan_execution_policy: [scan_execution_policy])
+      end
+
+      it 'persists both pipeline execution policy and scan execution policy jobs', :aggregate_failures do
+        expect { execute }.to change { Ci::Build.count }.from(0).to(3)
+
+        expect(execute).to be_success
+        expect(execute.payload).to be_persisted
+
+        stages = execute.payload.stages
+        expect(stages.map(&:name)).to contain_exactly('build', 'test')
+
+        expect(stages.find_by(name: 'build').builds.map(&:name)).to contain_exactly('namespace_policy_job')
+        expect(stages.find_by(name: 'test').builds.map(&:name))
+          .to contain_exactly('project_policy_job', 'secret-detection-0')
+      end
+    end
   end
 
   context 'when commit contains a [ci skip] directive' do

Scenario 2:

This is currently expected, but we can make an enhancement to it.

We have to force the project pipeline to continue if we run in pipeline_context.execution_policy_mode? and the project CI is invalid. The config is processed in lib/gitlab/ci/pipeline/chain/config/process.rb, so we'll need to override it if the validation fails there and there are PEPs to run.

Edited by Martin Cavoj