jdx/mise · error · eyre::Report
brew-cask: app target '{target_name}' must not contain '..'
Error message
brew-cask: app target '{target_name}' must not contain '..' What it means
When a cask 'app' target contains '/', mise expands $HOMEBREW_PREFIX, parses it into path components, and rejects any Component::ParentDir ('..'). This keeps artifact linking confined so a cask cannot write a bundle outside its declared destination — a standard path-traversal containment check before symlinks are created, potentially with elevated privileges.
Source
Thrown at src/system/packages/brew/cask.rs:5989
return false;
}
}
true
}
fn app_target_path(target_name: &str) -> Result<PathBuf> {
let app_dir = target_app_dir()?;
if target_name.contains('\0') {
bail!("brew-cask: app target contains NUL");
}
if target_name.contains('/') {
let target = target_name.replace("$HOMEBREW_PREFIX", &prefix::prefix().to_string_lossy());
let path = PathBuf::from(target);
if path
.components()
.any(|component| matches!(component, Component::ParentDir))
{
bail!("brew-cask: app target '{target_name}' must not contain '..'");
}
if path.is_absolute() {
let prefix_app_dir = prefix::prefix().join("Applications");
if path.starts_with(&app_dir) || path.starts_with(&prefix_app_dir) {
return Ok(path);
}
// Casks routinely hardcode an absolute `/Applications/Foo.app`
// target. When an override appdir is configured, relocate such a
// target into it (preserving any subdirectories) rather than
// rejecting it. `$HOMEBREW_PREFIX`-anchored targets are handled by
// the check above and are never relocated.
if app_dir != Path::new(DEFAULT_APP_DIR)
&& let Ok(rest) = path.strip_prefix(DEFAULT_APP_DIR)
{
return Ok(app_dir.join(rest));
}
bail!(
"brew-cask: app target '{target_name}' must be under {}",View on GitHub (pinned to 9dcfcaa0dc)
Solutions
- Fix the cask's target to a clean path: a bare bundle name ("Foo.app"), or an absolute path without '..' segments
- If the target comes from a tap, report the malformed artifact stanza upstream or remove the tap
- Verify with `brew info --json=v2 --cask <token>` what target Homebrew actually sees
- Update mise before assuming the rejection is a bug
Example fix
// before (cask artifact target) "target": "/Applications/../../Library/Foo.app" // after "target": "Foo.app"
Defensive patterns
Strategy: type-guard
Type guard
use std::path::{Component, Path};
fn app_target_has_no_dotdot(name: &str, homebrew_prefix: &str) -> bool {
let expanded = name.replace("$HOMEBREW_PREFIX", homebrew_prefix);
!Path::new(&expanded)
.components()
.any(|c| matches!(c, Component::ParentDir))
} Try / catch
match app_target_path(name) {
Ok(p) => p,
Err(e) if e.to_string().contains("must not contain '..'") => {
warn!("skipping cask app target with '..': {name}");
continue;
}
Err(e) => Err(e),
} Prevention
- Write cask targets as bare bundle names unless an absolute path is required
- Lint custom casks for '..' segments before installing
- Remember $HOMEBREW_PREFIX is expanded first — a '..' after the prefix counts too
When it happens
Trigger: An app artifact target such as "../Foo.app", "/Applications/../../Library/Foo.app", or any target whose component list contains '..' reaching app_target_path.
Common situations: Hand-edited cask override files; a malicious tap attempting to escape /Applications; a mis-typed target in a custom local cask.
Related errors
- brew-cask:{}: invalid {kind} {field} path {}
- brew-cask: app target contains NUL
- brew-cask: binary target contains NUL
- brew-cask: binary target '{}' must not contain '..'
- mise upgrade --monorepo is not implemented yet
AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17).
Data as JSON: /api/errors/c0e55cf90ff74871.
Report an issue: GitHub.