CI/CD security
CI/CD pipeline security checklist
A CI/CD pipeline holds credentials, runs code from the internet and produces what your customers install. That makes it a target in its own right. Use this checklist to review a GitHub Actions pipeline, from the workflow file to the artifact it ships.
Updated
Workflows
- Pin third-party actions to a full commit SHA. GitHub calls this "currently the only way to use an action as an immutable release." A tag can be changed to point at other code. In March 2025, CISA reported that every version of tj-actions/changed-files was compromised for three days.
- Give
GITHUB_TOKENthe least privilege. Default to read-only for contents and grant more per job. - Never check out untrusted code in
pull_request_target. These workflows run with secrets and elevated permissions. - Keep untrusted input out of scripts. Pass titles, branch names and other user-controlled values through an environment variable instead of expanding them into a shell command.
- Review changes to workflow files. Require code owner review for
.github/workflows.
Secrets
- Prefer short-lived credentials. Use OpenID Connect so jobs get short-lived, scoped tokens from your cloud provider instead of long-lived keys stored as secrets.
- Scope secrets to environments. Put production credentials behind an environment with required reviewers.
- Rotate after an incident. CISA's advice after the tj-actions compromise was to treat exposed secrets as compromised and rotate them immediately.
Runners
- Use ephemeral runners. A fresh machine per job leaves nothing behind for the next one.
- Keep self-hosted runners out of public repositories. GitHub says they should almost never be used there, because anyone can open a pull request against the repository.
- Isolate jobs. One job should not be able to read another job's workspace or credentials.
Network
- Control egress. Allow the package sources and services a job needs and block the rest, so a compromised dependency cannot send secrets out. How an egress policy stops exfiltration
- Record connections. Keep a trace of what each job connected to, so you can investigate later.
Dependencies
- Commit lockfiles. Builds should resolve the same versions every time.
- Review new dependencies. Check what a pull request adds before it merges.
- Gate builds on findings. Block a build when it pulls in a package with a known critical vulnerability, before the artifact ships.
Build evidence
- Record an SBOM of what the build fetched. A scan of files afterwards describes what was declared, not what the build used.
- Sign provenance. Link each artifact to the build that produced it, so you can check later that what runs is what was built.
- Monitor what shipped. Re-check released builds when new vulnerabilities are published.
Which tools cover which items
Scanners cover the dependency items, pipeline hardening tools cover the workflow items, and agents on the runner or a secure runner cover the network and evidence items. CRACI is the runner: it enforces an egress policy that fails closed, records an SBOM of what each job fetched, lets policy gates block a build on findings, and returns SBOMs, network traces and provenance through its API. It does not scan code or workflows. See CI/CD security tools compared for which tool covers what.
CI/CD pipeline security: frequently asked questions
What is CI/CD pipeline security?
It is protecting the systems that build and ship your software: the workflow definitions, the secrets they use, the runners that execute them, the dependencies they download and the artifacts they produce. A compromised pipeline can ship malicious code under your name without touching your source code.
Why pin GitHub Actions to a commit SHA?
Tags can be changed to point at different code. GitHub states that pinning an action to a full-length commit SHA is currently the only way to use an action as an immutable release. In March 2025, CISA reported that every version of tj-actions/changed-files was compromised for three days.
What permissions should GITHUB_TOKEN have?
The minimum each job needs. Set the default to read-only for repository contents and grant more per job, only where a job needs it.
Do I need network egress control in CI?
If your jobs hold secrets and run third-party code, yes. Most jobs only need to reach a known set of package registries and services. An egress policy blocks everything else, so a compromised dependency cannot send secrets to an attacker's server.
Check the network and evidence items on one job
Run one workflow on CRACI and see its egress policy, the connections it made and the SBOM of what it fetched.
Book a demo