skip to content

Why must the expected builder identity live in the gate's rule rather than be read from the evidence?

level: middleimportance: should knowfreq 47%

answer

  1. asserted terms versus expected terms
  2. where did the right-hand value come from
  3. a document is trivially self-consistent
  4. establish the issuer before believing fields
  5. forgery moves to build-platform access control

basics

~20 s

A field read out of the evidence says whatever its producer wanted it to say. Pinning the accepted builder identity in the rule is the step that compares the claim against a value the gated team cannot edit.

solid answer

~50 s

A promotion rule of the form "this artifact may ship only if it was built by builder X, from repository Z, on protected branch Y" has two kinds of terms: what the evidence asserts, and what the policy expects. The expected values must be the policy's, configured out of band. If the rule merely checks that a `builder` field is present and non-empty, or compares one field of the document against another field of the same document, it has verified nothing — any producer that can emit a document passes. The rule becomes meaningful only when the gate can attribute the statement to an issuer and compare that issuer against a list the rule pins. Repository and branch are then a second, independent condition on what that trusted builder was permitted to build from. The practical payoff: forging a pass now requires running a build on the pinned builder from the protected branch, which turns an evidence problem into an access-control problem you already manage.

go deeper

for a junior

Know that the value a rule compares against has to come from the policy, not from the document being checked, and be able to say why a present-and-non-empty check proves nothing.

for a middle

Explain the two-term structure clearly: establish the issuer first, then treat builder and source as separate pinned conditions, and be ready to spot the config-file-in-the-gated-repo mistake.

for a senior

Argue in terms of where forgery has to happen — the rule's value is that it relocates the attack onto build-platform access and branch protection — and describe how you keep pinned values maintained without wildcards creeping in.

for a principal

Own the boundary decision: which builders and which source refs the organisation is willing to accept as authoritative, who may widen that list, and how you avoid the deadline-driven broadening that silently returns the gate to a presence check.

## Two kinds of term in one rule The promotion rule everyone eventually writes reads roughly: *this artifact may be promoted only if the build statement says it was produced by builder X, from repository Z, on branch Y.* It looks like one condition. It is really two categories of term, and confusing them is the most common way a gate ends up decorative. - **Asserted terms** — the builder, repository and ref values carried in the document the gate reads. These are strings someone wrote. - **Expected terms** — the values the policy owner decided are acceptable. These live in the rule. A gate is a *comparison between the two*. Any rule that only inspects asserted terms is self-referential, no matter how elaborate it looks. ## The three shapes of a useless check 1. **Presence checks.** `builder` exists and is non-empty. This passes for every producer on earth, including one an attacker stood up. 2. **Field-against-field checks.** The statement's `repo` matches the `repo` recorded elsewhere in the same statement. Consistent lies are still lies; a document is trivially self-consistent. 3. **Expectations sourced from the gated repository.** The rule reads its allow-list of acceptable builders from a config file in the repo it is gating. This one feels safe and is the most insidious — the party constrained by the policy can widen the policy in the same commit as the change it wants through. If the expectation is editable by the gated team, it is not an expectation; it is another asserted term. ## What makes the builder term load-bearing Builder identity is the term that converts the rest of the document from claims into facts. Verifying the statement's signature yields an *issuer* — the mechanics of that verification are a separate subject, and what matters to rule authoring is the output: an identity, established independently of what the document says about itself. If that identity is one the policy pinned in advance, then you have a reason to believe the rest of the fields, because you know which system produced them and how it obtained them. If the gate accepts any issuer, then repository and branch checks are spell-checks over attacker-supplied strings. Order of reasoning in the rule follows from this: establish the issuer first, and treat every other field as meaningless until it is established. A rule that checks the branch before it has an issuer has done work in the wrong order. ## The source condition is separate, and still necessary Pinning the builder alone says "a system I trust produced this". It does not say what that system was told to build. A shared build platform will happily build anything anyone points it at, including a fork, a scratch branch, or a pull request from outside the organisation. So the source condition — repository, plus the ref or branch — is a second, independent constraint: *the trusted builder, building the code I meant*. Leave either half out and the rule has a hole with a name you can say out loud. ## What you have actually bought The point of pinning both terms is not cryptographic elegance; it is **where the attack has to happen**. Before: forging a pass means writing a file. After: forging a pass means obtaining the ability to run a build on the pinned builder, from the protected branch of the named repository. That is no longer an evidence problem. It is branch protection, build-platform access control and repository permissions — controls that already exist, already have owners, and are already audited. A good answer names this explicitly: the rule's job is to move the forgery requirement onto ground you already defend. ## Operating it Pinned expectations are configuration with a lifecycle, and pretending otherwise is how these rules get switched off: - **They break on legitimate change.** A repository rename, a migration to a new build platform, a branch renamed from `master` to `main`, a new team onboarding — each produces a hard block on a correct change. Expect it, and have a fast, logged path to update the pinned values that is owned by the policy team rather than the gated team. - **They must be readable.** A rule whose expected values are buried in an opaque bundle invites people to disable the rule instead of correcting the expectation. - **They tempt over-broadening.** "Any builder in our organisation" and "any branch in the repo" are the two shortcuts that quietly restore the original hole. Widening is a policy decision with a named owner, not a fix applied under deadline pressure. ## The question to ask of any gate rule For each condition: *where did the value on the right-hand side of this comparison come from?* If the answer is "the same document", or "a file the gated team can edit", the condition is decoration. If it is "policy the gated team cannot change", it is a control.

  • The allow-list of builders sits in a config file in the gated repository. What is wrong?
    The party the policy constrains can widen the policy. They can add their own builder identity in the same change that needs to pass, and the gate approves it. Expectations must live where the gated team has no write access — in the policy bundle or the platform's own configuration — or they are just another asserted value.
  • If the builder is pinned, why also pin the repository and branch?
    Because a trusted builder builds whatever it is pointed at. Without a source condition the rule accepts a build of a fork, a scratch branch or an external contributor's branch, all produced by the identity you trust. Builder answers "who ran it", source answers "on what"; you need both.
  • What does pinning both terms actually cost an attacker?
    It converts document forgery into obtaining build execution on the pinned builder from the protected branch. That means compromising build-platform access or branch protection — controls that already exist and are already monitored — rather than writing a JSON file in a workspace.
  • A repository rename breaks the gate for a legitimate release. How should that be handled?
    Treat pinned expectations as maintained configuration. Route the update through the policy owner with a logged change, keep the failure message specific enough that engineers see "expected repo Z, statement says Z2" rather than a generic denial, and never let the pressure of a broken release justify broadening the pin to a wildcard.

saying these in an interview costs you the question

  • Checks only that the builder field is present and non-empty
  • Compares one field of the document against another field of it
  • Keeps the allowed-builder list in the repository being gated
  • Pins the builder but accepts any branch or fork
  • Assumes a signature alone means the content is acceptable

context