The programThe DevSecOps ProgramPart 3 of 5
  1. Foundation — identity, secrets & repo hygienePublished
  2. Shift Left — security in the pipelinePublished
  3. 3Supply Chain — dependencies, IaC & artefactsYou're reading this
  4. Runtime & Response — detect and reactComing Oct 2026
  5. Govern & Mature — policy, metrics & evidenceComing Nov 2026
DevSecOps8 Sep 202616 min read

DevSecOps Program · Part 3 — Secure the Supply Chain: Dependencies, IaC & Artefacts

Part 3 of the DevSecOps Program: scan IaC before provisioning, generate an SBOM for every build, sign and verify artefacts, and enforce it all with policy-as-code.

This is Part 3 of the DevSecOps Program — the phase where you stop trusting your build and start being able to prove it. Part 2 wired three scanning layers into CI as merge gates developers trust: SAST reading your source, SCA matching your dependencies against known CVEs, and secret scanning catching credentials in the diff. Those gates all point inward — they inspect the code you and your team wrote and pulled in. Crucially, Part 2 also left them attached to real enforcement: a security workflow registered as a required status check inside the branch-protection Terraform that Part 1 established, so a red gate blocks a merge. This part extends that same enforcement outward, to the two ends of the build nobody looked at yet: what goes in (the infrastructure you provision and the packages you assemble) and what comes out (the artefact you ship). The through-line is one sentence: know exactly what is in every build, and be able to prove it has not been tampered with. Get that right and "are we affected by CVE-X?" becomes a query you run in seconds, not an all-hands investigation — and "is this the artefact we actually built?" becomes a signature check, not an act of faith.

Scope of this phase

Every part of this program is scope-bounded so you know exactly when you are done and are not tempted to boil the ocean.

  • In scope: IaC scanning (Terraform, CloudFormation and Kubernetes manifests scanned before provisioning with Checkov or tfsec); SBOM generation (a machine-readable inventory of every build via Syft / CycloneDX); artefact signing and provenance (signing container images with Cosign / Sigstore and verifying the signature at deploy, with a nod to SLSA provenance levels); and policy-as-code (rules expressed as machine-executable policy with OPA/Rego and Conftest, run as a deploy gate that also emits evidence).
  • Explicitly out of scope (later parts): DAST, runtime monitoring and incident response (Part 4), and continuous control monitoring, metrics and audit-evidence aggregation (Part 5). Here you secure the inputs and outputs of the build, not the running system's behaviour.
  • Definition of done: the exit checklist at the end of this part. When every box is ticked, misconfigured infrastructure is caught in review before it exists, every build carries an SBOM, your images are signed, deploy verifies those signatures, and at least one policy runs as a gate that both blocks and produces evidence.

The order matters. None of this is safe or sensible before Parts 1–2. Signing an artefact built from an unscanned, unreviewed pipeline just gives you a verifiable chain of custody for a compromised object — provenance is only as good as the gates behind it. With source and dependencies already gated, the supply-chain controls have something trustworthy to attach to.

Two ends of the build

Part 2 secured the middle — your code and its libraries. Supply-chain security is about the two ends nobody gated yet.

  • The input end is everything that shapes the build before your artefact exists: the infrastructure you declare (a Terraform bucket, a security group, an IAM role) and the base images and packages that get assembled into the thing you ship. A public S3 bucket or an over-broad IAM policy is a vulnerability you provisioned, not one you coded — and no SAST rule will catch it, because it lives in HCL and YAML, not in application source.
  • The output end is the artefact itself once built: the container image that lands in a registry and later gets pulled to a cluster. Between "we built it" and "the cluster ran it" sits a supply-chain gap — a registry compromise, a mistaken tag, a malicious mirror — where the object you deploy may not be the object you built. Signing and verification close that gap.

The four controls in this phase map cleanly onto those ends: IaC scanning and SBOMs govern the input, signing and provenance govern the output, and policy-as-code is the enforcement layer that runs across both.

The defence-in-depth tool stack

Code
SemgrepCodeQLESLint security
Dependencies
DependabotTrivyOSV-Scanner
Secrets
GitleaksTruffleHogVault / KMS
Infrastructure
CheckovtfsecOPA / Conftest
Containers
TrivyCosignDistroless
Runtime
FalcoCloud-native SIEMWAF

Pick one tool per layer to start — coverage matters more than tool count. All are open-source or have free tiers.

Read the stack as defence-in-depth: Part 2 populated the Code, Dependencies and Secrets layers; this phase adds the Infrastructure and Containers layers; Part 4 will add Runtime. Each layer is a distinct class of problem with its own tool and its own gate — coverage across layers, not depth in any single one, is what makes the build trustworthy end to end.

Infrastructure-as-Code scanning

If you provision with Terraform, CloudFormation or Kubernetes manifests, your infrastructure is code — and it carries the same failure modes as any other code, except the blast radius is a live cloud account rather than a single request. A public storage bucket, a security group open to 0.0.0.0/0 on port 22, an IAM role with *:*, an unencrypted volume, a container set to run as root: every one of these is a misconfiguration expressed in plain text, sitting in a file, reviewable in a pull request before it becomes real.

