CI/CD Pipeline & Supply Chain Security
Table of Contents#
- Why the Pipeline Itself Is a Target
- The SolarWinds Wake-Up Call
- CI/CD Pipeline Threat Model
- Hardening the Pipeline Itself
- Third-Party Actions and Dependency Pinning
- Infrastructure as Code Scanning
- IaC Scanning in Practice: tfsec and Checkov
- What Is a Software Supply Chain, Really
- SBOM — Software Bill of Materials
- Generating and Using an SBOM
- Artifact Signing with Sigstore/Cosign
- SLSA — Supply Chain Levels for Software Artifacts
- Provenance — Proving Where an Artifact Came From
- Putting It All Together: A Secure Pipeline Blueprint
- Common Mistakes
- Worked Practice Problems
- Summary and What's Next
Why the Pipeline Itself Is a Target#
Every tutorial in this series so far has focused on securing the application — its code, its dependencies, its containers, its secrets. This part asks a different question: what if the attack isn't against your application at all, but against the machinery that builds and deploys it?
Diagram
Why this is such a high-leverage attack for a real adversary: if you compromise one popular library's build process, or one company's CI/CD pipeline, you don't need to individually breach each of their thousands of downstream customers — the customers will voluntarily, trustingly pull in the poisoned artifact themselves, believing it's legitimate. This is exactly the reasoning behind supply chain attacks, and it's why this has become one of the most actively discussed areas in all of security over the last several years.
The SolarWinds Wake-Up Call#
Worth knowing by name as the canonical real-world example that made supply chain security a mainstream, board-level concern (disclosed in December 2020).
Diagram
The core lesson worth stating explicitly in an interview: "SolarWinds demonstrated that even a company with mature application security can be catastrophically compromised through their build/CI pipeline specifically — the attackers never needed to find a bug in SolarWinds' actual product code, because they compromised the process that builds and signs the product instead. This is exactly why supply chain security (SBOM, provenance, artifact signing) has become a first-class discipline, not an afterthought."
CI/CD Pipeline Threat Model#
Applying the STRIDE framework from Part 1 specifically to a CI/CD pipeline:
Diagram
The "Pwn Request" Pattern — A Real, Specific Attack#
A genuinely important, specific pattern worth knowing by name: many CI systems (GitHub Actions being the most commonly cited) have a workflow trigger (pull_request_target) that runs with access to the target repository's secrets, even for a pull request submitted from a completely untrusted fork.
Diagram
Why this matters and how to prevent it: pull_request_target should only ever be used when the workflow does NOT check out and execute the untrusted fork's own code — if it does need to run the PR's code (e.g., to run tests), use the regular pull_request trigger instead, which deliberately runs with no access to repository secrets for exactly this reason. This is a concrete, specific, real-world gotcha worth citing to demonstrate genuine hands-on pipeline security knowledge.
Hardening the Pipeline Itself#
A practical checklist of CI/CD-specific hardening measures:
Diagram
# Require signed, GPG-verified commits on a protected branch # (a GitHub branch protection setting, shown here via the API) gh api repos/OWNER/REPO/branches/main/protection \ --method PUT \ -f required_signatures=true
Third-Party Actions and Dependency Pinning#
Every CI/CD pipeline pulls in third-party building blocks — GitHub Actions from the Marketplace, Jenkins plugins, published container base images. Each one is effectively a dependency, with exactly the same trust and versioning concerns as the SCA discussion from Part 2 — but often overlooked because it "feels like infrastructure, not code."
Diagram
# BAD: mutable tag — trusts whatever "v4" points to, forever - uses: actions/checkout@v4 # BETTER: pinned to an exact, immutable commit SHA - uses: actions/checkout@8f4b7f84864484a7bde019a09fc0e2d5b7c8f5f2
A concrete, real-world incident worth citing: in 2024, a popular GitHub Action (tj-actions/changed-files) was compromised, with a malicious update pushed under an existing version tag, causing CI pipelines across many organizations that used the mutable @vX reference (rather than a pinned commit SHA) to unknowingly execute malicious code and leak secrets. This is the exact "pipeline itself is a supply-chain target" lesson made concrete and current.
Infrastructure as Code Scanning#
Just as SAST scans application code (Part 2), IaC scanning tools scan Terraform, CloudFormation, and Kubernetes manifests for known misconfiguration patterns — before that infrastructure is ever actually provisioned.
Diagram
Why this is such high-leverage shift-left security: a misconfigured cloud resource (a publicly readable S3 bucket, an overly permissive security group, an unencrypted database) is one of the single most common real-world sources of actual breaches — and IaC scanning catches it before the resource is ever created, rather than discovering it after the fact via a cloud security posture scan of already-live infrastructure.
IaC Scanning in Practice: tfsec and Checkov#
# tfsec — Terraform-specific static analysis tfsec . # Checkov — broader coverage (Terraform, CloudFormation, # Kubernetes, Dockerfile, and more, all in one tool) checkov -d . # Run Checkov specifically against Kubernetes manifests checkov -d ./k8s-manifests --framework kubernetes # CI gate example checkov -d . --compact --quiet --check CKV_AWS_20,CKV_AWS_21
A realistic finding from Checkov against a Terraform S3 bucket resource:
Check: CKV_AWS_20: "S3 Bucket has an ACL defined which allows public READ access"
FAILED for resource: aws_s3_bucket.data
File: /main.tf:12-16
12 | resource "aws_s3_bucket" "data" {
13 | bucket = "my-app-data"
14 | acl = "public-read"
15 | }
# The fix — remove public ACL, explicitly block public access resource "aws_s3_bucket" "data" { bucket = "my-app-data" } resource "aws_s3_bucket_public_access_block" "data" { bucket = aws_s3_bucket.data.id block_public_acls = true block_public_policy = true ignore_public_acls = true restrict_public_buckets = true }
What Is a Software Supply Chain, Really#
A helpful, complete mental model of everything involved in getting code from a developer's keyboard into a running production system — worth being able to draw from memory.
Diagram
Every single arrow in this diagram is a potential attack point — this is exactly why supply chain security requires layered controls at every stage, not just one: SCA for dependencies (Part 2), image scanning for artifacts (Part 3), pipeline hardening for the build system (above), and the two remaining pieces covered next — knowing exactly what's inside an artifact (SBOM) and proving where it actually came from (signing and provenance).
SBOM — Software Bill of Materials#
An SBOM is a complete, formal, machine-readable inventory of every single component — every library, every transitive dependency, every version — that went into building a piece of software. Think of it as an ingredients label for software.
Diagram
Why this matters practically, tied directly back to Part 2's transitive dependency discussion: imagine a brand-new, severe CVE is announced for log4j (the actual real-world 2021 "Log4Shell" scenario). Without an SBOM, answering "which of our hundreds of applications actually use this library, even transitively, and in which specific versions?" could take days or weeks of manual, panicked investigation across every team. With an SBOM already generated and stored for every artifact, that question becomes a simple, instant search.
Diagram
Generating and Using an SBOM#
# Generate an SBOM for a container image, using Trivy # (in CycloneDX format, one of the two dominant SBOM standards) trivy image --format cyclonedx --output sbom.json myapp:1.2.3 # Or in SPDX format (the other dominant standard) trivy image --format spdx-json --output sbom.spdx.json myapp:1.2.3 # Generate an SBOM directly from source, using Syft # (another very widely used SBOM generation tool) syft myapp:1.2.3 -o cyclonedx-json > sbom.json # Later — search a stored SBOM for a specific vulnerable component grep -i "log4j" sbom.json
The two dominant SBOM formats worth knowing by name: CycloneDX (originated in the OWASP ecosystem, strong security-tooling focus) and SPDX (originated in the Linux Foundation, strong license-compliance focus, now an ISO standard). Both are widely supported by modern tooling; knowing both names (even without deep format-level detail) is enough for most interview purposes.
Artifact Signing with Sigstore/Cosign#
An SBOM tells you what's inside an artifact. Signing tells you where it actually came from, and that nobody tampered with it after it was built.
Diagram
# Sign a container image (using keyless signing via Sigstore's # Fulcio + Rekor — no long-lived private key to manage or leak!) cosign sign myregistry.io/myapp:1.2.3 # Verify a signature before deploying cosign verify \ --certificate-identity="https://github.com/myorg/myapp/.github/workflows/build.yml@refs/heads/main" \ --certificate-oidc-issuer="https://token.actions.githubusercontent.com" \ myregistry.io/myapp:1.2.3
Why "keyless signing" is worth understanding specifically: traditional signing requires managing a long-lived private key — which is itself a secret that can leak (exactly the Part 4 problem, applied to signing). Sigstore's keyless model instead issues a very short-lived certificate tied to a verified identity (e.g., "this specific GitHub Actions workflow, triggered from this specific repo"), using an OIDC token as proof of identity, and records the signing event in a public, tamper-evident transparency log (Rekor) — eliminating the long-lived-key leak risk entirely, while still providing strong, verifiable provenance.
SLSA — Supply Chain Levels for Software Artifacts#
SLSA (pronounced "salsa," originated at Google, now a broader open industry framework) defines a tiered maturity model specifically for supply chain integrity — genuinely useful as a structured way to describe "how mature is our build security" in an interview, similar in spirit to the DevSecOps maturity model from Part 1.
Diagram
A great, concrete interview line: "SLSA gives a shared vocabulary for supply chain maturity, similar to how CVSS gives a shared vocabulary for vulnerability severity. Level 1 is basically 'we have a real build script, not a person manually typing commands.' Level 3-4 means the build platform itself is hardened against tampering, with signed, verifiable provenance for every artifact — that's the SolarWinds-style attack this framework is specifically designed to prevent."
Provenance — Proving Where an Artifact Came From#
Provenance is a signed, verifiable statement answering: "exactly which source code, built by exactly which pipeline, at exactly what time, produced this specific artifact?"
Diagram
Why this matters, tying directly back to the SolarWinds lesson: provenance lets a downstream consumer (or an automated policy gate) verify "this artifact genuinely came from the source code and build process I trust" — rather than blindly trusting an artifact simply because it's sitting in the right registry with the right name and tag, which is exactly the trust assumption a build-system compromise like SolarWinds exploited.
Putting It All Together: A Secure Pipeline Blueprint#
A realistic, complete CI/CD pipeline incorporating every concept from Parts 2, 3, 4, and 5 of this series into one coherent flow:
Diagram
This single diagram is genuinely one of the strongest artifacts you can have ready for a DevSecOps interview — being able to walk through it stage by stage, naming the specific tool category and threat each stage addresses, demonstrates the full breadth of this entire series in one coherent, memorable picture.
Common Mistakes#
| Mistake | Why It's Wrong | Fix |
|---|---|---|
| Referencing third-party CI actions/plugins by a mutable version tag | The maintainer (or an attacker who compromises their account) could push malicious code under that same tag later, silently | Pin to an exact, immutable commit SHA |
Using pull_request_target when checking out untrusted fork code | Exposes repository secrets to arbitrary code from an untrusted contributor ("pwn request") | Use pull_request (no secret access) unless you specifically know why pull_request_target is needed and have excluded untrusted code execution |
| Treating IaC as "just config," exempt from the same scrutiny as application code | Misconfigured infrastructure (public buckets, open security groups) is one of the most common real breach causes | Scan IaC (tfsec, Checkov) exactly like application code, in the same pipeline |
| No SBOM generated for artifacts | Answering "are we affected by this new CVE" becomes a days-long manual audit instead of an instant search | Generate and store an SBOM for every built artifact |
| Deploying artifacts with no signature verification | A tampered or unauthorized artifact could be deployed with no way to detect it | Sign artifacts (cosign/Sigstore) and enforce signature verification at deploy time via a policy gate |
| Treating supply chain security as "someone else's problem" (the vendor's) | Your own build pipeline is just as much a target as any vendor's — see SolarWinds | Apply the same hardening (isolated runners, least privilege, signed provenance) to your own pipeline |
Worked Practice Problems#
Problem 1: Your CI pipeline references a third-party GitHub Action using some-org/some-action@v2. The action's maintainer account is later compromised, and the attacker pushes malicious code under the existing v2 tag. What happens to your pipeline, and how would pinning have prevented it?
Answer: Because @v2 is a mutable reference, every subsequent CI run automatically pulls and executes whatever code the tag currently points to — meaning your pipeline would silently begin executing the attacker's malicious code the very next time it ran, with no change on your end at all, and with access to whatever secrets/permissions that step of the pipeline had. Pinning to an exact commit SHA (@a1b2c3d...) instead guarantees your pipeline always runs the exact code you originally reviewed and approved — a tag can be moved by the maintainer (or an attacker who compromises them), but a specific commit hash cannot be silently changed underneath you.
Problem 2: A new critical CVE is announced in a widely-used logging library. Your organization has 300 microservices. How would having SBOMs for every deployed artifact change your response time and process compared to not having them?
Answer: Without SBOMs, determining which of the 300 services actually use the vulnerable library — including transitively, buried several dependency levels deep — would require each team manually auditing their own dependency tree, likely taking days and prone to human error/omission. With SBOMs already generated and centrally stored for every artifact, this becomes a single, instant search across all 300 SBOMs for the specific vulnerable library and version range, immediately producing a precise, complete list of affected services — turning a days-long, error-prone manual fire drill into a minutes-long, reliable query.
Problem 3: A deployment pipeline currently pulls container images directly from a public registry with no signature verification, trusting the image purely because it has the expected name and tag. What specific attack does this leave the organization open to, and how would artifact signing close the gap?
Answer: This leaves the organization open to a tampered or maliciously substituted image being deployed — an attacker who compromises the registry, or performs a supply-chain-style substitution earlier in the pipeline, could publish a malicious image under the exact expected name/tag, and the deployment pipeline would have no way to distinguish it from the legitimate one. Enforcing signature verification (e.g., via cosign, requiring the image be signed by the organization's actual trusted CI pipeline identity) closes this gap — a deploy-time policy gate would reject any image that isn't signed by the expected, verified source, regardless of whether its name/tag looks correct, directly mirroring the exact trust failure that enabled the SolarWinds attack.
Summary and What's Next#
- Attackers increasingly target the build pipeline itself, not just the application — compromising one build system can poison software trusted by thousands of downstream victims, as demonstrated concretely by the SolarWinds attack.
- CI/CD pipelines need their own threat modeling — watch specifically for the "pwn request" pattern (
pull_request_targetexposing secrets to untrusted fork code) and always use least-privilege, ephemeral, isolated build credentials/environments. - Third-party CI actions/plugins are dependencies too — pin them to exact commit SHAs, never mutable version tags, exactly as demonstrated by the real 2024
tj-actions/changed-filescompromise. - IaC scanning (tfsec, Checkov) catches misconfigured infrastructure (public buckets, open security groups) before it's ever provisioned — one of the highest-leverage shift-left security practices, since misconfiguration is a leading real-world breach cause.
- An SBOM (Software Bill of Materials — CycloneDX or SPDX format) is a complete, machine-readable inventory of every component in an artifact, turning "are we affected by this new CVE" from a days-long manual audit into an instant search.
- Artifact signing (cosign/Sigstore, often using modern "keyless" signing tied to a verified CI identity) proves an artifact genuinely came from your trusted build process and wasn't tampered with — directly closing the exact trust gap SolarWinds exploited.
- SLSA provides a shared, tiered maturity model (Level 1 through 4) for describing and improving overall supply chain integrity, similar in spirit to CVSS for vulnerability severity or the DevSecOps maturity model from Part 1.
- A fully secure pipeline layers every concept from this entire series — secret scanning, SAST/SCA, IaC scanning, image scanning, SBOM generation, signing, DAST, and policy gating — at the appropriate stage, cheapest checks first.
Continue to Part 6 (06-compliance-and-cheatsheet.md) for compliance frameworks (SOC 2, ISO 27001, PCI-DSS, GDPR), and a consolidated master cheat sheet tying every tool and concept from this entire DevSecOps series together in one quick-reference document.