jdx/mise · error

invalid tool version {s:?}: contains path-traversal sequence

Error message

invalid tool version {s:?}: contains path-traversal sequence

What it means

validate_version_string blocks version strings containing '..' to prevent path-traversal: a version is used to build install paths, and '..' could escape the tool's install directory. Such strings are rejected with this bail.

Source

Thrown at src/toolset/tool_request.rs:631

/// The deny list is the minimum set of characters that can break out of either
/// a single- or double-quoted shell string, or that trigger expansion *inside*
/// double quotes: quotes themselves, backslash, backtick, and `$`. Plus control
/// characters (newlines split shell tokens) and `..` (filesystem traversal).
/// Everything else is allowed so legitimate version vocabulary (npm-style
/// semver ranges like `>=20 <21 || >=22` or `^1.0.0`, dates, channel names,
/// `lts/hydrogen`, etc.) continues to work — those characters are only
/// dangerous in *unquoted* shell context, which cannot occur without one of
/// the rejected expansion characters appearing first. Leading dashes are also
/// rejected so backend install tools cannot mistake a version for a CLI flag.
fn validate_version_string(s: &str) -> Result<()> {
    if s.is_empty() {
        return Ok(());
    }
    if s.starts_with('-') {
        bail!("invalid tool version {s:?}: must not start with '-'");
    }
    if s.contains("..") {
        bail!("invalid tool version {s:?}: contains path-traversal sequence");
    }
    if let Some(c) = s.chars().find(|c| is_forbidden_version_char(*c)) {
        bail!("invalid tool version {s:?}: contains forbidden character {c:?}");
    }
    Ok(())
}

/// Validate `ref:`/`branch:`/`tag:`/`rev:` values. Same character rules as
/// version strings: branch/tag names already use the same broad vocabulary
/// (`/`, `+`, `-`, etc.), so only shell-quote-breaking characters and leading
/// dashes need rejection. Kept as a separate function for distinct error
/// messages.
fn validate_ref_string(s: &str) -> Result<()> {
    if s.is_empty() {
        return Ok(());
    }
    if s.starts_with('-') {
        bail!("invalid tool ref {s:?}: must not start with '-'");

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Remove the '..' sequence; use a real version or ref syntax (e.g. ref:main) for non-version installs
  2. Validate/normalize any user-supplied version before passing it to mise
  3. Use 'path:' backend syntax if you actually need a local path, not a version string
  4. Keep version specs sourced from `mise ls-remote` output

Example fix

// before
node = '22/../../shared'
// after
node = '22'
Defensive patterns

Strategy: validation

Validate before calling

// block traversal sequences in version strings before invoking mise
if (version.includes('..')) throw new Error('path traversal in version');

Type guard

const traversalSafe = (v) => typeof v === 'string' && !v.includes('..');

Try / catch

try { installTool(name, version) } catch (e) { if (String(e).includes('path-traversal')) { console.error('version contains ../ — refusing'); } else { throw e; } }

Prevention

When it happens

Trigger: ToolRequest::new_with_options -> validate_version_string sees a version containing '..' anywhere — e.g. 'node@22/../evil' or an unvalidated variable interpolated into a version spec.

Common situations: Malicious or malformed input in scripts generating version strings; typo'd relative paths pasted into version fields; attempts to reference versions outside the install tree.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/28fb83bf9b6b26b9. Report an issue: GitHub.