skip to content

What does actions/attest-build-provenance produce, and what does gh attestation verify prove?

level: seniorimportance: must knowfreq 39%

answer

  1. Signed claim about bytes, not about safety
  2. Three permissions, one of them mints an identity
  3. No long-lived signing key exists
  4. The verifier must say who it expected

basics

~20 s

The action produces a signed provenance statement binding an artifact's digest to the repository, workflow, ref and commit that built it. gh attestation verify checks the signature and that the digest matches an attestation from the repository or owner you name — nothing about the artifact's contents.

solid answer

~50 s

`actions/attest-build-provenance` runs in the workflow that produced the artifact. You give it `subject-path` (or a digest), and the job needs `id-token: write` for OIDC, `attestations: write` to store the result, and `contents: read`. It emits an in-toto provenance statement — SLSA provenance predicate — recording the artifact's digest alongside the repository, workflow, ref, commit SHA and builder, then signs it with a short-lived Sigstore certificate keyed to the workflow's OIDC identity. No long-lived signing key exists. On the consuming side, `gh attestation verify ./app.tar.gz --repo my-org/my-service` recomputes the digest, fetches the attestation, checks the signature chain, and confirms the signer identity matches the repository you asserted. What that proves: *this exact byte sequence came out of that workflow, in that repository, at that commit.* What it does not prove: that the code is safe, that dependencies are clean, or anything at all if the verifier omits the expected identity.

code

yaml · 13 lines
yaml
jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      attestations: write
      contents: read
    steps:
      - uses: actions/checkout@v4
      - run: ./build.sh
      - uses: actions/attest-build-provenance@v2
        with:
          subject-path: dist/app.tar.gz

go deeper

for a junior

Recall that a GitHub workflow can attach a signed record of what built an artifact, and that a command-line check confirms a downloaded file matches that record.

for a middle

Explain the mechanism: the subject digest, the provenance predicate naming repository, workflow, ref and commit, the OIDC identity, and the permissions the job needs.

for a senior

Show where verification belongs and what it must pin — the expected repository or owner at the deployment gate — and state plainly what provenance does not prove.

for a principal

Own the trust boundary: which artifacts require verified provenance before deploy, what runner and branch controls the claimed assurance level actually depends on, and how the policy is enforced rather than encouraged.

## The question provenance answers A build produces bytes. Later, someone deploys bytes. Provenance is the cryptographic answer to "did these bytes actually come out of the pipeline we think they did, from the source we think they did?" It is a different question from "are these bytes safe", and conflating the two is the most common error in this area. ## What the action does `actions/attest-build-provenance` runs as a step in the workflow that built the artifact — that placement is the whole point, because it is what lets the signed claim be about *this* build. **Inputs.** `subject-path` pointing at the built file (globs allowed), or a `subject-digest` plus `subject-name` when the artifact lives elsewhere, such as a pushed container image identified by its digest. **Permissions.** Three, and candidates should be able to say why each: - `id-token: write` — mints the workflow's OIDC token, the identity everything else rests on. - `attestations: write` — stores the resulting attestation against the repository. - `contents: read` — the ordinary checkout. **Output.** An in-toto statement whose *subject* is the artifact's name and SHA-256 digest and whose *predicate* is SLSA provenance: the repository, the workflow file and ref, the commit SHA, the trigger, and the builder identity. It is signed through Sigstore using a short-lived certificate issued against the workflow's OIDC identity — "keyless" signing, meaning there is no private key in a secret for anyone to steal, and the certificate's subject encodes which workflow in which repository at which ref did the signing. ## What verification actually checks `gh attestation verify ./app.tar.gz --repo my-org/my-service` performs, in order: 1. Compute the SHA-256 digest of the local file. 2. Retrieve attestations recorded for that digest (from GitHub, or from a bundle for offline verification). 3. Validate the signature and its certificate chain. 4. Check that the certificate's identity matches the expectation you passed — `--repo` for a specific repository, `--owner` for any repository under an organisation. Step 4 is load-bearing and is where real deployments go wrong. Verification without a pinned expected identity degenerates into "somebody, somewhere, attested something" — an attacker with any GitHub repository can produce a validly signed attestation for their own artifact. **A verification that does not assert who you expected the signer to be is not a security control.** Pin the repository, and where it matters, pin the workflow too. ## SLSA levels, honestly SLSA (Supply-chain Levels for Software Artifacts) grades build integrity. Roughly: Build L1 means provenance exists; L2 means the provenance is signed and generated by a hosted build platform rather than asserted by the developer; L3 requires stronger isolation so the provenance cannot be forged by the build itself. GitHub positions artifact attestations produced this way on hosted runners at Build Level 2, with higher levels demanding additional isolation of the build and signing process. Say the level with that hedge; do not claim a level a plain workflow does not earn, and note that a self-hosted runner someone can log into changes the trust story materially. ## What it does not prove Be explicit, because interviewers push here: - **Not safety.** Provenance says who built it, not whether the code is vulnerable, whether dependencies are clean, or whether tests passed. - **Not source integrity.** If a malicious commit reached the branch, the build faithfully attests a malicious artifact. Provenance composes with branch protection and review; it does not replace them. - **Nothing, if unverified.** An attestation nobody checks at the deployment gate is decoration. - **Nothing about a different artifact.** The binding is to a digest. Rebuild and you get different bytes and a different attestation; "the same version" is not the same thing as the same digest. ## Where verification belongs At the point of consumption, not the point of production. The deployment step, the admission controller, or the installer verifies before the artifact runs. Verifying in the same pipeline that produced the artifact proves very little — the interesting adversary is one who substitutes bytes *between* build and deploy, which is precisely the window a digest-bound, identity-pinned check closes. ## The senior signal Anyone can quote the action name. The signal is in the second half: what the check pins, where it runs, and what it still does not tell you. A candidate who says "verify with `--repo` at the deploy gate, because otherwise any signed artifact passes" has thought about the adversary. A candidate who says "we generate attestations" has thought about the checkbox.

  • What does provenance not protect against?
    A compromised source. If a malicious commit lands on the branch, the build faithfully produces and attests a malicious artifact with perfectly valid provenance. Provenance composes with branch protection, review requirements and dependency controls — it certifies the path from source to binary, not the quality of the source.
  • How do you attest a container image rather than a file on disk?
    Push the image first, then attest by digest: pass the image's SHA-256 digest and its name instead of a path. The digest is the artifact identity in both cases, so a consumer verifies the digest they are about to run, not a tag — tags move, digests do not.
  • Why is keyless signing preferable to a signing key in a repository secret?
    A stored key is a long-lived secret that can be exfiltrated, copied to a laptop, or used from an unrelated workflow. Keyless signing derives a short-lived certificate from the workflow's OIDC identity, so the credential expires almost immediately and the certificate itself records which workflow in which repository signed.

saying these in an interview costs you the question

  • Believing an attestation shows the artifact has no vulnerabilities
  • Verifying without pinning the expected repository or owner
  • Thinking a signing key is stored in repository secrets
  • Verifying in the same pipeline that built the artifact
  • Assuming attestations bind to a version rather than a digest

context