fix(prepare): resolve sources/outputs relative to dir when set#8472
Conversation
When a prepare provider sets `dir`, relative source and output paths now resolve against `project_root/dir` instead of just `project_root`. This fixes change detection for custom providers with `dir` set, where source hashes were never saved to `prepare-state.toml` because the files couldn't be found at the project root. Adds `config_root()` helper to `ProviderBase` and updates all built-in providers to use it consistently for sources, outputs, cwd, and applicability checks. Closes #8471 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 an issue 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
|
There was a problem hiding this comment.
Code Review
This pull request correctly implements support for the dir option in prepare providers by introducing a config_root() helper. However, it highlights a pre-existing path traversal vulnerability in the git-submodule provider. The git-submodule provider parses the untrusted .gitmodules file and uses path values to construct file paths without proper validation, which could allow mise to interact with arbitrary files on the user's system. Additionally, my review includes suggestions to address minor performance issues where config_root() is called inside a loop and to reduce code duplication in path construction.
| value | ||
| .strip_prefix('=') | ||
| .map(|value| self.base.project_root.join(value.trim())) | ||
| .map(|value| self.base.config_root().join(value.trim())) |
There was a problem hiding this comment.
This code block in submodule_paths is vulnerable to path traversal. The path values from the untrusted .gitmodules file are joined directly with the configuration root without validating that they are relative paths. This allows an attacker to provide malicious paths (e.g., absolute paths or parent directory references like path = /etc/passwd or path = ../../../etc/passwd), which could cause mise to interact with arbitrary files on the user's system, leading to information leakage or modification of file metadata. To remediate this, validate that the path values extracted from .gitmodules are relative and do not traverse outside the intended directory. You can use Path::is_relative() and ensure the resulting path is contained within the config_root. Additionally, for performance, self.base.config_root() is called repeatedly inside this loop; consider calling it once and reusing the result.
| for pattern in patterns { | ||
| let base_dir = self.base.config_root(); |
There was a problem hiding this comment.
|
|
||
| // Use `go mod vendor` if vendor/ exists, otherwise `go mod download` | ||
| let vendor = self.base.project_root.join("vendor"); | ||
| let vendor = self.base.config_root().join("vendor"); |
There was a problem hiding this comment.
The path to the vendor directory is constructed here and also in the outputs function on line 37. To avoid this duplication and improve maintainability, you could consider creating a private helper method on GoPrepareProvider like fn vendor_path(&self) -> PathBuf that returns self.base.config_root().join("vendor"). Then, both outputs and prepare_command can call this helper.
Greptile SummaryThis PR fixes a bug where Key changes:
Confidence Score: 5/5
Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[PrepareProvider] --> B{config.dir set?}
B -->|Yes| C["config_root() = project_root / dir"]
B -->|No| D["config_root() = project_root"]
C --> E[sources / outputs / is_applicable / cwd]
D --> E
A --> F{Custom run override?}
F -->|Yes| G["PrepareCommand::from_string(run, project_root, config)"]
G --> H{"config.dir set?"}
H -->|Yes| I["cwd = project_root / dir"]
H -->|No| J["cwd = project_root"]
F -->|No| K["Default PrepareCommand with cwd = config_root()"]
Last reviewed commit: 57198ee |
Hyperfine Performance
|
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.3.3 x -- echo |
26.0 ± 0.5 | 25.3 | 31.0 | 1.00 |
mise x -- echo |
26.2 ± 0.6 | 25.4 | 31.5 | 1.01 ± 0.03 |
mise env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.3.3 env |
25.5 ± 0.5 | 24.6 | 28.3 | 1.00 |
mise env |
25.6 ± 0.5 | 24.7 | 27.2 | 1.01 ± 0.03 |
mise hook-env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.3.3 hook-env |
26.1 ± 0.4 | 25.5 | 29.9 | 1.00 |
mise hook-env |
26.2 ± 0.3 | 25.4 | 28.1 | 1.00 ± 0.02 |
mise ls
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.3.3 ls |
25.2 ± 0.3 | 24.6 | 27.4 | 1.00 |
mise ls |
25.4 ± 0.4 | 24.8 | 29.5 | 1.01 ± 0.02 |
xtasks/test/perf
| Command | mise-2026.3.3 | mise | Variance |
|---|---|---|---|
| install (cached) | 161ms | 160ms | +0% |
| ls (cached) | 88ms | 86ms | +2% |
| bin-paths (cached) | 90ms | 90ms | +0% |
| task-ls (cached) | 822ms | 818ms | +0% |
### 🚀 Features - **(github)** keep exe extensions on Windows by @iki in [#8424](#8424) - **(task)** add `interactive` field for exclusive terminal access by @jdx in [#8491](#8491) - add header comment to generated lockfiles by @ivy in [#8481](#8481) - runtime musl/glibc detection for correct libc variant selection by @jdx in [#8490](#8490) ### 🐛 Bug Fixes - **(github)** use registry platform options during install by @jdx in [#8492](#8492) - **(http)** store tool opts as native TOML to fix platform switching by @jdx in [#8448](#8448) - **(installer)** error if MISE_INSTALL_PATH is a directory by @jdx in [#8468](#8468) - **(prepare)** resolve sources/outputs relative to `dir` when set by @jdx in [#8472](#8472) - **(ruby)** fetch precompiled binary by release tag instead of listing all releases by @jdx in [#8488](#8488) - **(schema)** support structured objects in task depends by @risu729 in [#8463](#8463) - **(task)** replace println!/eprintln! with calm_io in task output macros by @vmaleze in [#8485](#8485) - handle scoped npm package names without backend prefix by @jdx in [#8477](#8477) ### 📦️ Dependency Updates - update ghcr.io/jdx/mise:copr docker digest to c485c4c by @renovate[bot] in [#8484](#8484) - update ghcr.io/jdx/mise:alpine docker digest to 8118bc7 by @renovate[bot] in [#8483](#8483) ### 📦 Registry - disable sd version test by @jdx in [#8489](#8489) ### New Contributors - @ivy made their first contribution in [#8481](#8481) - @iki made their first contribution in [#8424](#8424) ## 📦 Aqua Registry Updates #### New Packages (5) - [`datadog-labs/pup`](https://github.com/datadog-labs/pup) - [`k1LoW/mo`](https://github.com/k1LoW/mo) - [`rtk-ai/rtk`](https://github.com/rtk-ai/rtk) - [`suzuki-shunsuke/docfresh`](https://github.com/suzuki-shunsuke/docfresh) - [`yashikota/exiftool-go`](https://github.com/yashikota/exiftool-go) #### Updated Packages (6) - [`cloudflare/cloudflared`](https://github.com/cloudflare/cloudflared) - [`mozilla/sccache`](https://github.com/mozilla/sccache) - [`owenlamont/ryl`](https://github.com/owenlamont/ryl) - [`spinel-coop/rv`](https://github.com/spinel-coop/rv) - [`technicalpickles/envsense`](https://github.com/technicalpickles/envsense) - [`weaviate/weaviate`](https://github.com/weaviate/weaviate)
Summary
dir, relative source and output paths now resolve againstproject_root/dirinstead of justproject_rootconfig_root()helper toProviderBasethat returns the effective root directoryconfig_root()for sources, outputs, cwd, and applicability checksdiraffects source/output resolution and freshness trackingCloses #8471
Test plan
mise run build— compiles successfullymise run test:e2e test_prepare— all existing + new tests passdir = "subdir"and relative sources, verifyprepare-state.tomlincludes hashed sources🤖 Generated with Claude Code
Note
Medium Risk
Changes path resolution and working directories for all built-in prepare providers and custom glob expansion, which can affect applicability detection and freshness/state tracking in existing projects (especially monorepos/subdirs).
Overview
Prepare providers now resolve relative
sources/outputs(and built-in provider lockfile checks) against an effective root derived fromdir(project_root/dir) instead of always usingproject_root.This introduces
ProviderBase::config_root()and updates all built-in providers (npm/yarn/pnpm/bun/go/pip/poetry/uv/bundler/composer/git-submodule) plus custom provider glob expansion to use it forsources,outputs,cwd, and applicability checks, with an added e2e test ensuringdiraffects listing and freshness hashing for a custom provider.Written by Cursor Bugbot for commit 57198ee. This will update automatically on new commits. Configure here.