Checkov and tfsec scan that IaC statically, before terraform apply ever runs, and flag misconfigurations against a large library of built-in policies. The point is timing: you catch the public bucket in the diff, at review time, when fixing it is a one-line change — not in production, after a scanner (or an attacker) finds it exposed. This is exactly the shift-left logic of Part 2, applied to the infrastructure layer, and it ties into the same enforcement machinery: the IaC scan becomes a job in your security workflow, registered as a required status check in the branch-protection Terraform from Part 1. A high-severity misconfiguration flips the job's exit code, the check goes red, and the merge is blocked — same mechanism as the SAST and SCA gates, new layer.

Start narrow, exactly as you did with SAST: block on the high-severity, unambiguous misconfigurations (public exposure, wide-open ingress, missing encryption, over-broad IAM) and let the rest report while you tune. An IaC gate that fires only on things that are genuinely dangerous earns trust; one that blocks a merge over a missing tag gets routed around.

SBOM — a bill of materials for every build

An SBOM (Software Bill of Materials) is a machine-readable inventory of everything inside a build: every package, every version, every layer of a container image, down through the transitive dependencies your developers never named directly. Syft generates one in seconds; CycloneDX is the standard format to emit it in (SPDX is the other). You produce the SBOM as a build step and store it as an artefact alongside the image it describes.

The value is not the file — it is what the file makes instant. When the next headline CVE lands in a widely-used library, the question every customer, auditor and board member asks is "are we affected?" Without SBOMs, answering means an archaeology dig across repos, lockfiles and running containers, taking days and still ending in "we think so." With a stored SBOM per build, it is a query: grep your inventory for the package, get an exact list of which builds and which versions are affected, and move straight to remediation. Part 2's SCA scanning tells you about vulnerabilities at build time; the SBOM lets you answer the same question retroactively, for a CVE disclosed long after the build shipped — which is when most of the panic actually happens.

An SBOM is also increasingly a contractual and regulatory expectation rather than a nicety — a documented, current inventory of components is precisely the kind of evidence NIS2 supply-chain obligations and modern procurement questionnaires ask for. Generating one automatically per build turns a recurring manual scramble into a by-product of the pipeline.

Artefact signing and provenance

You have scanned the inputs and inventoried the build. One gap remains: between the moment CI produces an image and the moment a cluster pulls and runs it, how does the cluster know the image is the one you built — and not a substitute pushed by someone who compromised the registry, or a wrong tag pulled by mistake?

Signing closes that gap. Cosign (part of the Sigstore project) signs a container image at the end of the build, producing a cryptographic signature bound to the image's digest. At deploy time you verify that signature; if it does not check out — the image was tampered with, substituted, or never signed by you — the deploy is rejected. This is the output-end counterpart to everything before it: scanning tells you the artefact is clean, signing proves it is yours and unaltered.

cosign verify — reject anything not signed by us
# fails (non-zero exit) unless the image was signed by our identity
cosign verify \
  --certificate-identity "ci@your-org.example" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
  registry.example.com/app@sha256:<digest>

Verification pins to the digest, not a mutable tag — latest can be repointed, a digest cannot. Sigstore's keyless signing ties the signature to a CI workload's OIDC identity, so you are not managing long-lived private keys; the identity that signed is the identity you verify against.

Signing pairs naturally with provenance — a signed, verifiable record of how an artefact was built: which source commit, which builder, which steps. SLSA (Supply-chain Levels for Software Artifacts) frames this as a ladder: lower levels ask for a documented build process and generated provenance, higher levels for a hardened, tamper-resistant build platform. You do not need the top rung to start. Signing images and attaching build provenance is already a large step up from "we deploy whatever is in the registry," and it moves you onto the ladder — which is where the maturity conversation with auditors and enterprise customers actually begins.

Policy-as-code — the gate that also emits evidence

The three controls above each produce a signal. Policy-as-code is what turns those signals into enforcement you can express, version and prove. Instead of a rule living in a runbook or a reviewer's head — "we don't run containers as root," "everything must be encrypted," "only images from our approved registry" — you write the rule as machine-executable policy and run it as a gate.

OPA (Open Policy Agent) with its Rego language is the general-purpose engine; Conftest runs Rego policies against structured config files (Kubernetes manifests, Terraform plans, Dockerfiles) right in the pipeline. A policy is a small, testable, reviewed file in your repo:

policy/deny-root.rego
package main

# block any Kubernetes container that can run as root
deny[msg] {
    input.kind == "Deployment"
    c := input.spec.template.spec.containers[_]
    not c.securityContext.runAsNonRoot
    msg := sprintf("container '%s' must set runAsNonRoot: true", [c.name])
}

