refactor(security): switch to sigstore-rust verification#9260
Conversation
There was a problem hiding this comment.
Code Review
This pull request replaces the sigstore-verification dependency with a new internal crate, mise-sigstore, which leverages sigstore-verify v0.6.4. The transition involves updating various backends and plugins to use the new verification helpers. Several issues were identified in the new implementation, including the use of unstable Rust features like the 2024 edition and let_chains syntax. Additionally, feedback was provided regarding potential performance bottlenecks from synchronous I/O in asynchronous contexts and redundant loading of the production trust root within loops. Logic errors in error matching for SLSA fallback and unused variables in URL construction were also noted.
Greptile SummaryThis PR replaces the external
Confidence Score: 4/5Safe to merge after reviewing the one inline comment; the security-critical verification paths are substantially improved over the previous iteration. The new verify_dsse_signature + verify_cert_chain path for raw DSSE envelopes now performs full cryptographic chain validation and PAE signature verification, closing the gaps flagged in earlier review rounds. The one fresh concern is in is_slsa_format_issue: it matches a broad substring (missing field) inside Verification/Sigstore error messages, and a future upstream message-text change could silently misclassify a cryptographic failure as a format issue. src/backend/github.rs — the is_slsa_format_issue error-message pattern matching warrants a closer look. Important Files Changed
Reviews (24): Last reviewed commit: "chore(deps): ignore rustls-webpki 0.102 ..." | Re-trigger Greptile |
f4119a1 to
873b1fa
Compare
Hyperfine Performance
|
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.5.7 x -- echo |
24.1 ± 2.4 | 19.8 | 31.6 | 1.00 |
mise x -- echo |
24.3 ± 2.3 | 20.0 | 30.4 | 1.01 ± 0.14 |
mise env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.5.7 env |
24.0 ± 2.0 | 19.9 | 30.6 | 1.03 ± 0.14 |
mise env |
23.2 ± 2.4 | 18.8 | 29.5 | 1.00 |
mise hook-env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.5.7 hook-env |
24.8 ± 2.2 | 20.2 | 30.9 | 1.00 |
mise hook-env |
25.1 ± 2.3 | 20.3 | 31.1 | 1.01 ± 0.13 |
mise ls
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.5.7 ls |
20.0 ± 1.9 | 16.4 | 25.2 | 1.00 |
mise ls |
20.7 ± 2.0 | 16.5 | 27.4 | 1.03 ± 0.14 |
xtasks/test/perf
| Command | mise-2026.5.7 | mise | Variance |
|---|---|---|---|
| install (cached) | 146ms | 150ms | -2% |
| ls (cached) | 68ms | 71ms | -4% |
| bin-paths (cached) | 73ms | 76ms | -3% |
| task-ls (cached) | 582ms | 577ms | +0% |
426e9d3 to
3bca599
Compare
`verify_message_digest_attribute` hashed the `TSTInfo` content with SHA-256
unconditionally, regardless of what `SignerInfo.digestAlgorithm` (RFC 5652
§5.3) declared. That is correct for the public-good Sigstore TSA — which
signs with SHA-256 — but rejects every TSA response that uses any other
digest. The companion `verify_message_imprint` function one definition above
already reads the digest algorithm from the message imprint and switches on
SHA-256 / SHA-384 / SHA-512; this PR mirrors that pattern for the CMS
message-digest attribute.
The concrete trigger is GitHub Actions's internal TSA at
`timestamp.githubapp.com`, which signs CMS `SignedData` with SHA-384.
Any GitHub-issued artifact attestation (the bundles served from
`/repos/{owner}/{repo}/attestations/<digest>` for builds that publish via
`actions/attest-build-provenance`) currently fails with:
```
HashMismatch { expected: <48 hex bytes>, actual: <32 hex bytes> }
```
After this fix, the verifier picks SHA-384 from `signer_info.digest_alg.oid`
(already extracted a few lines below for ECDSA signature verification) and
the comparison succeeds.
The new regression test uses `jdx/communique@v0.1.9` — a real GitHub
attestation bundle, TSA-only with no Rekor entry — together with GitHub's
published TSA chain narrowed to `timestampAuthorities` (fetched once from
<https://tuf-repo.github.com/>). The existing `test_verify_valid_timestamp`
case (which covers SHA-256) still passes, so this is purely additive.
Discovered while switching `mise` from `sigstore-verification` to this crate
for GitHub artifact attestation verification (see
<jdx/mise#9260>).
Validation:
- `cargo test -p sigstore-tsa` — 15/15 pass (1 new)
- `cargo test` — full workspace passes
- `cargo fmt --check`
- `cargo clippy -p sigstore-tsa --all-targets`
Signed-off-by: jdx <216188+jdx@users.noreply.github.com>
…ion (#83) `verify_message_digest_attribute` hashed the `TSTInfo` content with SHA-256 unconditionally, regardless of what `SignerInfo.digestAlgorithm` (RFC 5652 §5.3) declared. That is correct for the public-good Sigstore TSA — which signs with SHA-256 — but rejects every TSA response that uses any other digest. The companion `verify_message_imprint` function one definition above already reads the digest algorithm from the message imprint and switches on SHA-256 / SHA-384 / SHA-512; this PR mirrors that pattern for the CMS message-digest attribute. The concrete trigger is GitHub Actions's internal TSA at `timestamp.githubapp.com`, which signs CMS `SignedData` with SHA-384. Any GitHub-issued artifact attestation (the bundles served from `/repos/{owner}/{repo}/attestations/<digest>` for builds that publish via `actions/attest-build-provenance`) currently fails with: ``` HashMismatch { expected: <48 hex bytes>, actual: <32 hex bytes> } ``` After this fix, the verifier picks SHA-384 from `signer_info.digest_alg.oid` (already extracted a few lines below for ECDSA signature verification) and the comparison succeeds. The new regression test uses `jdx/communique@v0.1.9` — a real GitHub attestation bundle, TSA-only with no Rekor entry — together with GitHub's published TSA chain narrowed to `timestampAuthorities` (fetched once from <https://tuf-repo.github.com/>). The existing `test_verify_valid_timestamp` case (which covers SHA-256) still passes, so this is purely additive. Discovered while switching `mise` from `sigstore-verification` to this crate for GitHub artifact attestation verification (see <jdx/mise#9260>). Signed-off-by: jdx <216188+jdx@users.noreply.github.com>
3bca599 to
13d3013
Compare
Replace the external `sigstore-verification` dependency with a local `mise-sigstore` adapter built on `sigstore-verify` 0.6.5 from sigstore-rust. The wrapper at `src/github/sigstore.rs` and the `vfox` crate are rerouted to the new adapter; mise's call sites are unchanged because the wrapper from PR #9307 already abstracts the underlying verifier. Why a local adapter and not just bumping `sigstore-verification`: the upstream `sigstore-verify` 0.6.x exposes a different (and richer) API surface. The adapter keeps mise's existing helper-style API (`verify_github_attestation`, `verify_cosign_signature`, `verify_slsa_provenance`, `detect_attestations`) so call sites do not need to change. Why we ship a custom TUF root and bypass `TrustedRoot::production()`: the `sigstore-trust-root` crate embeds TUF root v1 (from 2021), which fails verification under `tough` because the `expires` field uses a timezone-offset format that `chrono` normalizes to UTC `Z` on re-serialization, breaking the canonical-JSON byte sequence that the original signature was computed over (0 of 5 signatures verify, threshold is 3). We embed root v12 (the same workaround the original `sigstore` 0.13 crate uses) and pass it via `TufConfig::custom`, so `tough` walks v12 -> v13 -> v14 successfully. The TUF root file is added to `.prettierignore` because reformatting would invalidate its signatures. Verified end-to-end: - cargo check, cargo clippy --all-targets, cargo fmt --all -- --check, taplo fmt - cargo test --bin mise (747 tests pass) - cargo test -p mise-sigstore (4 tests pass) - mise run test:e2e -- test_aqua_github_attestations passes against goreleaser 2.14.1 (live download + TUF fetch + bundle verification) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Picks up cosign v3 bundle parsing fix for missing optional fields and TSA CMS digest-algorithm honoring from the new sigstore-rust release. The custom TUF root v12 workaround is still required — upstream sigstore-trust-root 0.6.6 still embeds the v1 root with the offset expires format. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
GitHub artifact attestations are signed by GitHub's internal Fulcio (O=GitHub, Inc.) and timestamped by GitHub's internal TSA. Neither is in Sigstore's public trust root, so verifying these bundles via the public Sigstore TrustedRoot fails: TSA timestamp validation reports "no certificate matches issuer and serial number", and the certificate-chain check fails because the leaf doesn't carry an SCT extension (GitHub's CA doesn't log to public CT). Detect GitHub-issued leaf certs by scanning the cert DER for the issuer organization marker, route those bundles to a separate TrustedRoot loaded from GitHub's published trusted_root.json (embedded; refresh from https://tuf-repo.github.com/), and skip the certificate-chain check for those bundles since SCT verification can't be turned off independently. TSA timestamp now validates against the GitHub TSA cert, and the signer-workflow identity check still gates acceptance. Also refreshes the embedded Sigstore TUF root from v12 to v14 since v12 expired in August 2025 and was preventing the TUF chain walk on clean caches. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
verify_cosign_signature_with_key took an unconditional network round-trip to load the production TUF trust root before checking whether the file was a bundle or a raw .sig. The raw-signature path doesn't use the trust root at all, so this broke offline / TUF-unreachable cosign key verification with a network error even when all required material was local. Move the fetch inside the bundle branch. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
slsa-github-generator and goreleaser publish provenance as a DSSE envelope in `*.intoto.jsonl` (e.g. sops's release artifacts), not as a sigstore Bundle. The new mise-sigstore adapter routed every line through `Bundle::from_json`, which rejects these files because they have no `verificationMaterial`, so SLSA verification failed for any package whose provenance ships in the DSSE-only format. When Bundle parsing fails, fall back to parsing the line as a raw DSSE envelope: extract the in-toto payload, check the SLSA predicate type, enforce min_level, and confirm the artifact's sha256 digest is listed in the statement's subjects. This matches the metadata-level checks the previous `sigstore-verification` 0.2.x crate performed for the same format. Also defer `TrustRoots::load()` until a sigstore Bundle is actually parsed, so the DSSE-only path doesn't pay an unnecessary TUF round-trip. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous DSSE-only fallback for SLSA provenance only checked that the artifact's sha256 appeared in the in-toto subject list. An attacker substituting the *.intoto.jsonl could forge a passing attestation by including the artifact's digest and any payload they wanted, since the envelope's signatures were never validated. Verify each signature against the DSSE Pre-Authentication Encoding using the public key extracted from the leaf cert embedded in the signature object (slsa-github-generator format). At least one signature must verify before the subject digest is even consulted. Cert chain validation against Sigstore Fulcio is still skipped on this path because intoto.jsonl files carry neither tlog nor TSA timestamps and Fulcio leaf certs expire ~10 minutes after issuance, so we have no verified time to anchor a chain check at — modern sigstore-Bundle provenance takes the strict `verify_bundle` path with full chain + tlog verification. Tests cover three failure modes: tampered signature, missing signatures, and artifact not in the subject list, using a real sops v3.9.0 envelope as fixture. Also tightens GitHub-internal certificate detection: instead of substring-searching the cert DER for \`b\"GitHub, Inc.\"\` (which would match any cert containing that ASCII anywhere), parse the certificate with x509-cert and check the issuer's organizationName field. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
verify_bundle was called with `skip_tlog: true` from every code path — including the public-Sigstore cosign path, where bundles ship a Rekor inclusion proof that should be cryptographically validated. An attacker swapping the bundle for one with a valid Fulcio chain but a fabricated or absent tlog entry would have passed verification. Drop the parameter and decide policy per bundle: skip tlog only when `bundle.has_inclusion_proof()` returns false (GitHub artifact attestations, TSA-only intoto bundles). Public-Sigstore cosign bundles now go through full tlog verification — Rekor checkpoint signature, SET, and inclusion-proof Merkle path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previously, GitHub artifact attestations skipped both Rekor tlog verification (the bundle has none) and certificate-chain verification (GitHub's CA doesn't issue SCTs, and sigstore-verify gates SCT and chain checks behind the same flag). That left only TSA timestamp validation and signer-workflow identity matching as the security boundary, which was flagged as too weak — an attacker substituting the bundle could put any cert claiming `O=GitHub, Inc.` and the cert chain would never be verified. Walk the chain manually with `webpki` against the GitHub trust root's CA certs before delegating to sigstore-verify with skip_certificate_chain. This performs the same chain build, signature checks, and CODE_SIGNING EKU enforcement that sigstore-verify does, just without the SCT step. TSA timestamp is still validated against the GitHub TSA cert and signer-workflow identity is still matched. Validation time is the leaf cert's notBefore — Fulcio leaf certs are short-lived (~10 minutes) so they're expired by the time we verify, and we don't have an independently verified TSA-derived time at this point in the flow. At notBefore the chain is by definition valid; this still catches forged certs whose issuer doesn't chain to the trust root. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Clippy's `single_match` lint flagged the `match Bundle::from_json` form where only the Ok branch had a body — collapse to `if let Ok(bundle)`. Pure refactor; no behavior change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
verify_dsse_signature accepted any cert in the envelope's signature field, including a trivially self-signed one — only the key-matches-signature consistency was checked. An attacker who knows the artifact's SHA-256 could substitute the *.intoto.jsonl with a forged DSSE envelope built around their own keypair and self-signed cert, and verification would pass. Generalize the existing GitHub-internal chain helper into a reusable verify_cert_chain that takes any TrustedRoot, and call it from verify_dsse_signature against the public Sigstore trust root before trusting the embedded leaf cert's public key. slsa-github-generator emits Sigstore-Fulcio-issued certs, so chain-building succeeds for genuine envelopes and fails for forgeries. Add a unit test that swaps the genuine sops envelope's cert for a foreign cert and asserts the chain step rejects it. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previously verify_cert_chain used the leaf cert's notBefore as the webpki validation time. notBefore is the moment the cert became valid, so any intermediate CA in its validity window at that instant trivially passes — including intermediates that are about to expire. notAfter is strictly later than notBefore, so an intermediate CA whose own validity ends before the leaf's notAfter will fail the time-window check rather than silently pass. The leaf itself is at the edge of its validity period, which webpki accepts. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
TrustRoots::load eagerly fetched both the Sigstore TUF production root (network round-trip) and parsed the embedded GitHub trusted_root.json. That meant verifying a GitHub artifact attestation — which only needs the GitHub root — paid for a full Sigstore TUF walk on every install and would fail outright once the embedded Sigstore TUF root expires (2026-06-22), even though GitHub verification doesn't depend on it. Switch TrustRoots to per-root lazy loading: callers ask for the root they need and pay only for that one. The github attestation flow now never touches the Sigstore TUF CDN; the cosign / SLSA bundle flows only fetch the Sigstore root if/when they actually see a non-GitHub bundle. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
verify_cosign_signature_with_key called tokio::fs::read_to_string then .ok().and_then(...) — which silently swallowed real I/O errors (e.g. permission denied) and re-read the same file in the raw-signature branch with no error context. Read once via tokio::fs::read, propagate the I/O error with `?`, and fall through only when the bytes don't parse as a sigstore Bundle. Refactor read_cosign_signature into a pure decode helper so the bundle and raw paths share the same bytes. Also documents why verify_cert_chain uses every CA cert in the trust root as both a trust anchor and as an intermediate (matching what sigstore-verify does internally, since we already trust the entire embedded CA bundle and a chain may legitimately terminate at an intermediate that we ship as trusted). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
sigstore-verify 0.7.0 (and the matching sigstore-trust-root) ships fixes and APIs that obsolete all three local workarounds in mise-sigstore: * The embedded Sigstore TUF root v1 (broken under `tough` due to timezone-offset re-serialization) is gone — upstream now ships v14 directly, so `TrustedRoot::production()` works without a custom root. * The embedded GitHub trusted_root.json is replaced by upstream's `TrustedRoot::from_embedded(SigstoreInstance::GitHub)` (byte-identical content, same fallback rationale: GitHub's TUF spec version is not yet validated by `tough`). * `VerificationPolicy::skip_sct()` was added in 0.7.0 to skip just the SCT check while keeping certificate-chain validation on. GitHub artifact attestations don't carry public Sigstore CT SCTs, so this is exactly what we need — we no longer have to skip the whole chain step and re-implement it manually with `webpki` for GitHub-internal certs. `verify_cert_chain` is still used for raw DSSE envelopes (slsa-github-generator's `*.intoto.jsonl`), which don't go through `sigstore_verify::verify` at all. Also removes the `crates/mise-sigstore/data/` entry from `.prettierignore` now that the embedded JSON files are gone. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cosign 2.x and earlier `cosign sign-blob --bundle` emits a v1 bundle
shape (`{base64Signature, cert, rekorBundle}`) rather than the modern
sigstore Bundle protobuf. sigstore-verify's `Bundle::from_json` rejects
the v1 shape outright, so any tool whose release pipeline still produces
v1 bundles (e.g. envsense, which goreleaser signs) was failing
`verify artifact with cosign` with "missing field `verificationMaterial`".
Add a fallback in `verify_cosign_signature`: when the JSON doesn't parse
as a modern Bundle, treat it as a v1 bundle, chain-validate the embedded
Fulcio cert against the public Sigstore trust root (reusing the same
`verify_cert_chain` helper we use for raw DSSE envelopes), and
ECDSA-verify `base64Signature` over the artifact bytes with the cert's
public key.
The rekord entry's hash and Rekor `SignedEntryTimestamp` aren't
independently re-checked — the cert+sig step already cryptographically
binds the signer to the artifact bytes, which matches what every
downstream consumer of cosign blob verification actually cares about.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1ca3b4c to
44006f0
Compare
verify_min_slsa_level only checked predicateType and the SLSA level; the parallel raw `*.intoto.jsonl` path already enforces that the artifact's SHA-256 appears in the in-toto statement's `subject` array via verify_intoto_payload, but the bundle path skipped it. Without this check, a valid SLSA bundle signed for one artifact would accept a completely different artifact as long as `verify_bundle` itself succeeded. Rename the helper to `verify_bundle_slsa` and delegate the predicate / level / subject-digest check to `verify_intoto_payload`, so both code paths apply the same verification. Also extend `is_slsa_format_issue` to recognize format-mismatch errors from sigstore-verify itself, which mise-sigstore maps into the `Sigstore(_)` variant (e.g. `missing field 'verificationMaterial'` when the file is a legacy cosign v1 bundle). Without this, the SLSA fallback path treats those as real verification failures rather than "wrong format, try the next strategy." Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit f614169. Configure here.
Never constructed anywhere in the crate. Removing it shrinks the public API surface and silences a stale dead-code warning. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Four rustls-webpki advisories (RUSTSEC-2026-0049, -0098, -0099, -0104) reach mise transitively via sigstore-tsa 0.7.0 → sigstore-bundle → sigstore-verify → mise-sigstore. sigstore-tsa pins `rustls-webpki = "0.102"`, so the fix has to come from a sigstore-rust upstream bump to 0.103.x — we can't drive that from here. None of the four are exploitable in mise's verification paths: we don't pass CRLs or URI-name-constrained certs to webpki, and the TSA chain checks don't go through the affected code. Same pattern as the existing sigstore-derived advisory ignores above. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
## Summary
Replace the external `sigstore-verification` 0.2.x dependency with a
local `mise-sigstore` adapter built on `sigstore-verify` 0.7.0 from
sigstore-rust. mise's call sites are unchanged — the wrapper at
[src/github/sigstore.rs](src/github/sigstore.rs) and the
[vfox](crates/vfox) crate are rerouted to the new adapter, which
preserves the existing helper-style API (`verify_github_attestation`,
`verify_cosign_signature`, `verify_slsa_provenance`,
`detect_attestations`).
### Why a local adapter rather than `sigstore-verify` directly
`sigstore-verify` 0.7.x exposes a different (and richer) API surface
than the legacy crate. The adapter keeps mise's existing helper-style
API stable, layers in GitHub-API attestation fetching (including
`bundle_url` resolution + Snappy decompression), and adds the two paths
sigstore-verify itself does not cover:
- **Legacy cosign v1 bundles** (`{base64Signature, cert, rekorBundle}`)
— emitted by `cosign sign-blob --bundle` 2.x and still produced by
goreleaser-signed releases (envsense, etc.). sigstore-verify's
`Bundle::from_json` rejects this shape, so we chain-validate the
embedded Fulcio cert ourselves and ECDSA-verify `base64Signature` over
the artifact bytes.
- **Raw DSSE envelopes** (`*.intoto.jsonl` from slsa-github-generator /
goreleaser) — no `verificationMaterial`, so sigstore-verify rejects
them. We verify the DSSE signature against the Fulcio cert in the
envelope (chain-validated), then enforce the artifact's SHA-256 is
present in the in-toto subject list.
### What `sigstore-verify` 0.7.0 buys us over 0.6.x
0.7.0 (and the matching `sigstore-trust-root` 0.7.0) lets us drop every
local workaround the original adapter shipped:
- The embedded Sigstore TUF root is gone — upstream ships TUF v14
directly, so `TrustedRoot::production()` walks the live TUF CDN with no
custom bootstrap root from us.
- The embedded `github_trusted_root.json` is gone —
`TrustedRoot::from_embedded(SigstoreInstance::GitHub)` provides it
(byte-identical content), same fallback rationale: `tough` doesn't yet
validate GitHub's TUF spec version.
- The manual `webpki` chain validation for GitHub-internal certs is gone
— `VerificationPolicy::skip_sct()` (new in 0.7.0) lets us turn off just
the SCT check (which GitHub doesn't issue) while keeping full
sigstore-verify-driven chain validation. We still use
`verify_cert_chain` for raw DSSE envelopes, since those don't go through
`sigstore_verify::verify` at all.
Net delta vs. the previous iteration of this PR: ~1,150 lines deleted
from `mise-sigstore`, plus the legacy cosign v1 path added.
## Verified
- `cargo check`, `cargo clippy --all-targets -- -D warnings`, `cargo fmt
--all -- --check`, `taplo fmt`
- `cargo test --bin mise` — 924 tests pass
- `cargo test -p mise-sigstore` — 8 tests pass
- e2e (locally): `test_aqua_github_attestations`, `test_aqua_cosign`,
`test_lockfile_cosign_top_level_binary`,
`test_lockfile_cosign_opts_only`, `test_python_github_attestations`,
`test_ruby_github_attestations` all pass — including the new
legacy-bundle path against envsense 0.3.4 and the modern bundle path
against goreleaser 2.14.1.
*This PR was generated by an AI coding assistant.*
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **High Risk**
> Replaces the core artifact attestation/cosign/SLSA verification
dependency and logic, including new certificate-chain and DSSE/legacy
bundle handling, which could incorrectly accept or reject releases if
any edge case is missed.
>
> **Overview**
> Swaps out the `sigstore-verification` dependency for a new workspace
crate, `mise-sigstore`, built on `sigstore-verify` v0.7, and reroutes
`mise` and `vfox` verification call sites to it.
>
> The new adapter adds GitHub attestation fetching (including
`bundle_url` resolution and Snappy decompression), updates verification
policy handling (e.g., skipping tlog or SCT checks when bundles lack
those proofs), and expands support to **legacy cosign v1 bundles** and
**raw DSSE `*.intoto.jsonl` provenance** by manually chain-validating
embedded Fulcio certs and verifying signatures + subject digests.
>
> Also updates error classification in the GitHub backend to treat more
`sigstore-verify` parse/format failures as non-fatal format issues,
adjusts TLS feature wiring, adds release automation for publishing
`mise-sigstore`, and updates `deny.toml` ignores for transitive
`rustls-webpki` advisories.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
29fa282. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>

Summary
Replace the external
sigstore-verification0.2.x dependency with a localmise-sigstoreadapter built onsigstore-verify0.7.0 from sigstore-rust. mise's call sites are unchanged — the wrapper at src/github/sigstore.rs and the vfox crate are rerouted to the new adapter, which preserves the existing helper-style API (verify_github_attestation,verify_cosign_signature,verify_slsa_provenance,detect_attestations).Why a local adapter rather than
sigstore-verifydirectlysigstore-verify0.7.x exposes a different (and richer) API surface than the legacy crate. The adapter keeps mise's existing helper-style API stable, layers in GitHub-API attestation fetching (includingbundle_urlresolution + Snappy decompression), and adds the two paths sigstore-verify itself does not cover:{base64Signature, cert, rekorBundle}) — emitted bycosign sign-blob --bundle2.x and still produced by goreleaser-signed releases (envsense, etc.). sigstore-verify'sBundle::from_jsonrejects this shape, so we chain-validate the embedded Fulcio cert ourselves and ECDSA-verifybase64Signatureover the artifact bytes.*.intoto.jsonlfrom slsa-github-generator / goreleaser) — noverificationMaterial, so sigstore-verify rejects them. We verify the DSSE signature against the Fulcio cert in the envelope (chain-validated), then enforce the artifact's SHA-256 is present in the in-toto subject list.What
sigstore-verify0.7.0 buys us over 0.6.x0.7.0 (and the matching
sigstore-trust-root0.7.0) lets us drop every local workaround the original adapter shipped:TrustedRoot::production()walks the live TUF CDN with no custom bootstrap root from us.github_trusted_root.jsonis gone —TrustedRoot::from_embedded(SigstoreInstance::GitHub)provides it (byte-identical content), same fallback rationale:toughdoesn't yet validate GitHub's TUF spec version.webpkichain validation for GitHub-internal certs is gone —VerificationPolicy::skip_sct()(new in 0.7.0) lets us turn off just the SCT check (which GitHub doesn't issue) while keeping full sigstore-verify-driven chain validation. We still useverify_cert_chainfor raw DSSE envelopes, since those don't go throughsigstore_verify::verifyat all.Net delta vs. the previous iteration of this PR: ~1,150 lines deleted from
mise-sigstore, plus the legacy cosign v1 path added.Verified
cargo check,cargo clippy --all-targets -- -D warnings,cargo fmt --all -- --check,taplo fmtcargo test --bin mise— 924 tests passcargo test -p mise-sigstore— 8 tests passtest_aqua_github_attestations,test_aqua_cosign,test_lockfile_cosign_top_level_binary,test_lockfile_cosign_opts_only,test_python_github_attestations,test_ruby_github_attestationsall pass — including the new legacy-bundle path against envsense 0.3.4 and the modern bundle path against goreleaser 2.14.1.This PR was generated by an AI coding assistant.
Note
High Risk
Replaces the core artifact attestation/cosign/SLSA verification dependency and logic, including new certificate-chain and DSSE/legacy bundle handling, which could incorrectly accept or reject releases if any edge case is missed.
Overview
Swaps out the
sigstore-verificationdependency for a new workspace crate,mise-sigstore, built onsigstore-verifyv0.7, and reroutesmiseandvfoxverification call sites to it.The new adapter adds GitHub attestation fetching (including
bundle_urlresolution and Snappy decompression), updates verification policy handling (e.g., skipping tlog or SCT checks when bundles lack those proofs), and expands support to legacy cosign v1 bundles and raw DSSE*.intoto.jsonlprovenance by manually chain-validating embedded Fulcio certs and verifying signatures + subject digests.Also updates error classification in the GitHub backend to treat more
sigstore-verifyparse/format failures as non-fatal format issues, adjusts TLS feature wiring, adds release automation for publishingmise-sigstore, and updatesdeny.tomlignores for transitiverustls-webpkiadvisories.Reviewed by Cursor Bugbot for commit 29fa282. Bugbot is set up for automated code reviews on this repo. Configure here.