Skip to main content
CICD-SEC-9

Improper Artifact Integrity Validation

Nothing checks that the artifact reaching production is the one that was built from the reviewed source.

If this is new to you

Here is a gap worth noticing: review, tests and scanning all apply to *source code*. Production runs an *artifact* — a container image, a package, a binary. If nothing ties the second to the first, then every control you built upstream can be bypassed by swapping the artifact.

And the swap leaves no trace where people look. Your repository is unchanged. Your pipeline is green. The thing running is simply not the thing you reviewed.

What to do about it

Start with a question you should be able to answer in seconds: which commit is production running right now? If answering it requires asking a colleague, that is the finding.

Fix it by labelling images with the commit, version and build date using the standard OCI annotation keys, so provenance travels with the artifact rather than living in somebody's memory. Then expose it at run time on a health endpoint.

Stop deploying `latest`. It is not a version, it is "whatever was pushed most recently by anyone", and it makes rollback meaningless because there is nothing to roll back to.

Finally, verify after deploying rather than trusting that the deploy command exited zero. Assert that the running service reports the version you meant to ship, and roll back automatically when it does not.

Going deeper

Signing is the step that turns provenance from a claim into evidence. Without a signature you are trusting your registry's access control; with one you have something that survives the registry being compromised.

An SBOM is the other half. When the next widely-reported vulnerability lands, the question you will be asked is "are we affected?", and an SBOM is the difference between answering in minutes and answering in days.

Words used on this page
Artifact
The built thing you actually deploy — image, package, binary — as opposed to the source it was built from.
Provenance
Evidence about where an artifact came from: which commit, which build, which system.
OCI annotations
Standard metadata keys for container images, including `revision` for the source commit and `version`.
SBOM
Software Bill of Materials — a machine-readable inventory of everything inside an artifact, so you can answer "is that vulnerable package in ours?"

Why this matters

Review, tests and scanning all apply to source code. Production runs an artifact. If no evidence ties the second to the first, every control upstream can be bypassed by substituting the artifact — and the substitution leaves no trace in the place people look, which is the repository.

What it looks like in practice

If any of these sound familiar, that is the point — most of them are things somebody did for a sensible reason on a busy afternoon.

  • Deploying `latest`, a tag that means 'whatever was pushed most recently by anyone'.
  • No record of which commit produced a running image.
  • Unsigned artifacts, so provenance rests on the registry's access control alone.
  • A deploy step that reports success without confirming what is actually running.
  • No SBOM, so a vulnerability disclosure cannot be answered with 'is it in ours?'

Check your own pipeline

Questions you can actually answer, rather than a maturity score. If you cannot answer one of them, that is itself the finding — and a more useful one than a number.

  1. Which commit is production running right now?

    If answering requires asking someone, that is the finding. Record it in the image and expose it at runtime.

    docker inspect --format '{{index .Config.Labels "org.opencontainers.image.revision"}}' <image>
  2. Does your deploy verify what it promoted, or only that a command exited zero?

    Assert after the fact that the running service reports the version you intended to ship.

    curl -s localhost:3000/api/health
  3. Can you prove an artifact came from your build system?

    Without a signature, you are trusting registry access control. Signing gives you a claim that survives a registry compromise.

How to fix it

  • Label images with the commit, version and build date using the OCI annotation keys, so provenance travels with the artifact.
  • Deploy immutable version tags and treat `latest` as a convenience pointer only.
  • Verify after deploying that the live service reports the exact version built, and roll back automatically when it does not.
  • Sign artifacts and generate an SBOM, so both provenance and contents can be answered later.

Did this get you to an answer?

No text box on purpose — please do not paste production logs anywhere

Risk taxonomy from OWASP Top 10 CI/CD Security Risks (Daniel Krivelevich and Omer Gil, September 2022, CC BY-SA 4.0). Explanations, checks, fixes and definitions written for this site.