jdx/mise · error

brew-cask: {kind} '{name}' must stay below Applications

Error message

brew-cask: {kind} '{name}' must stay below Applications

What it means

reject_appdir_escape validates that app/artifact target paths stay strictly beneath the Applications directory: the relative path must be non-empty and composed only of normal components. Empty paths or paths containing '..', '.', root, or prefix components are rejected.

Source

Thrown at src/system/packages/brew/cask/mod.rs:3365

    let relative = source.strip_prefix(final_caskroom).ok()?;
    if relative
        .components()
        .any(|component| matches!(component, Component::ParentDir))
    {
        return None;
    }
    Some(root.join(relative))
}

/// Rejects an empty relative path and any component that could climb out of
/// `$APPDIR` (`..`, a root, a prefix).
fn reject_appdir_escape(relative: &Path, kind: &str, name: &str) -> Result<()> {
    if relative.components().next().is_none()
        || relative
            .components()
            .any(|component| !matches!(component, Component::Normal(_)))
    {
        bail!("brew-cask: {kind} '{name}' must stay below Applications");
    }
    Ok(())
}

fn resolve_symlink_target(link: &Path, target: PathBuf) -> PathBuf {
    if target.is_absolute() {
        target
    } else {
        link.parent()
            .map(|parent| parent.join(&target))
            .unwrap_or(target)
    }
}

fn durable_internal_symlink_source(stage: &Path, source: &Path, link: &Path) -> Option<PathBuf> {
    if !source.is_absolute()
        || staged_relative_path(stage, source).is_none()
        || staged_relative_path(stage, link).is_none()

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Set the app target to a simple relative name like `Foo.app` that lives directly under Applications
  2. Remove `..`, absolute, or special path components from the target
  3. Check the upstream Homebrew cask for the canonical target name

Example fix

// before
target = "../Utilities/Foo.app"
// after
target = "Foo.app"
Defensive patterns

Strategy: validation

Validate before calling

let rel = Path::new(target_name);
let ok = !target_name.is_empty()
    && rel.is_relative()
    && rel.components().all(|c| matches!(c, Component::Normal(_)));
if !ok {
    return Err("app target must be a simple path under Applications");
}

Prevention

When it happens

Trigger: Installing a cask app (or similar kind) whose resolved target name is empty, absolute, or contains non-Normal components such as `..`, `/`, or a drive/root prefix.

Common situations: A cask stanza targeting a location outside /Applications (e.g. `../Utilities/Foo.app` or an absolute `/Library/Foo.app`), or a target template that expanded to an empty string.

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@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/995f7f5ffe1c86e0. Report an issue: GitHub.