Skip to content

CEP XXXX: add recipe source attestation validation - #168

Open
wolfv wants to merge 6 commits into
conda:mainfrom
wolfv:recipe-source-attestations
Open

CEP XXXX: add recipe source attestation validation#168
wolfv wants to merge 6 commits into
conda:mainfrom
wolfv:recipe-source-attestations

Conversation

@wolfv

@wolfv wolfv commented May 20, 2026

Copy link
Copy Markdown
Contributor

Adding a CEP to validate the source section against know sigstore providers.

Checklist for submitter

  • I am submitting a new CEP: Put your title here.
    • I am using the CEP template by creating a copy cep-0000.md named cep-XXXX.md in the root level.
  • I am submitting modifications to CEP XX.
    • The PR title reflects the CEP I'm modifying: CEP XX: Amend XYZ.
    • I updated the "Updated" date.
    • I added the link of this PR to the Discussions row.
    • I added or extended the ## Changelog section right above the final "Copyright" section with an item that uses syntax YYYY-MM-DD: Brief explanation of changes.
  • Something else: (add your description here).

Checklist for CEP approvals

  • The vote period has ended and the vote has passed the necessary quorum and approval thresholds.
  • A new CEP number has been minted. Usually, this is ${greatest-number-in-main} + 1.
  • The cep-XXXX.md file has been renamed accordingly.
  • The # CEP XXXX - header has been edited accordingly.
  • The CEP status in the table has been changed to approved.
  • The last modification date in the table has been updated accordingly.
  • The table in the README has been updated with the new CEP entry.
  • The pre-commit checks are passing.

Comment thread cep-xxxx-source-attestations.md Outdated
Comment thread cep-xxxx-source-attestations.md Outdated
Comment thread cep-xxxx-source-attestations.md Outdated
Comment on lines +90 to +103
### Publisher Identity Grammar

A publisher identity is a string of the form:

```text
<provider>:<owner>/<repo>[@<ref>]
```

| Component | Meaning |
| ---------- | -------------------------------------------------------------------------------------- |
| `provider` | The identity provider. This CEP defines `github` and `gitlab`. Others MAY be added. |
| `owner` | The owner / organization on that provider. MUST NOT be empty. |
| `repo` | The repository on that provider. MUST NOT be empty. |
| `ref` | Optional ref constraint (e.g. `refs/tags/v1.0`). Reserved; see **Future Work** below. |

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about using some subset of PURLs here?

Comment thread cep-XXXX.md
A publisher identity is a string of the form:

```text
<provider>:<owner>/<repo>[@<ref>]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is too restrictive. For example, on PyPI, packages uploaded with attestations signed by Google Cloud have subjects that are emails, and not owner/repo:

  Subject Alternative Name (critical):
    email:
    - 919436158236-compute@developer.gserviceaccount.com

src

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think something like provider:<identifier> would work where the tool, or the CEP, should define a list of supported provider & identifier pairs? Or should we turn this into a true dictionary supporting all OID fields and match on those? Do you know of any prior art for this?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should support this shorthand form to begin with. It adds complexity by having multiple ways of representing the same identity, and by having different rules on how to parse the shorthand versions (i.e gihub's <owner>/<repo>[@<ref>] vs whatever we come up with the Google Cloud identity).

If we decide to go with it, we should make the matching depend on the issuer. For example, the paragraph that says:

An identity value that is a URL (starts with https://) is matched
against URI SANs using repository-boundary prefix matching

we shouldn't do this in general, since there's no guarantee that a subject that contains a URL is referring to a repository. Rather, we can specify it for known issuers where we know it to be the case (github, gitlab), but otherwise we should do exact matching.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(the above also applies to the explicit mapping form)

Comment thread cep-XXXX.md
Comment on lines +111 to +112
| `github` | `https://github.com/<owner>/<repo>` | `https://token.actions.githubusercontent.com` |
| `gitlab` | `https://gitlab.com/<owner>/<repo>` | `https://gitlab.com` |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the upcoming PyPI support for self-hosted GitLab and GitHub instances, there will be attestations that are technically from github and gitlab, but will have different OIDC issuers. Something to keep in mind here, with regards to naming the providers

