jdx/mise · error

remote task path escapes its Git checkout: {}

Error message

remote task path escapes its Git checkout: {}

What it means

When a remote task (fetched from a Git URL) is resolved to a local path, mise validates that the resolved path — including via symlink_metadata/canonicalize — still lives inside the cloned checkout root. If the canonicalized path escapes the checkout (e.g. via a symlink pointing outside), mise refuses with this error, guarding against path traversal from untrusted task repositories.

Source

Thrown at src/task/task_file_providers/remote_task_git.rs:81

            url_without_path: url_without_path.to_string(),
            path: path.to_string(),
            branch,
        }
    }
}

/// Ensure a remote task path resolves inside its Git checkout and points at a
/// regular file or directory.
pub(crate) fn validate_remote_git_path(
    checkout_root: &Path,
    path: &Path,
) -> Result<std::fs::Metadata> {
    let metadata = path.symlink_metadata()?;
    if !path
        .canonicalize()?
        .starts_with(checkout_root.canonicalize()?)
    {
        eyre::bail!(
            "remote task path escapes its Git checkout: {}",
            display_path(path)
        );
    }
    if metadata.file_type().is_file() || metadata.file_type().is_dir() {
        return Ok(metadata);
    }
    eyre::bail!(
        "remote task path is not a regular file or directory: {}",
        display_path(path)
    )
}

impl RemoteTaskGit {
    /// Make fetched task files executable while leaving task include directories intact.
    fn prepare_remote_path(checkout_root: &Path, path: &Path) -> Result<()> {
        if validate_remote_git_path(checkout_root, path)?
            .file_type()

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Fix the remote task path to point inside the checkout (no ../ traversal, no outbound symlinks)
  2. Remove or replace outbound symlinks in the task repository
  3. Re-clone the repository so paths resolve within the new checkout root
  4. If you own the repo, restructure so task files are real files inside the repo

Example fix

# before: remote task path escaping the checkout
source = "https://github.com/org/tasks#../../host-tool"
# after
source = "https://github.com/org/tasks#tasks/build.toml"
Defensive patterns

Strategy: validation

Validate before calling

import fs from "fs";
import path from "path";
function insideCheckout(root, p) {
  const realRoot = fs.realpathSync(root);
  const realP = fs.realpathSync(path.join(root, p));
  return realP.startsWith(realRoot + path.sep);
}

Type guard

fn stays_in_checkout(root: &Path, p: &Path) -> bool {
    match (p.canonicalize(), root.canonicalize()) {
        (Ok(p), Ok(r)) => p.starts_with(r),
        _ => false,
    }
}

Try / catch

try {
  const meta = fs.lstatSync(taskPath);
  if (meta.isSymbolicLink() && !insideCheckout(checkoutRoot, taskPath)) {
    throw new Error("remote task path escapes checkout");
  }
} catch (e) {
  console.error("Use a path inside the cloned repo and avoid outbound symlinks");
  throw e;
}

Prevention

When it happens

Trigger: validate_remote_git_path (called by resolve_git_url_to_path and prepare_remote_path) raises when path.canonicalize() does not start with checkout_root.canonicalize() — a symlink inside the checkout points outside, the configured subpath contains ../ traversal, or the checkout moved so canonicalization diverges.

Common situations: A remote task repo where the task file is a symlink to another location on the machine; a configured remote task subpath like ../../something; a moved/renamed checkout confusing path resolution; \\wsl$ vs native path mismatches on Windows.

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/8809ad4449ed491f. Report an issue: GitHub.