jdx/mise · error

unsafe source repository path

Error message

unsafe source repository path

What it means

While enumerating the repository tree with git ls-tree, install_at rejects any entry whose path contains non-Normal components (.., ., leading /) or a segment named .git (case-insensitive). This blocks path-traversal and .git-spoofing attacks when copying source files into the global config directory.

Source

Thrown at src/system/remote_repository.rs:233

        .args(["clone", "--no-checkout", "--"])
        .arg(bundle)
        .arg(&checkout)
        .output()?;
    if !output.status.success() {
        bail!("invalid transferred repository bundle");
    }
    if git(&checkout, &["rev-parse", "HEAD"])? != revision {
        bail!("transferred revision mismatch");
    }
    let entries = git(&checkout, &["ls-tree", "-r", "-z", "--name-only", revision])?;
    for entry in entries.split('\0').filter(|s| !s.is_empty()) {
        let path = Path::new(entry);
        if path
            .components()
            .any(|c| !matches!(c, std::path::Component::Normal(_)))
            || entry.split('/').any(|p| p.eq_ignore_ascii_case(".git"))
        {
            bail!("unsafe source repository path");
        }
        if entry.to_ascii_lowercase().ends_with(".local.toml") {
            bail!(
                "source contains machine-local configuration ({entry}); remove it from the repository before onboarding"
            );
        }
    }
    git(&checkout, &["remote", "set-url", "origin", origin])?;
    let branch = git(&checkout, &["symbolic-ref", "--short", "HEAD"])?;
    git(
        &checkout,
        &["-c", "core.hooksPath=/dev/null", "checkout", &branch],
    )?;
    if destination.join(".git").exists() {
        if git(destination, &["remote", "get-url", "origin"])? != origin {
            bail!("global configuration origin does not match");
        }
        if !git(

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Remove unsafe entries (../, absolute paths, .git segments) from the source repository and commit the fix
  2. Use a trusted, reviewed repository as the onboarding source
  3. Pin a known-good revision of the cleaned repository

Example fix

# before (repo contains .Git/config and ../x.toml)
# after (cleaned tree)
$ git rm -r .Git ../x.toml && git commit -m "clean unsafe paths"
Defensive patterns

Strategy: validation

Validate before calling

fn tree_paths_safe(entries: &[String]) -> bool {
    entries.iter().all(|e| {
        std::path::Path::new(e).components()
            .all(|c| matches!(c, std::path::Component::Normal(_)))
            && !e.split('/').any(|p| p.eq_ignore_ascii_case(".git"))
    })
}

Prevention

When it happens

Trigger: A (potentially malicious) repository contains files like ../escape.toml, /abs/path.toml, .Git/config, or any path whose components aren't plain names.

Common situations: Onboarding an untrusted third-party config repository that includes dotfile symlinks or a nested .git directory; accidentally committing a submodule's .git entry.

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/87fd0ba3bb6364bd. Report an issue: GitHub.