Skip to content

fix(lockfile): resolve SLSA provenance URLs deterministically for all platforms#8982

Merged
jdx merged 7 commits intojdx:mainfrom
cameronbrill:fix/lockfile-provenance-determinism
Apr 11, 2026
Merged

fix(lockfile): resolve SLSA provenance URLs deterministically for all platforms#8982
jdx merged 7 commits intojdx:mainfrom
cameronbrill:fix/lockfile-provenance-determinism

Conversation

@cameronbrill
Copy link
Copy Markdown
Contributor

@cameronbrill cameronbrill commented Apr 9, 2026

Summary

i was noticing differences in SLSA provenance urls in mise.lock generated from mise lock on my laptop (mac-arm64) vs in CI (linux-x64) (i.e. the lockfile generated on my mac had SLSA provenance URLs for mac-arm64 binaries, but mise lock in CI was updating mise.lock to add SLSA provenance URLs for respective linux-x64 binaries)

  • GitHub backend: resolve provenance asset URL from already-fetched release metadata during detection (not just current-platform verification), so all platforms get Slsa { url: Some(url) } consistently
  • Aqua backend: add resolve_slsa_url() that resolves provenance URLs for any target platform using cached release data or template URL construction
  • Both backends: on lock-time verification failure, keep detected provenance instead of clearing to None — consistent with cross-platform detection-only trust model

Fixes non-deterministic mise lock output where SLSA provenance was provenance = "slsa" (short form) for cross-platform entries but expanded with URL for the current platform only.

Test plan

  • New e2e test test_lockfile_provenance_determinism_slow verifies cross-platform entries get expanded provenance form and byte-for-byte determinism across runs
  • Existing test_lockfile_provenance and test_lockfile_provenance_upgrade pass
  • New unit tests for serialization consistency and merge behavior

🤖 Generated with Claude Code

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 9, 2026

Greptile Summary

This PR fixes non-deterministic mise lock output by ensuring SLSA provenance URLs are resolved for all target platforms (not just the current host platform). The aqua backend gains a new resolve_slsa_url() helper that fetches provenance URLs without downloading, and the GitHub backend now captures browser_download_url during the existing detection phase rather than returning Slsa { url: None }.

All security concerns from previous review rounds have been addressed: both backends still set provenance = None on lock-time verification failure, preserving install-time re-verification behavior.

Confidence Score: 5/5

Safe to merge — correctness concerns from prior rounds are resolved; only P2 observations remain.

Both previous P0/P1 security findings (lock-time failure silently bypassing install-time reverification) are addressed: both backends still set provenance = None on verification error. The slsa_pkg vs pkg change in asset_strs is a subtle behavioral delta but is semantically more correct and covered by the slow e2e test. The per-platform API call in the aqua path is a performance trade-off acceptable for a lock-time operation.

src/backend/aqua.rs — the resolve_slsa_url slsa_pkg/pkg substitution warrants a quick sanity check for tools with cross-repo SLSA configs.

Important Files Changed

Filename Overview
src/backend/aqua.rs Introduces resolve_slsa_url() split from run_slsa_check(); called once per platform during lock. Note that asset_strs now uses slsa_pkg instead of pkg (a subtle behavioral change), and the per-platform GitHub API call is not cached.
src/backend/github.rs Detection now captures browser_download_url from already-fetched release assets, cleanly resolving SLSA URL without extra API calls. Verification failure path unchanged.
src/lockfile.rs Adds two focused unit tests covering the all-platforms-get-URL scenario and None-provenance-preserves-existing-URL merge behavior.
e2e/lockfile/test_lockfile_provenance_determinism_slow New slow e2e test validates expanded provenance form on both current and cross-platform entries and byte-for-byte determinism across runs. Uses standard detect_platform helper correctly.

Sequence Diagram

sequenceDiagram
    participant User
    participant MiseLock as mise lock
    participant AquaBackend
    participant GitHubBackend
    participant GitHubAPI

    User->>MiseLock: mise lock --platform linux-x64,macos-arm64

    loop For each platform
        MiseLock->>AquaBackend: get_platform_info(target_os, target_arch)
        AquaBackend->>AquaBackend: detect_provenance_type() → Slsa{url:None}
        AquaBackend->>GitHubAPI: resolve_slsa_url() [github_release_asset]
        GitHubAPI-->>AquaBackend: provenance download URL
        AquaBackend->>AquaBackend: provenance = Slsa{url:Some(url)}
        alt target.is_current()
            AquaBackend->>AquaBackend: verify_provenance_at_lock_time()
            alt verification ok
                AquaBackend->>AquaBackend: provenance = Some(verified)
            else verification failed
                AquaBackend->>AquaBackend: provenance = None (force install-time reverify)
            end
        end
        AquaBackend-->>MiseLock: PlatformInfo{provenance: Slsa{url:Some(...)}}

        MiseLock->>GitHubBackend: get_platform_info(target_os, target_arch)
        GitHubBackend->>GitHubAPI: get_release (once, already fetched)
        GitHubAPI-->>GitHubBackend: release assets
        GitHubBackend->>GitHubBackend: detect_provenance_type() → Slsa{url:Some(browser_download_url)}
        alt target.is_current()
            GitHubBackend->>GitHubBackend: verify_provenance_at_lock_time()
            alt verification ok
                GitHubBackend->>GitHubBackend: provenance = Some(verified)
            else verification failed
                GitHubBackend->>GitHubBackend: provenance = None
            end
        end
        GitHubBackend-->>MiseLock: PlatformInfo{provenance: Slsa{url:Some(...)}}
    end

    MiseLock->>MiseLock: write mise.lock (all platforms have expanded provenance URL)
    MiseLock-->>User: deterministic mise.lock
