{"record":{"id":"c28293e0d1369c99","repo":"jdx/mise","slug":"brew-cask-git-only-path-must-stay-within-the-c","errorCode":null,"errorMessage":"brew-cask:{}: git only_path must stay within the checkout","messagePattern":"brew-cask:(.+?): git only_path must stay within the checkout","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/packages/brew/cask/fetch.rs","lineNumber":213,"sourceCode":"            let dest = extract_dir.join(entry.file_name());\n            file::rename(entry.path(), &dest)?;\n        }\n    }\n    file::remove_all(&clone_dir)?;\n    Ok(extract_dir)\n}\n\npub(super) fn git_only_path_source(\n    cask: &Cask,\n    clone_dir: &Path,\n    only_path: &Path,\n) -> Result<PathBuf> {\n    if only_path.is_absolute()\n        || only_path\n            .components()\n            .any(|component| matches!(component, Component::ParentDir | Component::Prefix(_)))\n    {\n        bail!(\n            \"brew-cask:{}: git only_path must stay within the checkout\",\n            cask.token\n        );\n    }\n    let clone_root = clone_dir.canonicalize()?;\n    let source = clone_dir.join(only_path).canonicalize().wrap_err_with(|| {\n        format!(\n            \"brew-cask:{}: git only_path does not exist: {}\",\n            cask.token,\n            only_path.display()\n        )\n    })?;\n    if !source.starts_with(&clone_root) || !source.is_dir() {\n        bail!(\n            \"brew-cask:{}: git only_path must name a directory within the checkout\",\n            cask.token\n        );\n    }","sourceCodeStart":195,"sourceCodeEnd":231,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/packages/brew/cask/fetch.rs#L195-L231","documentation":"When a cask's URL is a git repository, an optional `only_path` selects a subdirectory of the clone to stage. Before use, mise rejects any only_path that is absolute or contains a parent-dir ('..') or Windows prefix component, because such a path could escape the checkout directory. This is a path-traversal guard, so the fetch fails fast rather than copying files from outside the clone.","triggerScenarios":"Installing a git-URL cask whose url_specs.only_path is absolute (e.g. '/app'), contains '..' segments, or includes a Windows drive/prefix component; git_only_path_source is reached via fetch_git_clone_and_stage after a successful clone.","commonSituations":"Hand-written or vendored cask JSON where only_path was copied from an absolute build path; typos like '..' meant as relative shorthand; casks authored on Windows with drive-letter paths.","solutions":["Edit the cask's only_path to a relative path with no '..' segments (e.g. 'src/app' instead of '/build/src/app' or '../app').","If the desired directory is outside the repository, point the cask URL at the repository that actually contains it.","Clear the cask metadata cache and retry after fixing the cask definition."],"exampleFix":"// before (cask url_specs)\n\"only_path\": \"/build/output/App\"\n// after\n\"only_path\": \"build/output/App\"","handlingStrategy":"validation","validationCode":"fn safe_only_path(p: &std::path::Path) -> bool {\n    use std::path::Component;\n    !p.is_absolute()\n        && p.components().all(|c| !matches!(c, Component::ParentDir | Component::Prefix(_)))\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Author only_path as a repo-relative directory path with no '..' segments.","Test the cask's only_path against a plain `git clone` checkout before publishing.","Never copy absolute build-machine paths into cask metadata."],"tags":["brew","path-traversal","git","security"],"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-14T05:17:10.506Z"}