{"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":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/packages/brew/cask/fetch.rs","lineNumber":209,"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":191,"sourceCodeEnd":227,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/system/packages/brew/cask/fetch.rs#L191-L227","documentation":"When a cask is fetched from a git repository with an `only_path` (fetching a subdirectory of the checkout), the path must be relative and must not escape the clone. `git_only_path_source` rejects absolute paths and any component that is ParentDir (\"..\") or a Prefix (e.g. \"C:\\\\\" on Windows) before any filesystem access, preventing path traversal outside the cloned repository.","triggerScenarios":"fetch_git_clone_and_stage calls git_only_path_source with an only_path that is absolute or contains \"..\" / a Windows prefix component, e.g. \"/etc/passwd\" or \"../../foo\".","commonSituations":"A cask's git url stanza specifies a subdirectory with a leading slash or \"..\"; config templating builds the only_path from user input that includes absolute paths.","solutions":["Rewrite only_path as a plain relative path within the repo (e.g. \"subdir/tool\" instead of \"/subdir/tool\")","Remove any \"..\" segments and reference the intended path from the clone root","Fix the cask's git stanza in the tap if upstream data is malformed"],"exampleFix":"// before (cask git stanza)\nurl \"https://github.com/x/y.git\", only_path: \"/tools/foo\"\n// after\nurl \"https://github.com/x/y.git\", only_path: \"tools/foo\"","handlingStrategy":"validation","validationCode":"// Validate only_path before fetching a git cask\nfn safe_only_path(p: &str) -> bool {\n    !p.is_absolute()\n        && std::path::Path::new(p).components().all(|c| {\n            matches!(c, std::path::Component::Normal(_) | std::path::Component::CurDir)\n        })\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Write only_path as a repo-relative directory path","Strip leading slashes and '..' segments from any templated input","Test cask git stanzas against a fresh clone before publishing"],"tags":["homebrew","cask","git","path-traversal"],"backgroundTag":"path-traversal-blocked","analyzedSha":"533346cc374382b41ec5ff70536252b2e96e725c","analyzedAt":"2026-09-17T13:35:38.149Z","contentChangedAt":"2026-09-17T13:35:38.149Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}