jdx/mise · error

brew-cask: app target

Error message

brew-cask: app target '{target_name}' must not contain '..'

What it means

`app_target_path` rejects app targets whose path contains a `..` component (Component::ParentDir). Parent-directory traversal could escape the intended Applications directories, so any target containing `..` is refused as a path-traversal guard.

Solutions

  1. Rewrite the app target as a direct absolute path under `$HOMEBREW_PREFIX/Applications` (or `/Applications`), removing all `..` segments.
  2. Use `$HOMEBREW_PREFIX` expansion instead of relative climbing to reach the desired location.
  3. If the target intentionally lives outside allowed roots, move the app into the prefix Applications dir and target it there.

Example fix

# before
app target: "$HOMEBREW_PREFIX/../../Apps/MyApp.app"

# after
app target: "$HOMEBREW_PREFIX/Applications/MyApp.app"
Defensive patterns

Strategy: validation

Validate before calling

let p = std::path::PathBuf::from(target_name.replace("$HOMEBREW_PREFIX", prefix));
if p.components().any(|c| matches!(c, std::path::Component::ParentDir)) {
    return Err("app target must not contain '..'".into());
}

Type guard

fn has_parent_dir(p: &std::path::Path) -> bool {
    p.components().any(|c| matches!(c, std::path::Component::ParentDir))
}

Prevention

When it happens

Trigger: Any app target string that yields a `..` component after `$HOMEBREW_PREFIX` expansion — e.g. `../Apps/Foo.app`, `$HOMEBREW_PREFIX/../../Foo.app` — passed through the app install/adopt/validate call sites.

Common situations: A typo or misused variable in a custom app target; a crafted cask definition attempting traversal; copy-pasted paths with stray `..` segments.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17). Data as JSON: /api/errors/f85a7483778c81ce. Report an issue: GitHub.

Appendix: source

Thrown at src/system/packages/brew/cask/paths.rs:124

            return false;
        }
    }
    true
}

pub(super) 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 533346cc37)