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

  1. Fix the cask's target to a clean path: a bare bundle name ("Foo.app"), or an absolute path without '..' segments
  2. If the target comes from a tap, report the malformed artifact stanza upstream or remove the tap
  3. Verify with `brew info --json=v2 --cask <token>` what target Homebrew actually sees
  4. 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

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


AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17). Data as JSON: /api/errors/c0e55cf90ff74871. Report an issue: GitHub.