Skip to content

security: prevent http install path escape#10245

Merged
jdx merged 6 commits into
mainfrom
security/http-install-version-path
Jun 6, 2026
Merged

security: prevent http install path escape#10245
jdx merged 6 commits into
mainfrom
security/http-install-version-path

Conversation

@jdx

@jdx jdx commented Jun 5, 2026

Copy link
Copy Markdown
Owner

Summary

  • sanitize non-latest HTTP install symlink version names with ToolVersion::tv_pathname()
  • preserve content-addressed install names for latest and empty HTTP versions
  • add regression coverage for absolute version paths and latest behavior

Security

Fixes GHSA-f94h-j2qg-fxw3 by preventing repository-controlled HTTP backend version strings from escaping the mise installs directory via absolute paths.

Tests

  • cargo test backend::http::tests
  • cargo fmt --check

Note

High Risk
Changes install directory layout and lookup for HTTP tools (security-critical path handling); existing installs keyed by raw version strings may not be found until reinstalled.

Overview
Security fix for the HTTP backend: install symlink directory names no longer use raw version strings, so malicious or path-like versions cannot escape installs_path (GHSA-f94h-j2qg-fxw3).

Non-latest versions are named via ToolVersion::tv_pathname() with extra rules (./.., backslashes, hash suffix when sanitization changes the string). latest still uses a short content-derived cache key; empty versions map to _implicit. Alias symlinks under latest are created only for explicit "latest", not empty versions.

Install and lookup paths are centralized (install_path_for / lookup_install_path); installs record tv.install_path, is_version_installed and list_bin_paths resolve the same sanitized paths (including runtime-symlink checks where applicable). Unit tests cover absolute paths, .., Windows separators/UNC, distinct version collisions, and latest/empty behavior.

Reviewed by Cursor Bugbot for commit 80ae49f. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • Refactor
    • New install-path naming helpers standardize install directory names; "latest"/empty installs map to short content-derived names while other versions are sanitized. Alias symlink creation now runs only for explicit "latest" installs and targets content-derived names. Install records now store the computed install path during installation.
  • Tests
    • Added unit tests for sanitization, out-of-root/UNC/Windows-separator cases, distinct-character handling, and "latest"/empty aliasing behavior.

@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Need an answer fast? Review this PR in Change Stack to ask focused questions about the PR or a changed range.

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 05948ebf-03b3-485c-8843-e5fcc1154d90

📥 Commits

Reviewing files that changed from the base of the PR and between 39e8496 and 80ae49f.

📒 Files selected for processing (1)
  • src/backend/http.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/backend/http.rs

📝 Walkthrough

Walkthrough

HttpBackend centralizes deterministic, filesystem-safe install-directory naming (sanitizes inputs, uses cache-key for latest/empty), uses helpers for symlink creation and backend state, sets tv.install_path during install, and adds unit tests for these behaviors.

Changes

Install Symlink Naming

Layer / File(s) Summary
Imports for runtime symlink checks and requests
src/backend/http.rs
Added imports for is_runtime_symlink and ToolRequest used by the new naming and lookup logic.
Naming helpers and sanitization
src/backend/http.rs
Added install_version_name, install_path_for, lookup_install_path, content_version_name, and sanitize_install_version_name to compute sanitized or content-derived install directory components and append short hashes when sanitization would otherwise change identity.
Use helpers in symlink creation
src/backend/http.rs
Replaced inline install-path computation in create_install_symlink with install_path_for; updated create_version_alias_symlink to create the alias only when tv.version == "latest" and to use content_version_name(cache_key) for the alias target name.
Set tv.install_path during install
src/backend/http.rs
Explicitly set tv.install_path = Some(Self::install_path_for(&tv, &cache_key)) in install_version_ after creating symlinks so backend state reflects the computed path.
Installed checks and bin path lookup
src/backend/http.rs
Added is_version_installed override to validate installed directories while excluding runtime symlinks; updated list_bin_paths to resolve the install directory via lookup_install_path, set tv.install_path, and then discover bin locations.
Unit tests for naming behavior
src/backend/http.rs
Added tests for out-of-root/absolute-ish pathname sanitization, .. handling, Windows separators and UNC inputs, distinct sanitized outputs for differing characters, and that latest/empty versions map to cache-key-derived short names; tests also assert install_path_for/lookup_install_path consistency.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

A rabbit hops through path and key,
Trims the dots and slashes free.
Latest wears a tiny mark,
Others cleaned to leave their mark.
Symlinks placed — the tests agree. 🐇

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 76.19% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'security: prevent http install path escape' directly matches the main objective of the PR, which is to prevent HTTP install path escape vulnerabilities by sanitizing version names.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch security/http-install-version-path

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps

greptile-apps Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes GHSA-f94h-j2qg-fxw3 by sanitizing HTTP backend version strings before using them as install-directory names, preventing repository-controlled version values from escaping ~/.local/share/mise/installs/ via absolute paths, .., or Windows separators.

  • install_version_name / sanitize_install_version_name replace :, /, and \ with dashes, map bare . and .. to safe names, and append a SHA-256 prefix when sanitization changes the string so distinct inputs cannot collide on the same directory.
  • latest and empty versions continue to use content-derived short names; the latest → {content_version} alias symlink is preserved only for explicit latest (no longer for empty versions).
  • A new is_version_installed override and updates to list_bin_paths route through lookup_install_path instead of the default tv.install_path() to honour the sanitized path after installation.

Confidence Score: 4/5

The path-traversal fix is sound and well-tested; the only rough edge is an untested inconsistency in lookup_install_path for latest.

