{"record":{"id":"8809ad4449ed491f","repo":"jdx/mise","slug":"remote-task-path-escapes-its-git-checkout","errorCode":null,"errorMessage":"remote task path escapes its Git checkout: {}","messagePattern":"remote task path escapes its Git checkout: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/task/task_file_providers/remote_task_git.rs","lineNumber":81,"sourceCode":"            url_without_path: url_without_path.to_string(),\n            path: path.to_string(),\n            branch,\n        }\n    }\n}\n\n/// Ensure a remote task path resolves inside its Git checkout and points at a\n/// regular file or directory.\npub(crate) fn validate_remote_git_path(\n    checkout_root: &Path,\n    path: &Path,\n) -> Result<std::fs::Metadata> {\n    let metadata = path.symlink_metadata()?;\n    if !path\n        .canonicalize()?\n        .starts_with(checkout_root.canonicalize()?)\n    {\n        eyre::bail!(\n            \"remote task path escapes its Git checkout: {}\",\n            display_path(path)\n        );\n    }\n    if metadata.file_type().is_file() || metadata.file_type().is_dir() {\n        return Ok(metadata);\n    }\n    eyre::bail!(\n        \"remote task path is not a regular file or directory: {}\",\n        display_path(path)\n    )\n}\n\nimpl RemoteTaskGit {\n    /// Make fetched task files executable while leaving task include directories intact.\n    fn prepare_remote_path(checkout_root: &Path, path: &Path) -> Result<()> {\n        if validate_remote_git_path(checkout_root, path)?\n            .file_type()","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/task/task_file_providers/remote_task_git.rs#L63-L99","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Fix the remote task path to point inside the checkout (no ../ traversal, no outbound symlinks)","Remove or replace outbound symlinks in the task repository","Re-clone the repository so paths resolve within the new checkout root","If you own the repo, restructure so task files are real files inside the repo"],"exampleFix":"# before: remote task path escaping the checkout\nsource = \"https://github.com/org/tasks#../../host-tool\"\n# after\nsource = \"https://github.com/org/tasks#tasks/build.toml\"","handlingStrategy":"validation","validationCode":"import fs from \"fs\";\nimport path from \"path\";\nfunction insideCheckout(root, p) {\n  const realRoot = fs.realpathSync(root);\n  const realP = fs.realpathSync(path.join(root, p));\n  return realP.startsWith(realRoot + path.sep);\n}","typeGuard":"fn stays_in_checkout(root: &Path, p: &Path) -> bool {\n    match (p.canonicalize(), root.canonicalize()) {\n        (Ok(p), Ok(r)) => p.starts_with(r),\n        _ => false,\n    }\n}","tryCatchPattern":"try {\n  const meta = fs.lstatSync(taskPath);\n  if (meta.isSymbolicLink() && !insideCheckout(checkoutRoot, taskPath)) {\n    throw new Error(\"remote task path escapes checkout\");\n  }\n} catch (e) {\n  console.error(\"Use a path inside the cloned repo and avoid outbound symlinks\");\n  throw e;\n}","preventionTips":["Never configure remote task paths with ../ components","Audit remote task repos for symlinks leaving the checkout","Clone remote task repos fresh rather than reusing moved directories","Treat remote tasks as untrusted and pin them to a commit SHA"],"tags":["security","path-traversal","git","task"],"backgroundTag":"path-traversal-blocked","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}