Because the rule is code, it does two jobs at once. As a gate, Conftest evaluates it in CI and exits non-zero when a manifest violates it — blocking the deploy exactly like the Part 2 scanners block a merge. As evidence, that same pass/fail is a timestamped, attributable record that the control was enforced on this change — which is precisely what an ISO 27001 or SOC 2 auditor wants to see: not a policy document asserting "we require non-root containers," but a machine record proving the rule ran and held on every deploy.

Here is a focused workflow tying the input-end and output-end controls together — IaC scan, SBOM, sign — into one supply-chain job:

.github/workflows/supply-chain.yml
name: supply-chain
on: [pull_request]
jobs:
  secure-build:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      id-token: write          # keyless signing needs the OIDC token
    steps:
      - uses: actions/checkout@v4

      - name: IaC scan (block on high severity)
        uses: bridgecrewio/checkov-action@v12
        with: { soft_fail: false }   # a HIGH misconfig fails the job => merge blocked

      - name: build image
        run: docker build -t "$IMAGE" .

      - name: generate SBOM
        uses: anchore/sbom-action@v0
        with: { image: "${{ env.IMAGE }}", format: cyclonedx-json }

      - name: sign image (keyless)
        run: cosign sign --yes "$IMAGE"

That single job is the phase in miniature: it refuses to proceed past a dangerous misconfiguration, inventories what it built, and signs the result — and its job name is the context you register in branch protection, turning the whole thing into a merge requirement rather than an optional courtesy.

Gate versus informational, at this stage

As in Part 2, not everything you scan should block on day one. A sane starting posture for the supply-chain layers:

ControlGate (blocks merge/deploy)Informational (reports only)
IaC scanningPublic exposure, open ingress, over-broad IAM, missing encryptionStyle/tagging findings; low-severity policy checks under evaluation
SBOMMissing SBOM for a release buildComponent-count deltas; licence notes
Signing / verifyUnsigned or signature-mismatched image at deployProvenance completeness below target SLSA level
Policy-as-codeRoot containers, unencrypted resources, unapproved registriesNew policies running in warn mode while they prove out

The principle carries straight over from Part 2: block on the serious and the certain; report on the rest. A public bucket, an unsigned image and a root container are unambiguous and actionable — gate them. A newly written Rego policy still proving its signal runs in warn mode until it earns promotion. Revisit the table each quarter and move controls from informational to gating as they prove out.

Definition of done — Supply Chain exit checklist

You are ready for Part 4 when every one of these is true:

  • IaC scanned before provisioning: Checkov or tfsec runs on every pull request that touches Terraform, CloudFormation or Kubernetes manifests, and blocks on high-severity misconfigurations.
  • Wired into the same enforcement: the IaC scan is a job in your security workflow and a required status check in the branch-protection Terraform from Part 1 — the merge is blocked when it is red.
  • SBOM per build: every build generates a CycloneDX (or SPDX) SBOM via Syft and stores it as an artefact, so "are we affected by CVE-X?" is a query, not an investigation.
  • Images signed: container images are signed with Cosign / Sigstore at build time, pinned to the digest.
  • Signatures verified at deploy: deploy verifies the signature and rejects unsigned or mismatched artefacts — signing without verification is not a control.
  • At least one policy-as-code gate: an OPA/Rego policy runs via Conftest as a deploy gate (no root containers, encryption required, or approved registries only) — and its pass/fail is retained as evidence.
  • Provenance on the ladder: build provenance is generated and attached, with a target SLSA level identified even if not yet at the top.

Tick every box and you can now state, and prove, exactly what goes into every build and that it has not been tampered with on the way out — and every one of these controls emits the pass/fail record that ISO 27001, SOC 2 and NIS2 supply-chain requirements ask for, produced continuously by the pipeline rather than reassembled before an audit.

What's next

Part 4 — Runtime & Response turns from the build to the running system. Everything so far — Parts 1 through 3 — has hardened what you ship and proved what it contains; Part 4 asks what happens once it is live: DAST against running applications, runtime monitoring and detection, and a rehearsed incident-response path for when something gets through anyway. The signed, inventoried artefacts you produce in this phase are exactly what runtime controls observe and, when needed, trace an incident back to. It ships next month.

Securing the supply chain is also where evidence stops being a document and becomes a stream: ISMShed ingests your IaC-scan results, SBOMs, signing verifications and policy-as-code pass/fail history and maps them to controls across ISO 27001, ENS, NIS2, DORA, SOC 2 and GDPR — so "we know and can prove what is in every build" is a claim backed by machine records, not screenshots. If you would like experienced hands to stand up IaC gating, SBOM generation and a signing-and-verification path against your own stack — and to draw the block-versus-report line where it fits your risk — Axelia's DevSecOps and GRC consultants run the program alongside you. Secure the supply chain well, and the runtime phase watches artefacts you can already vouch for.

Coming soon
Runtime & Response — detect and react
Coming Oct 2026

Talk to an expert

Don't wait for the guide. Book a call with our AI-CISO team and get a tailored roadmap to ISO 27001, NIS2, ENS or DORA compliance.