The core sanitization logic is correct: tv_pathname() eliminates / and :, sanitize_install_version_name then catches backslash, ., and .., and the hash suffix prevents identity collisions. The one gap is that lookup_install_path for latest without tv.install_path set returns installs_path/latest rather than installs_path/{content_version}, and there is no test covering this branch.

src/backend/http.rs — specifically the lookup_install_path function and its behaviour for the latest version when tv.install_path has not yet been populated.

Important Files Changed

Filename Overview
src/backend/http.rs Adds install-path sanitization helpers to prevent HTTP version strings from escaping the installs directory; introduces is_version_installed override and updates list_bin_paths to use lookup_install_path. One minor logic gap: lookup_install_path for latest without tv.install_path set returns installs_path/latest (the alias symlink) while install_path_for returns installs_path/{content_version}.

Fix All in Claude Code

Reviews (5): Last reviewed commit: "security: align http install lookup path..." | Re-trigger Greptile

Comment thread src/backend/http.rs

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/backend/http.rs`:
- Around line 509-515: install_version_name currently returns an empty string
for implicit versions which causes create_version_alias_symlink (and the other
alias logic around install_version_name) to resolve to the installs root and
potentially remove it; change install_version_name (and the same logic used at
the other location noted) to never return "" — either reject empty tv.version by
returning a Result and bubble an error, or map empty/implicit versions to a safe
deterministic alias (e.g. a prefixed name derived from cache_key like
"_implicit_<short-cache>") instead of "" — update callers such as
create_version_alias_symlink to handle the new non-empty alias (or the Result)
so we never join an empty string onto installs_path.
- Around line 509-515: install_version_name currently trusts tv.tv_pathname()
for non-"latest" versions but tv_pathname only normalizes ':' and '/' so Windows
backslashes (e.g. "..\\..\\escape" or "\\\\server\\share") can still cause path
traversal when later joined into installs_path; update install_version_name (and
any joins around installs_path usage) to normalize and reject or escape
backslashes (treat '\' like '/' for normalization or strictly sanitize any path
separator characters) before returning the install name, ensure tv.tv_pathname()
is updated or wrap its result to replace/backslash-normalize and validate there
are no parent-segment escapes or UNC paths, and add a regression test that
passes a Windows-style version string (e.g. "..\\..\\escape" and
"\\\\server\\share") to install_version_name and to the install flow to confirm
traversal/UNC are blocked.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 300170fb-8ced-47c4-9024-806ed984956b

📥 Commits

Reviewing files that changed from the base of the PR and between 44f2b2e and e8ed2e5.

📒 Files selected for processing (1)
  • src/backend/http.rs

Comment thread src/backend/http.rs
Comment thread src/backend/http.rs

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/backend/http.rs`:
- Around line 509-526: The install-name mapping
(install_version_name/content_version_name/sanitize_install_version_name) is
lossy and can collide for inputs like "a/b", "a:b", "a\\b", and "a-b"; update
sanitize_install_version_name (used by install_version_name and
content_version_name logic) so the produced install directory is injective:
compute a deterministic short hash (e.g., first 7 hex chars of SHA-256) of the
original raw version/request string (use tv.tv_pathname() or the raw version as
available) and append it to the sanitized name whenever sanitization changes the
input (or always append for simplicity) so different raw inputs never map to the
same directory; update install_version_name to use this new behavior and add a
regression test that installs two colliding-looking versions (e.g., "a/b" and
"a-b" or "a:b") and asserts they produce distinct install directories and do not
clobber each other.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 6def6307-a2f4-44f8-8384-620bb7475ec0

📥 Commits

Reviewing files that changed from the base of the PR and between e8ed2e5 and a33c831.

📒 Files selected for processing (1)
  • src/backend/http.rs

Comment thread src/backend/http.rs Outdated
@github-actions

github-actions Bot commented Jun 6, 2026

Copy link
Copy Markdown

Hyperfine Performance

mise x -- echo

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.6.0 x -- echo 19.1 ± 0.9 17.3 22.3 1.00
mise x -- echo 20.1 ± 2.0 17.6 50.3 1.05 ± 0.12

mise env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.6.0 env 18.7 ± 0.9 16.7 23.0 1.00
mise env 19.1 ± 1.0 17.2 25.2 1.02 ± 0.07

mise hook-env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.6.0 hook-env 19.3 ± 1.0 17.5 24.6 1.00
mise hook-env 19.8 ± 0.9 18.0 24.1 1.02 ± 0.07

mise ls

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.6.0 ls 15.6 ± 0.7 14.0 19.1 1.00
mise ls 16.1 ± 0.8 14.5 20.0 1.03 ± 0.07

xtasks/test/perf

Command mise-2026.6.0 mise Variance
install (cached) 136ms 136ms +0%
ls (cached) 61ms 62ms -1%
bin-paths (cached) 63ms 64ms -1%
task-ls (cached) 125ms 130ms -3%

@jdx jdx enabled auto-merge (squash) June 6, 2026 00:26

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 39e8496. Configure here.

Comment thread src/backend/http.rs

jdx commented Jun 6, 2026

Copy link
Copy Markdown
Owner Author

Addressed the hashed install path lookup mismatch in 80ae49f: HTTP now uses the same normalized install path for installed checks and bin discovery, and empty HTTP versions use a stable _implicit path instead of an unresolvable content-derived name.

Verified with:

  • cargo test backend::http::tests --quiet
  • cargo fmt --check
  • git diff --check
  • cargo clippy --all-features --all-targets -- -D warnings

This comment was generated by an AI coding assistant.

@jdx jdx merged commit f08ed34 into main Jun 6, 2026
34 checks passed
@jdx jdx deleted the security/http-install-version-path branch June 6, 2026 00:50
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.

1 participant