{"record":{"id":"d42f00e492013309","repo":"jdx/mise","slug":"brew-cask-refusing-elevated-operation-through-mut-d42f00","errorCode":null,"errorMessage":"brew-cask: refusing elevated operation through mutable directory {}","messagePattern":"brew-cask: refusing elevated operation through mutable directory (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/packages/brew/cask/mod.rs","lineNumber":1730,"sourceCode":"        target\n            .file_name()\n            .ok_or_else(|| eyre!(\"brew-cask: generic artifact target has no filename\"))?,\n    ))\n}\n\n#[cfg(unix)]\nfn validate_strict_elevated_ancestors(path: &Path) -> Result<()> {\n    use std::os::unix::fs::MetadataExt;\n    let stable_prefix = file::desymlink_path(&prefix::prefix());\n    for directory in path.ancestors() {\n        let metadata = directory.symlink_metadata()?;\n        if !strict_elevated_directory_is_trusted(\n            directory,\n            &stable_prefix,\n            metadata.uid(),\n            metadata.mode(),\n        ) {\n            bail!(\n                \"brew-cask: refusing elevated operation through mutable directory {}\",\n                directory.display()\n            );\n        }\n    }\n    Ok(())\n}\n\n#[cfg(unix)]\nfn strict_elevated_directory_is_trusted(\n    directory: &Path,\n    stable_prefix: &Path,\n    uid: u32,\n    mode: u32,\n) -> bool {\n    uid == 0\n        && mode & 0o002 == 0\n        // Intel Homebrew conventionally uses root:admin 0775 for /usr/local.","sourceCodeStart":1712,"sourceCodeEnd":1748,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/packages/brew/cask/mod.rs#L1712-L1748","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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","Check for ACLs with ls -le on macOS and strip permissive ones (chmod -N)","Audit every path component to the target directory, not just the leaf","Avoid running installs as different users against the same caskroom/target paths"],"exampleFix":"# before\ndrwxrwxrwx  admin  /Applications/MyApp\n# after\nsudo chown root:admin /Applications/MyApp && sudo chmod 755 /Applications/MyApp","handlingStrategy":"validation","validationCode":"// verify the whole path to a system target is root/current-user owned and not group/other writable\nlet mut dir = target.parent().unwrap().to_path_buf();\nloop {\n    let md = std::fs::metadata(&dir)?;\n    if md.uid() != 0 && md.uid() != nix::unistd::geteuid().as_raw() { return Err(format!(\"untrusted owner: {}\", dir.display())); }\n    if md.mode() & 0o022 != 0 { return Err(format!(\"group/other-writable: {}\", dir.display())); }\n    if !dir.pop() { break; }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep system target directories (e.g. /Applications subdirs) owned by root:admin with mode 755","Check for permissive ACLs (ls -le on macOS) and strip them with chmod -N","Do not run installs as different users against shared target paths","Audit writable-by-others directories: find /Applications -perm -o+w"],"tags":["security","permissions","elevation","brew-cask"],"backgroundTag":"insufficient-permissions","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}