Comment thread cep-xxxx-source-attestations.md Outdated
The bundle URL is determined as follows, in order:

1. If `bundle_url` is set in the recipe, the builder MUST use that URL.
2. Otherwise, if the source URL host is `pypi.io` or

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why .io instead of .org?

Comment thread cep-XXXX.md
Comment on lines +133 to +134
3. Otherwise, the builder MUST fail with an error reporting that no
`bundle_url` is set and one cannot be auto-derived.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Otherwise, the builders MUST fail

this is in contradiction with the statement below:

Builders MAY recognize additional auto-derived hosts

If builders may recognize additional auto-derived hosts, they will not fail for those hosts, even if the bundle_url is not set in the recipe (1) and the source URL host is not pypi (2).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe it makes sense to say "builders SHOULD fail" instead?

Comment thread cep-XXXX.md
Comment on lines +199 to +202
### Predicate Semantics Are Out of Scope

This CEP does not define, restrict, or interpret the predicate types
that appear in the in-toto statement. In particular:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not lock the predicate type too? The builders don't need to "interpret it", but they could check if it matches the type specified in the lockfile.

My thinking here is that in this CEP (as it's currently written), the verification process checks that a given artifact has a valid signature from an identity specified in the lockfile. But since these signatures have semantics attached (specified by their predicate type), accepting any signature from that identity as valid might be too much.

So, locking the predicate type, we can ensure during veriication that the semantics of the signature have not changed since they were locked, without actually interpreting them.

Comment thread cep-XXXX.md
source:
url: https://files.pythonhosted.org/packages/ab/cd/flask-3.1.1.tar.gz
sha256: "6489f1..."
attestation:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

currently, this CEP only allows for one attestation url/sounce per source. One thing to consider is if in the future we might want to have multiple attestations sources (such as PyPI and GitHub) per source

wolfv and others added 5 commits May 28, 2026 08:30
Co-authored-by: jaimergp <jaimergp@users.noreply.github.com>
Co-authored-by: jaimergp <jaimergp@users.noreply.github.com>
- Rename cep-xxxx-source-attestations.md to cep-XXXX.md to satisfy the
  cep-filenames pre-commit hook
- Add an explicit {identity, issuer} mapping form for publishers, so
  non-repository SANs (e.g. Google Cloud email identities) and
  self-hosted GitHub/GitLab instances can be expressed
- Add optional predicate_type to pin the in-toto predicate type
- Allow bundle_url to be a list to support multiple attestation sources
- Add pypi.org to the auto-derivation hosts and explain the pypi.io alias
- Close the auto-derivation list to resolve the MUST-fail vs
  MAY-recognize contradiction
- Add a note on why (identity, issuer) pairs are used instead of PURLs

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Require sha256 when attestation is present; note schema is additive
- Define which URL drives auto-derivation for mirror lists
- Specify project/version parsing per PEP 625 / wheel filename format
- Builders MUST reject the reserved @ref suffix instead of ignoring it
- Allow repo paths with slashes (GitLab subgroups) in shorthands
- State case-sensitivity rules for identity and issuer matching
- Rework verification into explicit filter steps: subject mismatches in
  pooled responses are excluded, not fatal; empty set fails the build
- Reference the Sigstore Client Specification for bundle verification
- Add a Security Considerations section (threat model, unsigned recipe
  caveat, bundle transport, network dependence) and an offline
  verification / bundle vendoring item to Future Work

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Verified attestation bundles are stored under
info/recipe/attestations/ next to the rendered recipe, with a
`verified` index (path, sha256, predicate_type, san, issuer) in the
rendered recipe's attestation mapping. The built package thus keeps a
machine-checkable link back to its upstream source publisher and can be
re-verified offline; combined with a CEP 27 publish attestation this
forms a chain from package to source producer.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants