fix(github): resolve "latest" version correctly via GitHub API#8532
fix(github): resolve "latest" version correctly via GitHub API#8532
Conversation
The GitHub API endpoint for the latest release is `/releases/latest`, not `/releases/tags/latest` (which looks for a tag literally named "latest"). This fixes a regression in v2026.3.5 where `github:` backend tools with `@latest` would fail with a 404 during install/exec. Two fixes: 1. `get_release_` in github.rs now uses `/releases/latest` when tag is "latest" 2. Revert the prefer-offline guard on `latest_version_with_opts` from #8500 — "latest" must always resolve to a concrete version via remote lookup when no installed version is found, otherwise it propagates as the literal string "latest" into the install path Fixes #8530 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request addresses a regression where Highlights
Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
Two fixes for the regression in v2026.3.5 where `github:` backend tools with `@latest` fail with a 404 during install/exec (#8530): 1. `get_release_()` in github.rs now uses `/releases/latest` when tag is "latest", instead of `/releases/tags/latest` (which looks for a tag literally named "latest") 2. In prefer-offline mode, when "latest" can't be resolved locally, return early with "latest" so the backend's install path can handle it via the correct API endpoint — rather than falling through to general version matching which fails to find it. This preserves the prefer-offline guard from #8500 that prevents hangs with private registries. Fixes #8530 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The prefer-offline guard was added in #8500 to prevent npm hangs, but that was properly fixed in bdaf470 by making subprocess calls async with kill_on_drop. The guard is now redundant and was causing "latest" to not resolve to a concrete version in prefer-offline mode. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Greptile SummaryFixes a regression where Key changes:
Both code fixes are minimal, well-scoped, and directly address the reported regression. The e2e test provides solid coverage of the primary scenario with explicit assertions for both negative (reject literal "latest") and positive (require semver pattern) cases. Confidence Score: 4/5
Important Files Changed
Last reviewed commit: ded8093 |
There was a problem hiding this comment.
Code Review
This pull request correctly fixes an issue with resolving the latest version for GitHub-backend tools by using the correct API endpoint and addresses a regression that prevented latest from being resolved in prefer-offline mode. However, a high-severity credential leakage vulnerability was identified in src/github.rs. The get_release_ function uses an untrusted api_url (from mise.toml) to construct requests, and the get_headers function incorrectly attaches the user's GITHUB_TOKEN to these requests even for non-GitHub hosts. This allows a malicious project to steal a user's GitHub credentials, and this PR increases exposure to this vulnerability by making remote lookups for latest more frequent. Additionally, while a new end-to-end test has been added, there is a suggestion to improve its long-term maintainability.
| let url = if tag == "latest" { | ||
| format!("{api_url}/repos/{repo}/releases/latest") | ||
| } else { | ||
| format!("{api_url}/repos/{repo}/releases/tags/{tag}") | ||
| }; |
There was a problem hiding this comment.
The get_release_ function constructs a URL using the api_url parameter, which can be provided by an untrusted mise.toml configuration file. This URL is then passed to get_headers, which incorrectly attaches the user's GITHUB_TOKEN (or MISE_GITHUB_ENTERPRISE_TOKEN) to the request even if the host is not api.github.com.
A malicious project can specify a custom api_url in its mise.toml (e.g., api_url = "https://attacker.com") to steal the user's GitHub credentials when mise attempts to resolve tool versions.
This vulnerability is exacerbated by the changes in src/toolset/tool_version.rs which remove the prefer_offline guard, causing mise to perform remote lookups for latest versions more frequently, even in modes where it previously avoided network requests.
| cat <<EOF >mise.toml | ||
| [tools] | ||
| "github:jdx/mise-test-fixtures" = { version = "latest", asset_pattern = "hello-world-1.0.0.tar.gz", bin_path = "hello-world-1.0.0/bin", postinstall = "chmod +x \$MISE_TOOL_INSTALL_PATH/hello-world-1.0.0/bin/hello-world" } | ||
| EOF |
There was a problem hiding this comment.
The test configuration hardcodes the version "1.0.0" in asset_pattern, bin_path, and postinstall. This makes the test brittle and dependent on the jdx/mise-test-fixtures repository's "latest" release always pointing to version 1.0.0.
If the test fixture is updated with a new release, this test will likely fail, which could cause confusion during unrelated development.
For better long-term maintainability, consider one of these approaches:
- Modify the test fixture to provide version-agnostic asset names (e.g.,
hello-world.tar.gz). - Create a new test fixture that uses
{{version}}templating in its asset names, and update this test to use it.
While the current implementation correctly tests the regression, improving its robustness will benefit the project in the long run.
Check that the version column contains a semver-like string and does not contain the literal "latest", rather than just checking for the absence of "latest (missing)". Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Hyperfine Performance
|
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.3.5 x -- echo |
23.0 ± 0.3 | 22.4 | 27.7 | 1.00 |
mise x -- echo |
23.3 ± 0.4 | 22.6 | 26.4 | 1.01 ± 0.02 |
mise env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.3.5 env |
22.8 ± 0.7 | 22.0 | 27.8 | 1.00 |
mise env |
22.9 ± 0.4 | 22.4 | 25.1 | 1.00 ± 0.04 |
mise hook-env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.3.5 hook-env |
23.2 ± 0.3 | 22.4 | 24.9 | 1.00 |
mise hook-env |
23.5 ± 0.3 | 22.9 | 24.9 | 1.01 ± 0.02 |
mise ls
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.3.5 ls |
22.6 ± 0.5 | 22.0 | 29.5 | 1.00 |
mise ls |
22.9 ± 0.3 | 22.3 | 26.5 | 1.01 ± 0.03 |
xtasks/test/perf
| Command | mise-2026.3.5 | mise | Variance |
|---|---|---|---|
| install (cached) | 150ms | 151ms | +0% |
| ls (cached) | 82ms | 82ms | +0% |
| bin-paths (cached) | 84ms | 84ms | +0% |
| task-ls (cached) | 839ms | -69% |
harlequin 1.24.0 is incompatible with duckdb 1.5.0 (released today), causing an UnboundLocalError in plugin loading. Bump to 2.5.1 which works with the latest duckdb. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
### 🐛 Bug Fixes - **(activate)** reorder shims to front of PATH on re-source in fish by @jdx in [#8534](#8534) - **(backend)** strip mise shims from dependency_env PATH to prevent fork bomb by @pose in [#8475](#8475) - **(github)** resolve "latest" version correctly via GitHub API by @jdx in [#8532](#8532) - **(lock)** set env tags and clarify lockfile docs by @jdx in [#8519](#8519) - **(lock)** use separate mise.<env>.lock files instead of env tags by @jdx in [#8523](#8523) - **(task)** include args in task output prefix and truncate long prefixes by @jdx in [#8533](#8533) - **(task)** only include args in task prefix when disambiguating duplicates by @jdx in [#8536](#8536) - **(test)** pin goreleaser version in attestation e2e test by @jdx in [#8518](#8518) - **(windows)** env._.source needs to run bash.exe on Windows (fix #6513) by @pjeby in [#8520](#8520) - handle locked .exe shims on Windows during reshim by @davireis in [#8517](#8517) ### 🚜 Refactor - **(prepare)** remove touch_outputs and update docs to reflect blake3 hashing by @jdx in [#8535](#8535) ### 📚 Documentation - **(docker)** replace jdxcode/mise image with curl install, update to debian:13-slim by @jdx in [#8526](#8526) - fix "gzip: stdin is encrypted" error in shell tricks cookbook by @pjeby in [#8512](#8512) ### 📦 Registry - add tigerbeetle ([github:tigerbeetle/tigerbeetle](https://github.com/tigerbeetle/tigerbeetle)) by @risu729 in [#8514](#8514) ### New Contributors - @pjeby made their first contribution in [#8520](#8520) - @davireis made their first contribution in [#8517](#8517) - @Aurorxa made their first contribution in [#8511](#8511) ## 📦 Aqua Registry Updates #### New Packages (6) - [`betterleaks/betterleaks`](https://github.com/betterleaks/betterleaks) - [`majorcontext/moat`](https://github.com/majorcontext/moat) - [`princjef/gomarkdoc`](https://github.com/princjef/gomarkdoc) - [`remko/age-plugin-se`](https://github.com/remko/age-plugin-se) - [`sudorandom/fauxrpc`](https://github.com/sudorandom/fauxrpc) - [`swanysimon/mdlint`](https://github.com/swanysimon/mdlint) #### Updated Packages (1) - [`moonrepo/moon`](https://github.com/moonrepo/moon)
Summary
/releases/latestinstead of/releases/tags/latest(which looks for a tag literally named "latest")latest_version_with_optsfrom fix(env): skip remote version fetching for "latest" in prefer-offline mode #8500 — the npm hang it aimed to fix was properly addressed in bdaf470 by making subprocess calls async withkill_on_drop, making the guard redundantgithub:backend with@latestversionContext
Regression in v2026.3.5 where
github:backend tools configured with@latestfail with a 404 error during install/exec. Two issues combined:get_release_()ingithub.rsalways constructed/releases/tags/{tag}, but "latest" isn't a tag — GitHub's API uses/releases/latestfor the latest release endpointexec/env/hook-env, causing the literal string "latest" to propagate into the install path. This guard was added to prevent npm hangs with private registries, but that issue was already properly fixed in bdaf470 by using async subprocess calls withkill_on_dropFixes #8530
Test plan
test_github_latestverifies install and exec with@latest🤖 Generated with Claude Code
Note
Medium Risk
Touches version-resolution logic and GitHub API calling behavior, which can affect installs/execs across environments; changes are small and covered by a targeted e2e regression test.
Overview
Fixes
github:tool installs/execs usingversion = "latest"by routing GitHub release lookups to/releases/latest(instead of treatinglatestas a tag via/releases/tags/latest).Adjusts
ToolVersion::resolve_versionsolatestcontinues to resolve to a concrete remote version even under prefer-offline flows (while still respecting true offline mode), and adds an e2e regression test (test_github_latest) that assertsmise lsreports a real semver rather than the literallatest.Updates the
pipxextras e2e test to use a newerharlequinversion and tweaks the python selection comment for consistency.Written by Cursor Bugbot for commit ded8093. This will update automatically on new commits. Configure here.