jdx/mise · error

brew-cask: ' ' must stay below Applications

Error message

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

What it means

reject_appdir_escape validates that a staged artifact's relative path stays strictly below the Applications directory: the path must be non-empty and every component must be a plain Normal component. Any '..', '.', root-prefix, or other special component (or an empty path) causes this bail, preventing cask artifacts from being written outside the intended appdir subtree.

Solutions

  1. Fix the cask artifact's relative path so it is non-empty and contains only plain path components (no '..', '.', or absolute prefixes)
  2. If the artifact genuinely belongs elsewhere, adjust the cask's appdir/staging root instead of using '..' in the path
  3. Verify you are using a trusted cask definition — this rejection often indicates a corrupt or malicious stanza
  4. Re-parse the cask config to ensure empty strings were not produced by a parsing bug

Example fix

// before: escapes Applications
let relative = Path::new("../Utilities/MyApp.app");
// after: stays below Applications
let relative = Path::new("MyApp.app");
Defensive patterns

Strategy: validation

Validate before calling

use std::path::{Component, Path};
fn stays_below_appdir(relative: &Path) -> bool {
    !relative.as_os_str().is_empty()
        && relative.components().all(|c| matches!(c, Component::Normal(_)))
}

Prevention

When it happens

Trigger: Staging a cask artifact (app/pkg/binary stanza) whose declared relative target path is empty or contains a non-Normal Path component such as ParentDir ('..'), CurDir ('.'), a RootDir/Prefix, per reject_appdir_escape(relative, kind, name).

Common situations: A cask stanza with a target like '../Utilities/Foo.app' or '/ absolute/path'; empty target strings produced by bad parsing; casks upgraded to a version with a malformed artifact path.

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/995f7f5ffe1c86e0. Report an issue: GitHub.

Appendix: source

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

    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 533346cc37)