skip to content

Your build script writes builder.id into its own SLSA provenance — why is that value worthless?

level: seniorimportance: must knowfreq 58%

answer

  1. ask who is making the claim
  2. the suspect signing its own alibi
  3. must be stamped outside the job
  4. signer must be authorized for it
  5. the build track grades exactly this

basics

~20 s

builder.id names the platform whose security you are relying on, so a value the job writes about itself is a self-assertion: a compromised step can name any platform. It counts only when stamped outside the job's reach.

solid answer

~50 s

`builder.id` is the trust anchor of the whole statement — it says which build platform's security properties you are leaning on. A value written by the build itself is a claim made by exactly the component whose integrity is in question, so a compromised step can name any platform it wants and the JSON looks identical. Two things make the field meaningful: the provenance is generated and signed **outside** the job's control, and the verifier confirms that the signing identity is authorized to speak for that `builder.id`. That is the SLSA build track ladder: L1 allows the build to produce its own provenance, so it guards against mistakes rather than adversaries; L2 has a hosted platform sign it, so tampering is detectable and forgery means compromising the platform; L3 isolates runs and keeps signing material out of reach of build steps. Same field, three trust properties.

go deeper

for a junior

Know that builder.id names the build platform and that provenance a build writes about itself is only as trustworthy as that build. The phrase to hold onto is that a suspect cannot sign their own alibi.

for a middle

Be ready to explain what has to be true for the field to mean anything: generation outside the job, and a signer authorized to speak for that identifier.

for a senior

Show you can place a real pipeline on the build track and say honestly which level it reaches, including the awkward answer that a self-emitted attestation caps you at L1 no matter how polished the JSON is.

for a principal

Own the separation between build integrity and input quality when a stakeholder proposes a SLSA level as the answer to a vulnerability question, and decide which builder identifiers your organisation will accept at all.

## Who is speaking Every field in a provenance predicate is an assertion, and the only question that matters is **who made it**. The predicate itself is a payload; it becomes evidence only when something signs it. So the useful frame is: is this statement about the build, produced by a party outside the build, or is it a statement the build made about itself? `builder.id` is where that distinction bites hardest, because it is the field a verifier keys its trust on. It is a URI standing for the transitive closure of the trusted build platform — shorthand for "everything I am relying on when I accept this artifact". If a build script emits its own statement containing `"builder.id": "https://trusted-platform.example/hosted"`, that string is precisely as trustworthy as the script. A compromised step — a malicious dependency's install hook, an injected command, a rogue plugin — writes whatever it likes, and produces JSON byte-identical in shape to the real thing. ## The two properties that rescue the field **Generation outside the job.** The provenance must be assembled by the platform's control plane, observing the run, rather than by code running inside the run. When the platform stamps `builder.id`, `metadata.invocationId` and the timestamps, those fields describe the run from outside; when the job writes them, they describe what the job wished to claim. The JSON shape is identical, which is why reading a provenance document tells you nothing about this on its own — you have to know how it was produced. **Binding the signer to the identifier.** Even platform-generated provenance is worthless if any key can sign a statement naming any builder. The verifier must check that the identity that signed is authorized to assert that particular `builder.id`. Skipping this is a classic hole: the signature verifies, the `builder.id` matches the expectation, and yet the statement was signed by an unrelated key that simply typed the right string. ## Where the SLSA build track puts the line The build track is graded precisely on this question. | Level | What it requires | What it stops | | --- | --- | --- | | L0 | nothing | nothing | | L1 | provenance exists and is distributed; the build may generate it itself | mistakes and accidents, not adversaries | | L2 | provenance is generated and signed by a hosted build platform | undetected tampering after the build; forging now requires compromising the platform | | L3 | the platform hardens runs so one cannot influence another or forge provenance; signing material is unreachable from user-defined steps | a compromised build step minting its own provenance | The self-written `builder.id` is therefore not a violation of L1 — it is the *definition* of L1's ceiling. What it cannot do is support a claim of L2 or L3, and it cannot survive an adversary who controls any part of the build. ## The same argument applies to metadata `metadata.invocationId`, `startedOn` and `finishedOn` inherit the property. A job-written `finishedOn` proves nothing and cannot be used to argue "this artifact predates the compromise". A platform-stamped `invocationId` is genuinely useful: during an incident it is the handle that takes you from an artifact in a registry back to the specific run, its logs and its triggering event. The value of the field is entirely a function of who wrote it. ## What the field still does not tell you A hardened builder and a perfect `builder.id` say the build platform did what it was asked, faithfully. They say nothing about whether what it was asked to do was safe. The inputs may include a compromised dependency; the source may contain a backdoor a reviewer missed; the recorded ref may be a branch nobody approved. Build integrity and input quality are different axes, and a candidate who answers "we are L3, so we are covered" has collapsed them. Raising the builder's level closes forgery of the record; it does not clean what the record describes.

  • A signature verifies and the builder.id matches your expectation. What could still be wrong?
    The signing identity may not be authorized to assert that `builder.id`. If your check is "any trusted key" plus "the string matches", anyone holding any accepted key can name that platform. You need the binding from signer to builder identifier. Separately, the build platform can be entirely honest while the recorded inputs — the ref, the config path — are ones you never approved, so an unexamined `externalParameters` remains a hole.
  • Does hardening the builder to Build L3 mean the artifact has no vulnerable dependencies?
    No. The build track is about the integrity of the build and the unforgeability of the record, not about the quality of what went in. An L3 platform will faithfully build source containing a backdoor and truthfully attest that it did. Dependency risk is a separate axis, assessed from component inventories and advisories, and a strong SLSA level is never an answer to a vulnerability question.
  • How does a consumer tell platform-generated provenance from self-generated, given identical JSON?
    Not from the document — the fields look the same. It comes from knowing the platform: what its published build track level is, whether its provenance generation runs in the control plane, and whether the signing identity is one only the platform can use. In practice consumers maintain a small list of builder identifiers they have assessed, and treat anything else as unverified regardless of how well-formed the JSON is.

A parcel with a return address printed by the sender proves nothing; the postmark applied by the carrier is what says where it actually entered the network.

saying these in an interview costs you the question

  • Treats any well-formed provenance as evidence of a trusted build
  • Accepts a signature without binding the signer to builder.id
  • Claims Build L3 removes dependency vulnerabilities
  • Uses job-written timestamps as forensic evidence
  • Thinks the JSON shape reveals who generated the statement

context