skip to content

In SLSA v1 provenance, why does a verifier care more about externalParameters than internalParameters?

level: middleimportance: must knowfreq 65%

answer

  1. one half is chosen by the caller
  2. the other sits inside a trusted boundary
  3. verification is a comparison, not a parse
  4. a true record of the wrong branch
  5. an unchecked input voids the rest

basics

~20 s

externalParameters holds whatever the person who triggered the build chose, so it is where an adversary with trigger rights expresses themselves. internalParameters is set by the platform you already trust. Verification means comparing the external half against expectations.

solid answer

~50 s

`externalParameters` is the caller's request — repository, ref, config path, version — so anyone who can trigger a build picks those values. `internalParameters` is applied by the build platform's own control plane; if you trust the platform enough to accept its `builder.id`, you are already trusting those. So verification concentrates on the external half: you compare each field against a policy expectation, such as the ref being an approved release tag rather than any branch. Two things follow. First, an accurate field is not a passing check — provenance that faithfully records a contributor's personal branch is a true statement about an untrusted build, and it should fail. Second, you should reject `externalParameters` fields you do not recognize, because an unknown input may change the build and void the assumption that the fields you did check pin the result.

code

json · 15 lines
json
{
  "buildDefinition": {
    "buildType": "https://example.com/buildtypes/library-pack/v1",
    "externalParameters": {
      "repository": "https://example.com/acme/payments-sdk",
      "ref": "refs/heads/fix-nuget-pack",
      "configPath": ".build/pack.yaml",
      "packageVersion": "4.2.0-preview"
    },
    "internalParameters": {
      "runnerImageDigest": "sha256:9f2c..."
    },
    "resolvedDependencies": [ "..." ]
  }
}

go deeper

for a junior

Remember which half is which: externalParameters is what the person triggering the build asked for, internalParameters is what the platform decided. Being able to point at a ref or a repository as an external parameter is enough here.

for a middle

Be ready to explain why the caller-supplied half is the one a verifier constrains, and to give a concrete expectation such as requiring the ref to be a release tag rather than any branch.

for a senior

Demonstrate the failure you have seen in production: a green signature check on provenance nobody compared to an expectation. Say what you would do when an unrecognized parameter appears in a platform upgrade.

for a principal

Own the argument that verification expectations are a contract that must be maintained as platforms add parameters, and decide who in the organisation reviews a buildType change before the expectation is widened.

## The distinction the spec draws A SLSA v1 build is modelled as a function. The `buildType` names the function, `externalParameters` are the arguments the caller supplied, and `internalParameters` are the arguments the build platform supplied on its own. Together they are meant to fully determine what the build did. That modelling is what makes the split security-relevant. **`externalParameters` is the attack surface of the request.** Every value in it was chosen by whoever triggered the build — which, in a repository that accepts contributions, includes people with far less privilege than the release owner. **`internalParameters` is inside the trust boundary you already accepted** when you decided the `builder.id` was one you trust. You cannot meaningfully audit a platform's internal choices from the outside; you either trust the platform or you do not. ## An accurate field is not a passing check Consider a library build for a package feed where the branch to build arrives as an external parameter. A contributor with only push rights to their own branch triggers a build of `refs/heads/fix-nuget-pack`. The provenance produced is completely honest: it records that exact ref, and `builder.id` is the same trusted platform that produces the real releases. Nothing is forged. And yet the artifact must not be published as a release. The provenance is *accurate* and *worthless as a trust signal on its own*, because accuracy is not authorization. This is the single most common failure in real verification setups: teams wire up signature checking, see it pass, and conclude the artifact is good — having never compared the recorded inputs against what they expected those inputs to be. Verification is a comparison, not a parse. The expectation might be "repository is exactly this one, ref matches `refs/tags/v*`, config path is the one in the release process". ## Why unrecognized fields must fail Suppose your check is "repository and ref match expectations". That check is only sound if repository and ref, together with the `buildType`, determine the build. If the platform also accepts a parameter you have never heard of — an extra script to run, an alternate registry, an override of the entry point — then an attacker who cannot change the ref can still change the artifact through the field you are not looking at. So the discipline is to enumerate the parameters you accept and reject anything else. A new field appearing is usually not an attack; it is more often the platform shipping a feature. But it means your checked fields no longer pin the result, and the correct response is to fail, read the `buildType` documentation, and then extend the expectation deliberately. ## What emptiness means on the internal side `internalParameters` is optional and is frequently omitted entirely. An absent or empty `internalParameters` does **not** mean the platform applied no settings of its own — every build runs on some image with some toolchain. It means the platform chose not to state them. Whatever assurance you have about those values comes from trusting the platform, not from reading the field. Treating an empty object as a positive claim is a design error in a verification rule. ## The reviewer's reading order A useful habit when handed a provenance document: read `buildType` first so you know what the parameter names mean, then read `externalParameters` and ask of each field, *could someone I do not fully trust have chosen this value, and does my expectation constrain it?* Anything you cannot answer is a gap, whether the field is malicious or merely undocumented. Only then look at `builder.id`, and treat `internalParameters` as background — informative for debugging a build difference, not load-bearing for the accept-or-reject decision.

  • Why reject an externalParameters field you do not recognize instead of ignoring it?
    Because your check assumes the fields you compared determine the build. An input you ignore may add a script, swap a source or change the entry point, so the artifact can vary while every field you looked at stays correct. Failing closed forces you to read what the new parameter does and extend the expectation on purpose, which is the only way the check stays sound as the platform evolves.
  • Does an empty internalParameters mean the platform applied no settings of its own?
    No. The field is optional and often omitted, so absence means unstated, not none — every build runs on some image with some toolchain. Your confidence in those values comes from trusting the platform named by `builder.id`, not from reading the field. A rule that treats an empty object as a positive claim about the environment is reading information that is not there.
  • How would you write the expectation for externalParameters without knowing every value in advance?
    Constrain rather than enumerate values: the repository must be exactly one URI, the ref must match an approved release pattern, the config path must be the release one, and the set of parameter names must be a subset of the ones you have read documentation for. That pins the build without needing to predict the version string or the timestamp of a particular release.

saying these in an interview costs you the question

  • Says accurate provenance means a trustworthy artifact
  • Checks only that the signature verifies
  • Treats internalParameters as attacker-controllable
  • Ignores parameter fields the verifier does not recognize
  • Reads an empty internalParameters as proof of a clean environment

context