jdx/mise · error

brew-cask: refusing operation through untrusted directory {}

Error message

brew-cask: refusing operation through untrusted directory {}

What it means

This check walks each directory on the operation's path and refuses to proceed if any component is not a trusted directory. A directory is trusted only if it is owned by a trusted owner (root or, when allowed, the current user), is a real directory (not a symlink), is not world-writable (`0o002` clear), and is not group-writable by an untrusted group (`0o020` clear unless the owning group is trusted and the current user counts as trusted). This blocks symlink/permission-based attacks where an unprivileged user could hijack a path component and redirect privileged writes.

Source

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

    let current_uid = nix::unistd::geteuid().as_raw();
    let current_gid = nix::unistd::getegid().as_raw();
    let current_groups = current_process_groups()?;
    let sudo_uid = sudo_invoking_id(current_uid, "SUDO_UID");
    let sudo_gid = sudo_invoking_id(current_uid, "SUDO_GID");
    let verify = |fd: &std::os::fd::OwnedFd, directory: &Path| -> Result<()> {
        let stat = fstat(fd)?;
        let owner_is_user = stat.st_uid == current_uid || Some(stat.st_uid) == sudo_uid;
        let trusted_owner = stat.st_uid == 0 || (allow_current_user && owner_is_user);
        let trusted_group = stat.st_gid == current_gid
            || Some(stat.st_gid) == sudo_gid
            || current_groups.contains(&stat.st_gid);
        let writable_by_untrusted = stat.st_mode & 0o002 != 0
            || (stat.st_mode & 0o020 != 0 && (!allow_current_user || !trusted_group));
        if !SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::S_IFDIR)
            || !trusted_owner
            || writable_by_untrusted
        {
            bail!(
                "brew-cask: refusing operation through untrusted directory {}",
                directory.display()
            );
        }
        Ok(())
    };
    let mut directory = resolved_root.to_path_buf();
    verify(&fd, &directory)?;
    for component in relative.components() {
        let Component::Normal(name) = component else {
            bail!("brew-cask: invalid generic artifact parent");
        };
        directory.push(name);
        fd = match openat(&fd, name, flags, Mode::empty()) {
            Ok(fd) => fd,
            Err(nix::errno::Errno::ENOENT) if create_missing => {
                match nix::sys::stat::mkdirat(
                    &fd,

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Identify the offending directory from the message and tighten its permissions: `chmod o-w <dir>` (and `chmod g-w` if group-writable by an untrusted group).
  2. Ensure the directory is owned by root or the current user: `sudo chown <owner> <dir>`.
  3. Replace any symlink in the path with the real directory, since symlink components are rejected.
  4. Reinstall or relocate the Homebrew prefix so all ancestor directories are root-owned and not writable by other users (the standard Homebrew permission model).

Example fix

// before: inspect a world-writable prefix component
drwxrwxrwx  /opt/homebrew
// after: fix ownership and permissions so the trust check passes
sudo chown root:admin /opt/homebrew
sudo chmod 755 /opt/homebrew
Defensive patterns

Strategy: validation

Validate before calling

use std::os::unix::fs::MetadataExt;
use std::path::Path;
fn dir_is_trusted(p: &Path, uid: u32, trusted_gids: &[u32]) -> bool {
    std::fs::metadata(p).map(|md| {
        let mode = md.mode();
        md.is_dir()
            && !p.is_symlink()
            && (md.uid() == 0 || md.uid() == uid)
            && mode & 0o002 == 0
            && (mode & 0o020 == 0 || trusted_gids.contains(&md.gid()))
    }).unwrap_or(false)
}

Type guard

fn is_trusted_dir(p: &std::path::Path) -> bool {
    use std::os::unix::fs::MetadataExt;
    std::fs::metadata(p).map(|m| {
        m.is_dir() && m.mode() & 0o002 == 0 && m.mode() & 0o020 == 0
    }).unwrap_or(false)
}

Prevention

When it happens

Trigger: A brew-cask elevated operation whose path traverses a directory that fails the trust test: world-writable (mode `o+w`), group-writable by a group not trusted for the current user, a symlink instead of a real directory, or owned by an unexpected user — detected by the `stat`-based closure around mod.rs:2062.

Common situations: A shared machine where the Homebrew prefix or a parent (e.g. `/opt`, `/usr/local`) is group-writable; a home-directory prefix on a multi-user system with a permissive umask; symlinked prefix paths after migrating Homebrew; running as a user whose primary group differs from the prefix's group.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


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