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
- Remove unsafe entries (../, absolute paths, .git segments) from the source repository and commit the fix
- Use a trusted, reviewed repository as the onboarding source
- 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
- Only onboard repositories you have reviewed
- Keep .git, submodule internals, and absolute/../ paths out of the config repo
- Audit `git ls-tree -r --name-only <rev>` before onboarding an untrusted repo
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
- brew-cask:{}: git only_path must stay within the checkout
- remote task path escapes its Git checkout: {}
- [dotfiles]."{}": target is not a safe OCI path
- brew-cask: invalid {kind} '{value}'
- brew-cask: staged symlink path escaped extraction root: {}
AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09).
Data as JSON: /api/errors/87fd0ba3bb6364bd.
Report an issue: GitHub.