-
Notifications
You must be signed in to change notification settings - Fork 36
feat(spec): Trust Manifest threat modeling review - suggested updates. #47
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 5 commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
58597c3
feat(spec): require substantive trust manifest; add OCI/xRegistry map…
muscariello 2dff4e0
chore(spec): resolve merge conflict with main
muscariello e7ec3b9
Merge branch 'main' into analysis/trust-manifest
muscariello 57be04d
fix(spec): address mindpower review feedback
muscariello a1d2532
fix(spec): align trust-manifest identity semantics
muscariello 6add452
docs(adr): bump trust-manifest ADR sequence to 0019 and 0020
muscariello 0d736ec
fix(spec): merge main — resolve metadata→extensions conflict, fix sub…
muscariello File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| # ADR-0017: Bind Trust Manifest Signatures to Artifact Content | ||
|
|
||
| **Status:** Accepted | ||
|
|
||
| **Date:** 2026-06-20 | ||
|
|
||
| **Participants:** Luca Muscariello (Cisco) | ||
|
|
||
| ## Context | ||
|
|
||
| [ADR-0009](0009-trust-manifest-substitution.md) acknowledged that Trust | ||
| Manifest substitution is a real threat and called for a security review. | ||
| That review was performed as a STRIDE threat model (see | ||
| [trust-manifest-threat-model.md](../specification/trust-manifest-threat-model.md)) | ||
| and confirmed the central weakness: the `signature` covered only the | ||
| Trust Manifest JSON, while the artifact reference (`url`, `data`, | ||
| `mediaType`) lived on the Catalog Entry *outside* the signed payload. | ||
| The only binding was `identity == identifier`. An attacker who controlled | ||
| the catalog document could keep a validly-signed manifest and repoint | ||
| `url` to a malicious artifact — the signature would still verify | ||
| (threat T1 / finding F1). | ||
|
|
||
| The threat model also compared the design against the Sigstore | ||
| architecture from which it derives. Sigstore's Cosign signs the artifact | ||
| *digest*, Fulcio binds identity through a CA, and Rekor witnesses the | ||
| event in a transparency log. The Trust Manifest natively reproduced none | ||
| of these; most importantly it lacked Cosign's digest binding. | ||
|
|
||
| ## Decision | ||
|
|
||
| Bind a signed Trust Manifest to the artifact it describes and harden the | ||
| verification story: | ||
|
|
||
| 1. **Subject binding.** A signed Trust Manifest MUST include a `subject` | ||
| object `{ url?, mediaType, digest }` committing to the artifact's | ||
| media type and content digest. `subject` is part of the signed | ||
| payload, so the artifact reference cannot be changed without | ||
| invalidating the signature. (Closes F1; mirrors Cosign.) | ||
| 2. **Signatures required at Level 3.** A Trusted Catalog MUST carry a | ||
| `signature`, `subject`, and `issuedAt` on each relied-upon entry, and | ||
| consumers MUST verify them. (Closes F2.) | ||
| 3. **Trust anchoring.** Verification MUST anchor `identity` to an | ||
| out-of-band trust root; a verified signature alone proves internal | ||
| consistency, not publisher authenticity. (Closes F3; substitutes for | ||
| Fulcio's CA-attested identity.) | ||
| 4. **Algorithm allowlist.** Consumers MUST reject `alg: none` and | ||
| symmetric algorithms and MUST use asymmetric JWS algorithms. (F4) | ||
| 5. **Freshness.** Add `issuedAt` (REQUIRED when signed) and `expiresAt`, | ||
| with anti-rollback and revocation guidance. (F5) | ||
| 6. **Catalog-level integrity.** Add an OPTIONAL top-level catalog | ||
| `signature` and recommend content-addressed (OCI) distribution. (F6) | ||
| 7. **Safe fetching.** Verification MUST defend against SSRF and | ||
| oversized responses. (F7) | ||
| 8. **Provenance-statement and publisher verification** procedures are | ||
| defined; publisher fields are advisory unless bound. (F8, F9) | ||
|
|
||
| ## Sigstore convergence | ||
|
|
||
| The Trust Manifest remains a *format that carries* trust evidence, not a | ||
| replacement for Sigstore services. The specification SHOULD allow | ||
| Sigstore evidence to be first-class: a `sigstore-bundle` attestation | ||
| (certificate + signature + Rekor inclusion proof), anchoring via the | ||
| Sigstore TUF root, and a preference for transparency-log inclusion and | ||
| keyless signing over long-lived publisher keys. This lets | ||
| trust-sensitive deployments inherit Sigstore's full chain instead of a | ||
| weaker re-implementation. Tracked as future work. | ||
|
|
||
| ## Consequences | ||
|
|
||
| - The substitution attack from ADR-0009 is closed for signed manifests. | ||
| - Level 3 conformance is stricter: presence of a manifest is no longer | ||
| sufficient; it must be signed and bound. | ||
| - Residual gaps versus Sigstore remain (no native transparency log, | ||
| long-lived keys, trust-anchor bootstrapping) and are documented as | ||
| residual risks plus the Sigstore-convergence roadmap. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| # ADR-0018: Require a Substantive Trust Manifest | ||
|
|
||
| **Status:** Accepted | ||
|
|
||
| **Date:** 2026-06-21 | ||
|
|
||
| **Participants:** Luca Muscariello (Cisco) | ||
|
|
||
| ## Context | ||
|
|
||
| The Trust Manifest is an OPTIONAL companion to a Catalog Entry. Within | ||
| it, however, only `identity` was unconditionally REQUIRED. Every other member — `signature`, | ||
| `attestations`, `provenance`, `trustSchema`, and the informational fields | ||
| — was independently OPTIONAL. | ||
|
|
||
| As a result the smallest valid Trust Manifest was `{ "identity": "..." }`, | ||
| which carries no trust signal by itself. | ||
| Only Level 3 (Trusted Catalog) forced a real payload (`signature` + | ||
| `subject` + `issuedAt`). Between "no manifest" and Level 3 the format | ||
| permitted a present-but-empty manifest that looks like trust metadata | ||
| while asserting nothing — misleading to consumers and noise to tooling. | ||
|
|
||
| If every field is optional, the unit of optionality is wrong: the | ||
| *manifest* should be the optional element, and when an author chooses to | ||
| include one it should be required to be meaningful. | ||
|
|
||
| ## Decision | ||
|
|
||
| Define a validity floor for the Trust Manifest. Beyond the required | ||
| `identity`, a Trust Manifest MUST contain at least one *substantive* trust | ||
| member: | ||
|
|
||
| - a `signature` (with its required `subject` and `issuedAt`), | ||
| - a non-empty `attestations` array, | ||
| - a non-empty `provenance` array, or | ||
| - a `trustSchema`. | ||
|
|
||
| `identity` and `identityType` (which identify the workload principal) | ||
| and the informational members `privacyPolicyUrl`, | ||
| `termsOfServiceUrl`, and `metadata` do not satisfy this requirement. | ||
| `subject`, `issuedAt`, and `expiresAt` are not substantive on their own: | ||
| an unsigned `subject` digest is attacker-settable and unverifiable, so | ||
| they count only as part of a `signature`. | ||
|
|
||
| A Trust Manifest that would carry only non-substantive members MUST be | ||
| omitted entirely. Consumers SHOULD treat a manifest that violates this | ||
| rule as if no Trust Manifest were present. | ||
|
|
||
| ## Consequences | ||
|
|
||
| - A present Trust Manifest now always carries at least one verifiable or | ||
| governance-bearing claim; the degenerate identity-only manifest is no | ||
| longer valid. | ||
| - Authors who have nothing substantive to assert simply omit | ||
| `trustManifest`, which is already OPTIONAL — no information is lost. | ||
| - The rule is independent of conformance level: even a Level 1 catalog | ||
| that includes a manifest must make it substantive. Level 3 remains | ||
| stricter, requiring a `signature` specifically (see | ||
| [ADR-0017](0017-trust-manifest-artifact-binding.md)). | ||
| - Unsigned manifests remain valid when they carry attestations, | ||
| provenance, or a trust schema, preserving low-assurance use cases that | ||
| do not sign. |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.