Loading

Reviews (6): Last reviewed commit: "fix(lockfile): warn when SLSA provenance..." | Re-trigger Greptile

Comment thread src/backend/aqua.rs
Comment thread src/backend/github.rs
Comment thread e2e/lockfile/test_lockfile_provenance_determinism_slow Outdated
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request significantly improves SLSA provenance handling in mise lockfiles, focusing on ensuring deterministic lockfile generation across different platforms. Key changes include the introduction of a resolve_slsa_url function in src/backend/aqua.rs to proactively resolve provenance URLs for all target platforms, updates to src/backend/github.rs to include download URLs in detected provenance, and refined error handling to retain detected provenance even if lock-time verification fails. New end-to-end and unit tests have been added to validate these enhancements. A high-severity issue was identified in src/backend/aqua.rs, where the resolve_slsa_url function incorrectly uses the original pkg object instead of the slsa_pkg with resolved repository information when calling asset_strs and url methods, which could lead to incorrect provenance URL resolution.

Comment thread src/backend/aqua.rs
@cameronbrill cameronbrill marked this pull request as ready for review April 10, 2026 01:14
Copy link
Copy Markdown
Owner

@jdx jdx left a comment

Choose a reason for hiding this comment

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

The silent .ok() on resolve_slsa_url (aqua.rs ~line 665) swallows errors without any indication. If resolution fails for a cross-platform entry, it silently falls back to Slsa { url: None } — which is the short form and defeats the purpose of this PR. Consider adding a warn!() so users know why their lockfile isn't fully deterministic.

This comment was generated by Claude Code.

cameronbrill and others added 7 commits April 10, 2026 09:45
… platforms

Previously, `mise lock` only resolved SLSA provenance URLs for the current
host platform (via cryptographic verification), leaving cross-platform entries
with the short `provenance = "slsa"` form. This caused non-deterministic
lockfile output depending on which machine ran `mise lock`.

Changes:
- GitHub backend: resolve provenance asset URL from already-fetched release
  metadata during detection, not just during current-platform verification
- Aqua backend: add resolve_slsa_url() that resolves provenance URLs for
  any target platform using cached release data or template URL construction
- Both backends: keep detected provenance on verification failure instead of
  clearing to None, consistent with cross-platform detection-only trust model
- Add e2e test verifying cross-platform provenance URL consistency and
  byte-for-byte determinism across runs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Revert verification failure behavior: restore `provenance = None` when
  lock-time verification fails, preserving install-time re-verification
  as a safety net for corrupted/invalid provenance
- Remove unnecessary cleanup block from e2e test per project convention

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Make run_slsa_check() call resolve_slsa_url() for URL resolution
instead of duplicating the logic.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…slsa_url

Pass &slsa_pkg (with resolved repo owner/name) to slsa.asset_strs() and
slsa.url() for defensive correctness, matching the pattern already used
for github_release_asset().

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace silent .ok() with explicit match + warn!() so users know why
a cross-platform lockfile entry falls back to the short provenance form.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@cameronbrill cameronbrill force-pushed the fix/lockfile-provenance-determinism branch from a26243c to 00a62cb Compare April 10, 2026 16:45
@cameronbrill cameronbrill requested a review from jdx April 10, 2026 18:45
@cameronbrill
Copy link
Copy Markdown
Contributor Author

cameronbrill commented Apr 10, 2026

The silent .ok() on resolve_slsa_url (aqua.rs ~line 665) swallows errors without any indication. If resolution fails for a cross-platform entry, it silently falls back to Slsa { url: None } — which is the short form and defeats the purpose of this PR. Consider adding a warn!() so users know why their lockfile isn't fully deterministic.

This comment was generated by Claude Code.

addressed

@jdx jdx merged commit 1fd09c2 into jdx:main Apr 11, 2026
34 checks passed
mise-en-dev added a commit that referenced this pull request Apr 11, 2026
### 🐛 Bug Fixes

