Enforce Custom Stages in Pipeline Execution Policies
<!--This issue template can be used as a great starting point for feature requests. Learn more about the process: https://handbook.gitlab.com/handbook/product/how-to-engage/#customer-feature-requests. The section "Release notes" can be used as a summary of the feature and is also required if you want to have your release post blog MR auto generated using the release post item generator: https://about.gitlab.com/handbook/marketing/blog/release-posts/#release-post-item-generator. The remaining sections are the backbone for every feature in GitLab.
The goal of this template is brevity for quick/smaller iterations. For a more thorough list of considerations for larger features or feature sets, you can leverage the detailed [feature proposal](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/issue_templates/Feature%20proposal%20-%20detailed.md).-->
# Release notes
We're excited to introduce a new capability for Pipeline Execution Policies (PEP) that allows you to enforce **custom stages** into your CI/CD pipelines. This feature provides greater flexibility and control over your pipeline structure while maintaining security and compliance requirements.
#### Benefits
* **Enhanced pipeline customization**: Define and inject custom stages at specific points in your pipeline, allowing for more granular control over job execution order.
* **Improved security and compliance**: Ensure that security scans and compliance checks run at the most appropriate times in your pipeline, such as after build but before deployment.
* **Flexible policy management**: Maintain centralized policy control while allowing development teams to customize their pipelines within defined guardrails.
* **Seamless integration**: Custom stages work alongside existing project stages and other policy types, providing a non-disruptive way to enhance your CI/CD workflows.
#### How it works
The new `inject_policy_ci` strategy for Pipeline Execution Policies allows you to define custom stages in your policy configuration. These stages are then intelligently merged with your project's existing stages using a Directed Acyclic Graph (DAG) algorithm, ensuring proper ordering and preventing conflicts.
For example, you can now easily inject a custom security scanning stage between your build and deploy stages:
```yaml
stages: [build, policy-test, deploy]
policy-security-scan-job:
stage: policy-test
script:
- run_security_script
```
This feature provides a powerful way to standardize and enforce security practices across your projects while maintaining the flexibility needed for diverse development workflows.
The `inject_ci` mode will be deprecated, allowing you to opt into the `inject_policy_ci` mode. The `inject_policy_ci` mode will become the default when configuring policies with `Inject` in the policy editor.
# Problem to solve
<!--What is the user problem you are trying to solve with this issue?-->
Currently, Pipeline Execution Policies only allow jobs to be placed in the following two stages:
- `.pipeline-policy-pre` which always run at the start of the pipeline.
- `.pipeline-policy-post`, which always runs at the end.
In the experimental feature [Pipeline execution policy action](https://docs.gitlab.com/17.0/ee/user/application_security/policies/scan-execution-policies.html#pipeline-execution-policy-action) (no longer available), there as an additional stage (`.pipeline-policy-test`) which always ran after the `test` stage, but this behavior was not carried over to `Pipeline Execution Policies`.
This limitation can affect the needs of some users (for example, those that practice trunk-based development and can't make use of [MRAP's](https://docs.gitlab.com/ee/user/application_security/policies/scan-result-policies.html)) who require more granular control over when policy-enforced jobs run, especially in relation to critical stages like testing and deployment.
## Example use case
Consider a workflow in a trunk-based development project in which a `deploy` job must run _only_ if the Pipeline Execution Policy jobs are successful and find no vulnerabilities:
```mermaid
graph LR
A[build] --> B[test] --> C[analyze]--> D[deploy]
```
The `deploy` job should only run if the `test` jobs run properly and generate security artifacts which are consumed consumed by the `analyze` job in order to parse any detected vulnerabilities. If any vulnerabilities are found, the `analyze` job fails the pipeline which prevents the vulnerabilities from being deployed.
Ideally, the `analyze` job would be enforced via a Pipeline Execution Policy; however, since neither of the two available stages run after the `test` job, they cannot fail the pipeline and prevent the changes to being deployed.
```mermaid
graph LR
A[.pipeline-policy-pre] --> B[Build] --> C[Test] --> D[Deploy] --> E[.pipeline-policy-post]
```
Notice that previously, the `.pipeline-policy-pre` stage helped achieving just that, but the feature does no longer implement this stage:
```mermaid
graph LR
A[.pipeline-policy-pre] --> B[Build] --> C[Test] --> D[.pipeline-policy-test] --> E[Deploy] --> F[.pipeline-policy-post]
```
# Proposal
Introduce support for "custom stages" that can be placed in the middle of the pipeline by defining stages sequentially. For example, `policy-test` can be placed between `test` and `deploy` in the project pipeline:
```
stages: [test, policy-test, deploy]
policy-test-job:
stage: policy-test
script:
- sleep 1
```
It is possible to specify any possible stages that projects might be using. Only stages with jobs will be present in the resulting pipeline. For example:
```
stages: [build, compile, test, check, policy-test, deploy, publish]
policy-test-job:
stage: policy-test
script:
- sleep 1
```
In this case, `policy-test` will be placed after `build, compile, test, check` and before `deploy, publish`. If project was using `stages: [compile, check, publish]`, the resulting pipeline would contain stages: `[compile, check, policy-test, publish]`.
If policy stages only specify the custom stage, this will be placed at the end of the pipeline, just before `.pipeline-policy-post` (if used):
```
stages: [policy-test]
policy-test-job:
stage: policy-test
script:
- sleep 1
```
With default project stages, this results in `[build, test, deploy, policy-test]`.
When used in a project without `.gitlab-ci.yml`, the behavior is the same as injecting into default stages.
#### Behavior with other policies
- It is additive to other policies and the project
- When used together with `override_project_ci` policies, it merges into the override stages.
#### New strategy
Users can specify a new `pipeline_config_strategy` called `inject_policy_ci` which enables custom stages from the policy pipelines to be injected. It is made as a separate strategy so that users can opt-in and avoid breaking changes. `inject_ci` will be deprecated and customers will be encouraged to use the new strategy.
#### Possible pipeline errors
Depending on the policy stages configuration, the pipeline could fail with this strategy - in case the stages are defined as cyclic. Example:
Policy stages: `[test, deploy]` Project stages: `[build, deploy, test]`
In this example, `deploy` depends on `test` in the policy, but `test` depends on `deploy` in the project. This configuration results in a "cyclic dependencies" error:
{width="585" height="122"}
<!--Who will use this feature? If known, include any of the following: types of users (e.g. Developer), personas, or specific company roles (e.g. Release Manager). It's okay to write "Unknown" and fill this field in later.
Personas are described at https://handbook.gitlab.com/handbook/product/personas/
* [Parker (Product Manager)](https://handbook.gitlab.com/handbook/product/personas/#parker-product-manager)
* [Delaney (Development Team Lead)](https://handbook.gitlab.com/handbook/product/personas/#delaney-development-team-lead)
* [Presley (Product Designer)](https://handbook.gitlab.com/handbook/product/personas/#presley-product-designer)
* [Sasha (Software Developer)](https://handbook.gitlab.com/handbook/product/personas/#sasha-software-developer)
* [Priyanka (Platform Engineer)](https://handbook.gitlab.com/handbook/product/personas/#priyanka-platform-engineer)
* [Sidney (Systems Administrator)](https://handbook.gitlab.com/handbook/product/personas/#sidney-systems-administrator)
* [Rachel (Release Manager)](https://handbook.gitlab.com/handbook/product/personas/#rachel-release-manager)
* [Simone (Software Engineer in Test)](https://handbook.gitlab.com/handbook/product/personas/#simone-software-engineer-in-test)
* [Allison (Application Ops)](https://handbook.gitlab.com/handbook/product/personas/#allison-application-ops)
* [Ingrid (Infrastructure Operator)](https://handbook.gitlab.com/handbook/product/personas/#ingrid-infrastructure-operator)
* [Dakota (Application Development Director)](https://handbook.gitlab.com/handbook/product/personas/#dakota-application-development-director)
* [Dana (Data Analyst)](https://handbook.gitlab.com/handbook/product/personas/#dana-data-analyst)
* [Eddie (Content Editor)](https://handbook.gitlab.com/handbook/product/personas/#eddie-content-editor)
* [Amy (Application Security Engineer)](https://handbook.gitlab.com/handbook/product/personas/#amy-application-security-engineer)
* [Isaac (Infrastructure Engineer)](https://handbook.gitlab.com/handbook/product/personas/#isaac-infrastructure-security-engineer)
* [Alex (Security Operations Engineer)](https://handbook.gitlab.com/handbook/product/personas/#alex-security-operations-engineer)
* [Cameron (Compliance Manager)](https://handbook.gitlab.com/handbook/product/personas/#cameron-compliance-manager)-->
## Verification steps
1. Create a project
2. Create a policy with `pipeline_config_strategy: inject_policy`
3. Verify the various examples mentioned in the documentation: https://docs.gitlab.com/ee/user/application_security/policies/pipeline_execution_policies.html#stages-injection
<!--How are you going to track usage of this feature? Think about user behavior and their interaction with the product. What indicates someone is getting value from it?
Explore (../../doc/development/internal_analytics/internal_event_instrumentation/quick_start.md) for a guide.-->
<!--- Checkout these docs to know more
https://docs.gitlab.com/ee/development/audit_event_guide/#what-are-audit-events
https://docs.gitlab.com/ee/administration/audit_events.html
--->
<!--Label reminders
Make sure to add the appropriate labels for the product stage and/or group (e.g ~"devops::plan") if known and add a comment tagging the appropriate Product Manager.
Use the following resources to find the appropriate labels:
- Use only one tier label choosing the lowest tier this is intended for
- https://gitlab.com/gitlab-org/gitlab/-/labels
- https://about.gitlab.com/handbook/product/categories/features/
Examples:
/label ~group:: ~section:: ~Category:
/label ~"GitLab Free" ~"GitLab Premium" ~"GitLab Ultimate"-->
issue