jdx/mise · error

brew-cask: refusing to stage cask payload through a path out

Error message

brew-cask: refusing to stage cask payload through a path outside the caskroom: {}

What it means

A security guard during cask payload staging: the computed destination (caskroom joined with the staged relative path) does not resolve to a location inside the caskroom, likely due to symlink traversal or '..' components. mise bails instead of writing outside the caskroom.

Source

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

        .iter()
        .filter_map(|app| find_app(stage, &app.source))
        .map(|source| file::desymlink_path(&source))
        .collect();
    for entry in std::fs::read_dir(stage)? {
        let source = entry?.path();
        if !path_starts_with_resolved_root(&source, stage) {
            continue;
        }
        let resolved = file::desymlink_path(&source);
        if app_sources.contains(&resolved) {
            continue;
        }
        let Some(relative) = staged_relative_path(stage, &source) else {
            continue;
        };
        let target = caskroom.join(&relative);
        if !path_starts_with_resolved_root(&target, caskroom) {
            bail!(
                "brew-cask: refusing to stage cask payload through a path outside the caskroom: {}",
                target.display()
            );
        }
        if target.symlink_metadata().is_ok() {
            continue;
        }
        if let Some(parent) = target.parent() {
            file::create_dir_all(parent)?;
        }
        if source.is_dir() {
            file::copy_dir_all_preserve_symlinks(&source, &target)?;
        } else {
            file::copy(&source, &target)?;
        }
    }
    Ok(())
}

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Remove any '..' components or symlink indirection from the staged payload paths
  2. Inspect the caskroom for pre-existing symlinks pointing outside it and delete them
  3. Re-extract the cask archive from a trusted source and retry the install

Example fix

// before
relative = "../../etc/foo" // escapes caskroom
// after
relative = "scripts/foo" // stays under caskroom
Defensive patterns

Strategy: validation

Validate before calling

let target = caskroom.join(&relative);
if !target.starts_with(&caskroom) || relative.components().any(|c| c == Component::ParentDir) {
    return Err("payload path escapes the caskroom");
}

Prevention

When it happens

Trigger: Staging a cask payload file whose relative path escapes the caskroom via '..' components or a symlink inside the caskroom pointing elsewhere, detected by path_starts_with_resolved_root before any write.

Common situations: Crafted or corrupted cask archives containing path-traversal entries, a previously created symlink in the caskroom redirected at another directory, or a stage root itself being a symlink into a different tree.

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/77abecf29da5c6e3. Report an issue: GitHub.