This is a follow up on my older post “7 Best Practices of Modern CI/CD“. Points outlined there still hold true, but they are missing several important security considerations. Today, in 2026, CI/CD pipelines have become one of the key supply chain attack vectors (refer, for example, to the recent Trivy compromise). That warrants an update.

Table of Contents
1. Redundancy: At Least 2 Independent Systems Need to Fail for a Successful Compromise
This is the core principle, but it is also abstract. The following ones will be more actionable. Here we assume that compromise may already have happened somewhere. Therefore, each system must be designed in such a way that any particular compromised system should not be enough for an attacker to reach their goal. See a practical implementation of this principle in the ReARM Blog.
2. Different Pipelines Must Not Share Credentials
Consider a traditional GitHub Actions publishing pipeline for a library. All workflow YAML files are located in one directory. One of them does initial triage, another one does the actual build, yet another one publishes the final artifact.
Given how GitHub Actions works, all secrets are shared between all these files. So a significant compromise in the triage pipeline may also expose publisher credentials and allow an attacker to publish a malicious payload without ever touching the actual publishing pipeline.
How to mitigate this? Follow the spirit of the first principle: make sure that different pipelines don’t share the same secret pools – which in the case of GitHub Actions means they must live in different repositories.
3. Staging Area is a Must
This again expands on the first principle. Specifically, there must be a point between build and publish pipelines at which deliverables can be assessed. Having a single pipeline that builds and pushes artifact in one shot means that an attacker who manages to compromise such a pipeline can also publish something without further safeguards.
4. Assume Unsafe or Malicious Inputs
This is especially important for public repositories. A lot has been said about pull_request_target on GitHub Actions (that should never be used), but an attacker may also try to compromise any input that a legitimate action accepts. There are linters that can and should be used here, e.g. Zizmor for GitHub Actions.
5. Pin All Dependencies Consumed by CI
Supply chain attacks of all kinds are on the rise, and CI is not a place for experiments. All dependencies, including CI’s own dependencies and any build dependencies, should be pinned by digests to the maximum extent possible.
This includes referencing CI pipelines by hash, installing only based on lockfiles where possible, pinning any Docker base images by digests.
6. Attest, Sign, Verify
CI deliverables and artifacts must be signed. Downstream stages of CI must verify signatures before proceeding. CD must verify everything before deploying. That’s the core principle.
A lot of sources now discuss attestations rather than signatures as the core element in this process. Essentially, attestation is a statement about something; that statement can be implicit or explicit based on the context.
An example of implicit attestation is signing a build with public Sigstore from GitHub Actions. Verification of such a signature means that the build was produced by the specific GitHub Action. An explicit attestation would also contain a specific statement being signed (e.g., this SBOM belongs to this build). In many cases in practice an implicit attestation is enough for supply chain verification purposes; however explicit attestation systems such as in-toto can bring additional benefits in certain scenarios.