skip to content

An in-toto statement lists three deploy bundles — how does a deploy job confirm it covers the one it holds?

level: seniorimportance: must knowfreq 50%

answer

  1. bind by content, not by identity
  2. hash what you are about to ship
  3. the array covers many artifacts independently
  4. names are labels chosen by the producer
  5. no matching entry means unattested

basics

~10 s

Hash the bundle in hand and look for that exact digest among the statement's subject entries. A statement can cover many artifacts, but it covers yours only if one entry's digest matches those bytes.

solid answer

~50 s

After verifying the signing envelope, the deploy job computes the digest of the file it is about to ship and searches the `subject` array for an entry whose `digest` map contains that value under an algorithm the policy allows. Multiple entries simply mean one build attested several artifacts; each is bound independently. Two failures are common and both are silent. First, matching on the subject `name` — filenames are labels, and a build can emit `service-a.zip` in ten different runs. Second, reasoning "the build produced attested output, so this output is attested", which is exactly the substitution an attacker with build-system access wants: swap one of the three bundles after the statement is generated and only a digest comparison catches it. If no entry matches, the artifact is unattested and the deploy stops — an unmatched subject is not a warning.

code

json · 10 lines
json
{
  "_type": "https://in-toto.io/Statement/v1",
  "subject": [
    { "name": "orders-fn.zip",   "digest": { "sha256": "1f0a...9c3d" } },
    { "name": "payments-fn.zip", "digest": { "sha256": "77be...02a1" } },
    { "name": "reports-fn.zip",  "digest": { "sha256": "c4d9...5e60" } }
  ],
  "predicateType": "https://slsa.dev/provenance/v1",
  "predicate": { "...": "..." }
}

go deeper

for a junior

Remember that a claim applies to an artifact only when the artifact's hash appears in the statement's subject, and that several artifacts can be listed in one statement.

for a middle

Explain the matching procedure precisely: compute the digest of the local file, search subject entries under an accepted algorithm, and fail closed on no match.

for a senior

Show where real pipelines break the binding — recompression, rendering, repacking between attestation and deploy — and how you fix that without loosening the comparison.

for a principal

Decide where the verification boundary sits across an estate: which steps are inside the trusted derivation chain, which must emit their own attestations, and who pays to close the gap.

## The scenario One build job produces three deploy bundles — say three functions of a serverless application — and publishes a single attestation whose statement enumerates all three in its `subject` array. A later deploy job handles one bundle at a time. It has the statement, and it has one zip file. Does the statement apply? ## The mechanical answer The subject array binds by **content**, not by identity, name, or provenance-by-association: 1. Verify the signing envelope first, so the statement's fields are trusted at all. 2. Compute the digest of the exact bytes about to be deployed — the file on disk in this job, not the one in the build log and not the one in the registry that you assume is the same. 3. Scan `subject` for an entry whose `digest` map holds that value under an algorithm your policy accepts. Hex comparison should be case-insensitive; algorithm choice should be explicit, because a statement may offer both a strong and a weaker hash and accepting whichever appears first hands the choice to the producer. 4. If exactly one entry matches, the claim in the predicate applies to these bytes. If none matches, the artifact is **unattested**. That is the whole binding. Everything else in the statement — including the friendly `name` on the matching entry — is commentary. ## The two failure modes worth naming **Matching by name.** The `name` field exists so humans reading a statement can tell which artifact is which. It is inside the signed payload, so a third party cannot edit it after the fact, but that is not the same as it identifying bytes: the *producer* chooses it, and it is stable across every run of the pipeline. A verifier that finds `name == "orders-fn.zip"` and stops has verified that a build once produced something called that. **Set-level reasoning.** "This build ran on a hardened builder and emitted an attestation, therefore what I am deploying is fine" collapses three artifacts into one trust decision. A build system compromise — an injected step, a poisoned cache, a tampered publish stage — does not need to defeat the signature to defeat that reasoning; it only needs to substitute one of the three files somewhere between attestation and deployment. Per-artifact digest matching is precisely what closes that gap, and it is the reason the subject is an array of digests rather than a build identifier. ## The mismatch that is subtler than a tampered file A related and very common defect: the statement's subject binds bytes **nobody actually consumes**. A packaged archive is attested, but the deploy step verifies against, or runs, something derived from it — an extracted tree, a rendered set of manifests, a repacked layer. The digests will never match, and the reflex fix is to relax the check "because the contents are the same". They may be; the point is that no attestation covers the derived bytes, so the derivation step sits outside the verified chain. There are two honest resolutions: verify the packaged artifact at the moment it is fetched and treat the derivation as inside your trust boundary (with the derivation code itself under change control), or have the deriving step emit and attest its own output so the thing you run is the thing that was signed. Loosening the comparison is not a third option. ## What to do when nothing matches Fail closed, and make the failure legible. A useful error names the digest computed, the algorithm used, and the digests the statement did offer, because the overwhelmingly common cause is not an attack but a pipeline that re-compresses, re-tags or normalises the artifact between attestation and deployment. Diagnosing that from a bare "verification failed" wastes hours, and teams that cannot diagnose it quickly are the teams that end up adding a bypass flag. ## What the check does *not* establish Matching a subject digest proves the predicate is about these bytes. It says nothing about whether the predicate's contents are true, whether the signer was authorised to make that claim, or whether the artifact is free of vulnerabilities. Those are separate questions answered by separate controls. The subject match is the join — it is what lets every other claim about this artifact be correlated — and it is worthless if performed on the wrong bytes.

  • The subject matches a packaged chart archive, but the platform deploys the rendered manifests. Is the check meaningful?
    Not for what actually runs. The rendered output is a different artifact with a different digest, and no statement covers it, so the binding points at bytes nobody consumes. Either verify the archive where it is fetched and bring the rendering step inside the trust boundary, or make the renderer emit and attest its own output. Relaxing the comparison just removes the control.
  • Filenames in our pipeline are stable and generated. Why not match on the subject name?
    Because stability is not identity. A name is chosen by the producer and repeats on every run, so it cannot distinguish this build's output from last week's or from a substituted file with the same name. The name is signed, which stops third-party edits, but the digest is what ties the claim to specific bytes.
  • A statement offers both sha256 and sha512 for your artifact. Which do you use?
    Whichever your policy names explicitly, and only that one. Iterating the digest map and accepting the first match lets the producer decide which algorithm you rely on, and a future weak algorithm appearing in that map becomes an accepted path silently. Pin the algorithm in the verifier and treat its absence as a failure.
  • How would you make a no-match failure debuggable without weakening it?
    Report the computed digest, the algorithm used, and the digests the statement actually offered, plus the artifact path. Almost every real occurrence is a pipeline that recompresses or re-tags between attestation and deploy. Opaque failures are what drive teams to add bypass flags, so a good error message is a security control in its own right.

saying these in an interview costs you the question

  • Matches the subject by file name instead of by digest
  • Treats 'the build is attested' as 'this artifact is attested'
  • Passes the check when no subject entry matches
  • Accepts whichever hash algorithm appears first
  • Hashes the build output rather than the bytes being deployed

context