{"record":{"id":"0134d7166c6b74b7","repo":"jdx/mise","slug":"brew-cask-completion-target-must-not-contain","errorCode":null,"errorMessage":"brew-cask: completion target '{}' must not contain '..'","messagePattern":"brew-cask: completion target '(.+?)' must not contain '\\.\\.'","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/packages/brew/cask.rs","lineNumber":4426,"sourceCode":"}\n\nfn completion_target_path(shell: CompletionShell, target_name: &str) -> Result<PathBuf> {\n    let prefix = prefix::prefix();\n    let prefix_str = prefix.to_string_lossy();\n    let target_name = target_name.replace(\"$HOMEBREW_PREFIX\", prefix_str.as_ref());\n    let path = PathBuf::from(&target_name);\n    let target = if path.is_absolute() {\n        path\n    } else if target_name.contains('/') {\n        prefix.join(path)\n    } else {\n        default_completion_dir(shell).join(completion_filename(shell, &target_name)?)\n    };\n    if target\n        .components()\n        .any(|component| matches!(component, Component::ParentDir))\n    {\n        bail!(\n            \"brew-cask: completion target '{}' must not contain '..'\",\n            target.display()\n        );\n    }\n    if !target.starts_with(&prefix) {\n        bail!(\n            \"brew-cask: completion target '{}' must be under {}\",\n            target.display(),\n            prefix.display()\n        );\n    }\n    Ok(target)\n}\n\nfn generated_completion_target_path(shell: CompletionShell, base_name: &str) -> Result<PathBuf> {\n    match shell {\n        CompletionShell::Pwsh => {\n            let name = format!(\"_{}.ps1\", base_name);","sourceCodeStart":4408,"sourceCodeEnd":4444,"githubUrl":"https://github.com/jdx/mise/blob/6f52dcdf99e282ef7a7db68c81301fa4618d0f79/src/system/packages/brew/cask.rs#L4408-L4444","documentation":"Before installing a completion artifact, mise resolves the cask's completion target into a concrete path (absolute path used as-is; a name containing '/' joined onto the brew prefix; a bare name placed into the shell's default completion directory) and then rejects any result whose path components include a ParentDir ('..') component. This is a path-traversal guard: a cask must not be able to write completion files outside the intended tree via dot-dot segments.","triggerScenarios":"A cask completion stanza whose target resolves to something like '../../etc/zshrc' or 'foo/../../bar' — either an absolute target containing '..' (checked as-is) or a relative target with '/' whose join with the prefix contains a ParentDir component. The check at src/system/packages/brew/cask.rs:4426 runs on the resolved target before the additional starts_with(prefix) containment check.","commonSituations":"Hand-written or machine-generated cask metadata with sloppily built relative paths; a cask edited to 'escape' the completion directory; upstream casks that legitimately used '..' in Homebrew itself (Homebrew allows some of this) but are rejected by mise's stricter containment rules.","solutions":["Rewrite the completion target to a plain, normalized path with no '..' component — e.g. 'zsh/_tool' or an absolute path genuinely under the brew prefix.","For bare filenames, drop any directory part entirely and let mise place the file in the shell's default completion directory.","If you need a specific subdirectory, express it as a forward relative path under the prefix ('share/fish/completions/tool.fish') without traversal.","If the cask is third-party metadata you cannot edit, report it upstream or skip installing that cask with mise."],"exampleFix":"# before\n\"completions\": { \"target\": \"../../../etc/zshrc\" }\n\n# after\n\"completions\": { \"target\": \"zsh/_mytool\" }","handlingStrategy":"validation","validationCode":"# reject traversal before handing metadata to mise\npython3 - <<'EOF'\nimport json,sys\nfor t in json.load(open(sys.argv[1])).get(\"completions\",[]):\n    assert \"..\" not in t[\"target\"].split(\"/\"), t[\"target\"]\nEOF","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Run `realpath -m` on every authored completion target and store the normalized result.","Never author '..' in targets; prefer bare filenames or single-level subdirectories.","Sanitize third-party cask JSON through a normalizer before installing."],"tags":["brew-cask","completions","path-traversal","security","mise"],"backgroundTag":"path-traversal-rejected","analyzedSha":"6f52dcdf99e282ef7a7db68c81301fa4618d0f79","analyzedAt":"2026-08-22T10:14:23.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}