- **(docs)** upgrade esbuild to 0.28.0 with es2022 build target by @jdx
in [#9047](#9047)
- **(env)** skip tools=true module hooks in dependency_env by @jdx in
[#9011](#9011)
- **(lockfile)** resolve SLSA provenance URLs deterministically for all
platforms by @cameronbrill in
[#8982](#8982)
- **(registry)** test of docuum in registry by @jylenhof in
[#8996](#8996)
- **(release)** publish extracted mise.exe alongside Windows zip by
@zeitlinger in [#8997](#8997)
- **(schema)** add missing config fields by @risu729 in
[#9044](#9044)
- **(task)** support sandbox fields in task templates by @risu729 in
[#9046](#9046)
- **(tasks)** respect env precedence for task config by @risu729 in
[#9039](#9039)
- prevent implicit enabling of `self_update` when rustls features are
enabled by @salim-b in [#9040](#9040)
- allow installing bun and others when downloads folder is on a
different mount by @bgeron in
[#9032](#9032)

### 📚 Documentation

- discourage direnv compatibility PRs and remove issue suggestions by
@jdx in
[ca78346](ca78346)
- tighten direnv compatibility language by @jdx in
[ab140c8](ab140c8)
- add Tera tip for unsupported version files by @risu729 in
[#9048](#9048)

### 📦️ Dependency Updates

- update ghcr.io/jdx/mise:deb docker digest to 49fa8a4 by @renovate[bot]
in [#8999](#8999)
- update ghcr.io/jdx/mise:copr docker digest to 61ba7b6 by
@renovate[bot] in [#8998](#8998)
- update ghcr.io/jdx/mise:copr docker digest to fa351ff by
@renovate[bot] in [#9002](#9002)
- update ghcr.io/jdx/mise:alpine docker digest to f3bb475 by
@renovate[bot] in [#9001](#9001)
- update ghcr.io/jdx/mise:rpm docker digest to d45af2d by @renovate[bot]
in [#9005](#9005)
- update ghcr.io/jdx/mise:deb docker digest to d7463ac by @renovate[bot]
in [#9004](#9004)
- update jdx/mise-action digest to 5228313 by @renovate[bot] in
[#9007](#9007)
- update rust docker digest to e8e2bb5 by @renovate[bot] in
[#9008](#9008)
- update taiki-e/install-action digest to 97a5807 by @renovate[bot] in
[#9010](#9010)
- update autofix-ci/action action to v1.3.3 by @renovate[bot] in
[#9015](#9015)
- update ubuntu:24.04 docker digest to 84e77de by @renovate[bot] in
[#9012](#9012)
- update actions/checkout action to v4.3.1 by @renovate[bot] in
[#9014](#9014)
- update ubuntu:26.04 docker digest to cc925e5 by @renovate[bot] in
[#9013](#9013)
- update rust crate tokio to v1.51.1 by @renovate[bot] in
[#9018](#9018)
- update rust crate zip to v8.5.1 by @renovate[bot] in
[#9019](#9019)
- update rust crate ctor to 0.9 by @renovate[bot] in
[#9024](#9024)
- update ubuntu docker tag to resolute-20260404 by @renovate[bot] in
[#9020](#9020)
- update dependency vitepress-plugin-tabs to ^0.8.0 by @renovate[bot] in
[#9023](#9023)
- update rust crate indexmap to v2.14.0 by @renovate[bot] in
[#9025](#9025)
- update rust crate nix to 0.31 by @renovate[bot] in
[#9030](#9030)
- update taiki-e/install-action digest to 7a4939c by @renovate[bot] in
[#9027](#9027)
- update dependency esbuild to v0.28.0 by @renovate[bot] in
[#9022](#9022)
- update rust crate rand to 0.10 by @renovate[bot] in
[#9031](#9031)
- update rust crate digest to 0.11.0 by @renovate[bot] in
[#9028](#9028)
- update rust crate confique to 0.4 by @renovate[bot] in
[#9026](#9026)
- update rust crate rattler to 0.40 by @renovate[bot] in
[#9034](#9034)
- lock file maintenance by @renovate[bot] in
[#8416](#8416)
- disable renovate for aws-config/aws-sdk-* crates by @jdx in
[#9052](#9052)
- update swatinem/rust-cache digest to e18b497 by @renovate[bot] in
[#9009](#9009)

### 📦 Registry

- remove broken tool tests by @jdx in
[#9017](#9017)
- update granted aqua backend repo by @risu729 in
[#9033](#9033)
- fix atlas-community test expected output by @jdx in
[#9054](#9054)

### Chore

- use deprecated_at! macro for ubi backend deprecation by @jdx in
[#9049](#9049)

### Security

- **(ci)** run test-tool inside Docker container by @jdx in
[#9055](#9055)
- **(ci)** avoid exposing MISE_GH_TOKEN to test-tool scripts by @jdx in
[#9053](#9053)

### New Contributors

- @bgeron made their first contribution in
[#9032](#9032)
- @salim-b made their first contribution in
[#9040](#9040)

## 📦 Aqua Registry Updates

#### Updated Packages (2)

-
[`cloudnative-pg/cloudnative-pg/kubectl-cnpg`](https://github.com/cloudnative-pg/cloudnative-pg/kubectl-cnpg)
- [`gleam-lang/gleam`](https://github.com/gleam-lang/gleam)
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.

2 participants