Poisoned pipeline execution (PPE), listed as CICD-SEC-4 in the OWASP Top 10 CI/CD Security Risks, lets an attacker with source repository access make your build system run their commands by changing pipeline configuration or files the pipeline uses. Stop it by protecting branches, reviewing pipeline changes and isolating untrusted builds.
What does OWASP say it is?
OWASP defines PPE as the ability of an attacker with access to source control systems, and without access to the build environment, to manipulate the build process by injecting malicious code or commands into the pipeline configuration. The malicious code then runs as part of the build.
The vector abuses permissions on a repository so that a CI pipeline executes malicious commands. Anyone who can change the CI configuration, or files the pipeline relies on, can poison it. OWASP adds that once the attacker can run code in the pipeline, they can do a wide range of malicious operations all within the context of the pipeline's identity. That identity is often powerful: it holds deployment credentials, signing keys and cloud roles.
What are the three types?
Direct PPE. The attacker edits the CI configuration file in a repository they can reach, by pushing to an unprotected branch or by opening a pull request from a branch or fork. If the pipeline runs on push or pull request events using the modified file, the new commands run on the build node.
Indirect PPE. Direct editing is blocked, for example because the pipeline reads its configuration from a protected branch, a separate repository, or the CI system itself. The attacker instead changes files the pipeline configuration references. OWASP lists examples: a Makefile, scripts stored in the same repository, test code, and the configuration of linters and scanners that load and run external code.
Public PPE. On a public repository, anonymous users can suggest code. If the pipeline runs that unreviewed code, it is open to what OWASP calls 3PE. It can also expose secrets of private projects when the public repository's pipeline runs on the same CI instance as private ones.
Why is it hard to spot?
The malicious change is small and looks like routine work: an added line in a test script, a changed build step. Reviewers read application code carefully and skim pipeline files. And the pipeline often runs before any human has reviewed the change, which is exactly the situation OWASP says is more susceptible: pipelines triggered directly off pull requests or commits to arbitrary branches execute code that has not been reviewed.
What does a constructed example look like?
A company's CI runs tests on every pull request from any branch. The test job has access to a token that can publish packages. A contributor with only write access to a feature branch edits a test helper script to print the token to the log, or send it elsewhere. The pull request is not merged, and nobody reviews it, but the pipeline already ran the script with the token. The code never reached the main branch, and the secret is gone.
The fix is not to trust reviewers more. It is to ensure the job that runs unreviewed code never holds a powerful secret.
How do you prevent it?
- Protect branches. Require review for changes to the main branch, and for changes to pipeline files and the scripts they call.
- Separate trust levels. Run unreviewed code, such as pull requests from forks, on isolated runners with no secrets and limited network access.
- Give each job the least privilege. Tokens should be scoped and short-lived, and only attached to jobs that need them.
- Keep pipeline definitions out of reach. Where your CI allows it, load the configuration from a protected branch or a separate repository.
- Use code owners. Require named reviewers for pipeline files, Makefiles and build scripts.
- Pin and verify external steps. Reference actions and plugins by an immutable identifier, not a moving tag.
- Log and alert. Notify on changes to pipeline files and on unexpected secret use.
How to check or apply this
- List which pipelines run on pull requests or arbitrary branches, and which secrets each can see.
- Move secrets out of any job that runs unreviewed code.
- Turn on branch protection and add code owners for pipeline files and build scripts.
- Switch to runners that are ephemeral and isolated for untrusted builds.
- Pin third-party pipeline steps by immutable reference.
- Review pipeline permissions quarterly and after any CI migration.
Safeguard's policies and gates can enforce checks on changes before they merge, which supports the review and gating steps.
Quick check
Does PPE require access to the build server?
No. OWASP's definition is that the attacker has access to source control but not to the build environment, and uses the pipeline to run code there.
Is protecting the pipeline file enough?
No. Indirect PPE changes files the pipeline calls, such as scripts and test code, so those need protection and review as well.
Sources
- OWASP Top 10 CI/CD Security Risks, CICD-SEC-4 Poisoned Pipeline Execution
- NIST SP 800-218, Secure Software Development Framework
Last reviewed: 18 October 2026.