jdx/mise · error

brew-cask: refusing elevated operation through mutable direc

Error message

brew-cask: refusing elevated operation through mutable directory {}

What it means

Certain cask operations may run with elevated (sudo) privileges while touching user-owned paths. Before doing so, mise checks each directory on the path with strict_elevated_directory_is_trusted (ownership, mode, and position under a stable prefix). If any directory on the path is mutable — owned by someone other than root or the current user, or group/other-writable — the elevated operation is refused, because a local attacker could swap the directory to redirect privileged writes.

Source

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

        target
            .file_name()
            .ok_or_else(|| eyre!("brew-cask: generic artifact target has no filename"))?,
    ))
}

#[cfg(unix)]
fn validate_strict_elevated_ancestors(path: &Path) -> Result<()> {
    use std::os::unix::fs::MetadataExt;
    let stable_prefix = file::desymlink_path(&prefix::prefix());
    for directory in path.ancestors() {
        let metadata = directory.symlink_metadata()?;
        if !strict_elevated_directory_is_trusted(
            directory,
            &stable_prefix,
            metadata.uid(),
            metadata.mode(),
        ) {
            bail!(
                "brew-cask: refusing elevated operation through mutable directory {}",
                directory.display()
            );
        }
    }
    Ok(())
}

#[cfg(unix)]
fn strict_elevated_directory_is_trusted(
    directory: &Path,
    stable_prefix: &Path,
    uid: u32,
    mode: u32,
) -> bool {
    uid == 0
        && mode & 0o002 == 0
        // Intel Homebrew conventionally uses root:admin 0775 for /usr/local.

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Fix the flagged directory: sudo chown root:admin <dir> (or your user) and sudo chmod o-w,g-w <dir> so it is not writable by others
  2. Check for ACLs with ls -le on macOS and strip permissive ones (chmod -N)
  3. Audit every path component to the target directory, not just the leaf
  4. Avoid running installs as different users against the same caskroom/target paths

Example fix

# before
drwxrwxrwx  admin  /Applications/MyApp
# after
sudo chown root:admin /Applications/MyApp && sudo chmod 755 /Applications/MyApp
Defensive patterns

Strategy: validation

Validate before calling

// verify the whole path to a system target is root/current-user owned and not group/other writable
let mut dir = target.parent().unwrap().to_path_buf();
loop {
    let md = std::fs::metadata(&dir)?;
    if md.uid() != 0 && md.uid() != nix::unistd::geteuid().as_raw() { return Err(format!("untrusted owner: {}", dir.display())); }
    if md.mode() & 0o022 != 0 { return Err(format!("group/other-writable: {}", dir.display())); }
    if !dir.pop() { break; }
}

Prevention

When it happens

Trigger: An elevated cask operation (e.g. installing to /Applications or another system path via FlightSudo) walks the target's parent directories; one of them fails strict_elevated_directory_is_trusted because its uid differs from root/current user or its mode is group/other-writable.

Common situations: A /Applications or /opt subdirectory chowned to another user or made world-writable; a shared machine where an admin relaxed permissions; macOS with a directory having an ACL granting write to others; running mise under a different user than the one that created intermediate directories.

Understand the failure class

Background: "You do not have permission" / 403 Forbidden errors: authenticated but not allowed — causes and fixes across open-source libraries — this error's family across 31 libraries.

Related errors


AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/d42f00e492013309. Report an issue: GitHub.