jdx/mise · error

brew-cask: refusing to stage generic artifact through a path

Error message

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

What it means

Generic artifacts are also copied into a temporary Caskroom so Homebrew-style layouts stay intact. After joining the source's stage-relative path onto the temporary caskroom, mise re-verifies that the joined path is still inside that caskroot after resolution. If the relative path escapes (via symlinks or '..'), staging is refused — otherwise the trusted caskroom copy could write outside the caskroom it was scoped to.

Source

Thrown at src/system/packages/brew/cask.rs:1720

fn install_generic_artifact(
    stage: &Path,
    temporary_caskroom: &Path,
    artifact: &GenericArtifact,
    targets: &mut FlightTargetTransaction,
) -> Result<()> {
    let source = find_artifact_matching(stage, &artifact.source, |_| true)
        .ok_or_else(|| eyre!("brew-cask: artifact '{}' was not found", artifact.source))?;
    if !path_starts_with_resolved_root(&source, stage) {
        bail!(
            "brew-cask: refusing generic artifact source outside the extraction root: {}",
            source.display()
        );
    }
    let target = generic_artifact_target_path(&artifact.target)?;
    let relative_source = source.strip_prefix(stage)?;
    let caskroom_source = temporary_caskroom.join(relative_source);
    if !path_starts_with_resolved_root(&caskroom_source, temporary_caskroom) {
        bail!(
            "brew-cask: refusing to stage generic artifact through a path outside the caskroom: {}",
            caskroom_source.display()
        );
    }
    #[cfg(not(unix))]
    if let Some(parent) = target.parent() {
        file::create_dir_all(parent)?;
    }
    let elevated_target = targets.protect_generic(&target)?;
    copy_generic_artifact(&source, &target, elevated_target.as_deref())?;
    if let Some(parent) = caskroom_source.parent() {
        file::create_dir_all(parent)?;
    }
    file::make_symlink(&target, &caskroom_source)?;
    targets.record_installed(target);
    Ok(())
}

View on GitHub (pinned to 9dcfcaa0dc)

Solutions

  1. Audit the cask's archive for symlinks in generic artifact paths and report the cask if any escape
  2. If you maintain the tap, ship real files (dereference symlinks when packaging)
  3. Do not bypass the guard by pre-creating caskroom paths — the check is scoped exactly to what the cask may write
Defensive patterns

Strategy: try-catch

Try / catch

Catch 'refusing to stage generic artifact through a path outside the caskroom' as a terminal security failure for that cask; log token and artifact and report. Do not pre-create or relax caskroom directories to work around it.

Prevention

When it happens

Trigger: An artifact whose relative_source, once symlinks under the temporary caskroom resolve, points outside the temporary caskroom — e.g. a staged symlink chained to ../../../, or a directory swapped during staging.

Common situations: Maliciously crafted cask archives; taps that package symlink trees; races where staged content is replaced between extraction and staging.

Related errors


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