The pipeline that ships your code is also the shortest path to production for anyone who compromises it. CI/CD pipeline security is about that path: the runners that execute build steps, the secrets those steps hold, the third-party actions they pull in, and the cloud trust they hold to deploy. Get it wrong and an attacker does not need to breach production directly. They poison a build, read a token from a log, or trick your pipeline into handing them a cloud credential. This guide walks the real attack paths and the controls that close each one, using GitHub Actions for concrete examples, though the same ideas apply to GitLab CI, Jenkins, and the rest.
This is the attacker’s view of the pipeline. For the developer’s view of moving testing earlier, see shifting security left in the SDLC; for the tools that scan code in the pipeline, see SAST vs DAST vs IAST.
Why the pipeline is a target
CI/CD is designed for speed. Code goes from a developer’s machine to production in minutes, largely on automation, with minimal human review at each hop. That is exactly what makes it attractive: the pipeline is a highly trusted, highly automated system that holds credentials to everything downstream. The industry has the incident history to prove it. The SolarWinds build system was compromised to push malware to thousands of customers. The Codecov breach exfiltrated secrets stored in environment variables across thousands of build pipelines. Dependency-confusion attacks, which abuse the way build tools fetch packages, hit dozens of large enterprises. OWASP built an entire list around this surface, the Top 10 CI/CD Security Risks, first released in 2022 and reviewed by security leaders from across the industry.
CI/CD pipeline security attack paths
Poisoned pipeline execution
The most direct attack is to make the pipeline run your code. OWASP calls this Poisoned Pipeline Execution. If a pipeline builds unreviewed code, for example anything triggered directly off a pull request or a push to an arbitrary branch, an attacker who can influence that code can run commands in the build. OWASP describes a direct flavor, where the attacker edits the pipeline config file itself, and an indirect one, where they cannot touch the config but can modify a file it runs: a Makefile, a test script, a linter config that loads external code. The worst case is the public variant, a public repository whose pipeline runs code from anyone’s pull request, which exposes the build environment, and any secrets in it, to the entire internet.
Controls. Require review before code that a pipeline will execute is merged, and do not run privileged pipelines on unreviewed pull-request code. On GitHub, understand pull_request_target: it runs with access to secrets, so a workflow using it “must not explicitly check out untrusted code, including from pull request forks.” Keep pipeline definitions in version control and under review like any other code.
Untrusted input as code injection
A build step that drops untrusted text straight into a shell is an injection bug, the same class as OWASP Top 10 injection, just in YAML. A workflow that interpolates a pull request title or an issue body directly into a run: block lets an attacker put shell commands in that title. GitHub’s own guidance is blunt: for inline scripts, “the preferred approach to handling untrusted input is to set the value of the expression to an intermediate environment variable.” Bind the value to an env variable and reference it as a variable, so it is data, not code.
Credential theft from runners and logs
Pipelines are full of secrets: registry tokens, cloud keys, signing keys, package-manager tokens. Two things go wrong. Secrets get printed into logs, and on public repositories those logs are world-readable. And secrets sit in a runner’s memory where a malicious build step can read them. The March 2025 compromise of the tj-actions/changed-files GitHub Action (CVE-2025-30066) showed both at once: the action’s version tags were repointed to a malicious commit that pulled secrets from the runner and printed them into the build logs. Any repository that ran it during the compromise window could leak access keys, GitHub personal access tokens, npm tokens, and private keys, readable by anyone wherever the logs were public. CISA told affected organizations to treat those secrets as compromised and rotate them immediately.
Controls. Never hard-code secrets; use a secrets manager or the platform’s secret store. Never log secrets, and never pass structured data (a JSON or YAML blob) as a single secret, because “this significantly reduces the probability the secrets will be properly redacted.” Prefer short-lived credentials over long-lived keys wherever the platform allows it, which leads directly to OIDC.
OIDC trust misconfiguration
The modern fix for long-lived cloud keys in a pipeline is OpenID Connect: instead of storing an AWS or Azure key, the workflow requests a short-lived token from the CI provider and exchanges it for cloud access, governed by a trust policy on the cloud side. Done right, there is no standing secret to steal. Done wrong, it is a new front door. The workflow must grant id-token: write, and, crucially, the cloud trust policy must pin the token’s claims. GitHub’s documentation is explicit that you “must define at least one condition, so that untrusted repositories can’t request access tokens for your cloud resources.” Without a subject (sub) condition, a workflow in someone else’s repository could obtain a token your cloud role accepts.
Controls. Restrict the trust policy to your repository, and further to a branch or environment, using the subject claim, for example repo:your-org/your-repo:ref:refs/heads/main or repo:your-org/your-repo:environment:Production. Do not trust on the organization alone, and do not use loose wildcards. Combine the audience and subject claims to scope access tightly. This is the same identity-misconfiguration risk we hunt in a cloud penetration test: an over-trusting role is an over-trusting role whether a human or a pipeline assumes it.
Supply chain: third-party actions and dependencies
Every third-party action or dependency your pipeline pulls is code you run with your pipeline’s privileges. A version tag is mutable: an attacker who controls the upstream can repoint @v4 at malicious code, as the tj-actions incident showed. The 2025 edition of the OWASP Top 10 made this whole area its own category, A03 Software Supply Chain Failures, and it ranked first in the community survey behind that list.
Controls. Pin third-party actions to a full-length commit SHA, which GitHub calls “the only way to use an action as an immutable release.” Pin dependency versions with lock files and verify integrity hashes, prefer scoped or private package feeds to blunt dependency confusion, and keep an inventory of what your pipeline actually runs. Our source code review covers dependency and infrastructure-as-code review as part of the scope.
Over-privileged tokens and weak IAM
The default token a pipeline is handed is often far more powerful than the job needs. GitHub recommends setting “the default permission for the GITHUB_TOKEN to read access only for repository contents,” then raising it per job only where required. The same least-privilege logic applies to the cloud roles a pipeline assumes and to who can edit pipelines, add runners, or change branch protection.
A CI/CD hardening checklist
| Area | Control |
|---|---|
| Flow control | Require review before pipelines run merged code; do not build unreviewed PRs with secrets |
| Injection | Bind untrusted input to environment variables, never interpolate into shell steps |
| Secrets | Use a secret store, never log secrets, prefer short-lived credentials |
| OIDC | Grant id-token: write only to jobs that deploy; pin cloud trust to repo plus branch or environment |
| Supply chain | Pin actions to commit SHA, lock and hash-verify dependencies |
| Runners | Isolate build nodes; keep self-hosted runners off public repositories |
| IAM | Least privilege on pipeline tokens, cloud roles, and pipeline-edit rights |
| Visibility | Centralize and monitor build logs for anomalies and secret exposure |
How this is tested
CI/CD runner, secret, and OIDC-trust review is in scope at Invadel as part of a cloud penetration test from $6,800, when the pipeline deploys to a cloud environment, and as part of a secure code review from $4,800, when the focus is the pipeline definitions, IaC, and dependencies. There is no separate pipeline product and no separate price: it is reviewed inside the engagement that owns the surface. If the pipeline deploys into Google Cloud, our GCP penetration testing guide shows what the cloud side of that test covers, and where a Kubernetes cluster is the deploy target, Kubernetes penetration testing covers the cluster the pipeline pushes to.
Frequently asked questions
What is CI/CD pipeline security? It is the practice of securing the systems and processes that build and deploy software: the runners that execute steps, the secrets they hold, the third-party actions and dependencies they pull, and the cloud trust they use to deploy. The goal is to stop an attacker from using the pipeline as a fast, trusted path into production.
What is a poisoned pipeline execution attack? An attack where someone who can influence code that a pipeline runs, such as a pipeline config file, a build script, or a pull request on a repository whose pipeline builds unreviewed code, gets the pipeline to execute their commands with its privileges. The fix is to require review before pipelines run merged code and to isolate builds of untrusted input.
Is OIDC safer than storing cloud keys in a pipeline? Yes, when configured correctly, because there is no long-lived key to steal; the pipeline gets a short-lived token instead. But the cloud trust policy must restrict which repository, branch, or environment can request a token. Without a subject-claim condition, any repository could request access to your cloud role.
How do we protect against a compromised GitHub Action? Pin third-party actions to a full-length commit SHA rather than a mutable tag, keep an inventory of what your workflows use, give the pipeline token least privilege, and monitor build logs. The 2025 tj-actions/changed-files compromise, where repointed version tags made the action print secrets into build logs, is the cautionary example.
Is CI/CD security the same as shifting left? No. Shifting left is about testing application code earlier in development. CI/CD pipeline security is about the pipeline itself as an attack surface: its runners, secrets, and trust. They are complementary, and our guide to shifting security left in the SDLC covers the former.
Fixed price, fixed scope, free retest. CI/CD review is in scope of a cloud penetration test from $6,800 and a secure code review from $4,800. Scope your test and get a written price within one business day.
Written by
Invadel Team
Senior penetration testers writing from real engagements, the same team that scopes, tests, and reports for our clients. About Invadel →