{"record":{"id":"0ac05c3adc83393e","repo":"jdx/mise","slug":"brew-cask-temporary-artifact-directory-is-not-pri-0ac05c","errorCode":null,"errorMessage":"brew-cask: temporary artifact directory is not private","messagePattern":"brew-cask: temporary artifact directory is not private","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/packages/brew/cask/mod.rs","lineNumber":1625,"sourceCode":"    }\n}\n\n#[cfg(unix)]\nfn copy_generic_artifact_unprivileged(from: &Path, to: &Path) -> Result<()> {\n    let parent = open_trusted_operation_parent(to, true, true)?;\n    let name = to\n        .file_name()\n        .ok_or_else(|| eyre!(\"brew-cask: generic artifact target has no filename\"))?;\n    let staging_name = format!(\".mise-copy-{}\", crate::rand::random_string(16));\n    nix::sys::stat::mkdirat(\n        &parent.fd,\n        staging_name.as_str(),\n        nix::sys::stat::Mode::S_IRWXU,\n    )?;\n    let staging_fd = open_dir_nofollow_at(&parent.fd, staging_name.as_str())?;\n    let staging_stat = nix::sys::stat::fstat(&staging_fd)?;\n    if staging_stat.st_uid != nix::unistd::geteuid().as_raw() || staging_stat.st_mode & 0o077 != 0 {\n        bail!(\"brew-cask: temporary artifact directory is not private\");\n    }\n    let staging = TrustedOperationParent { fd: staging_fd };\n    let temporary_name = std::ffi::OsStr::new(\"payload\");\n    match copy_cask_artifact_at(from, &staging.fd, temporary_name) {\n        Ok(()) => {\n            match nix::fcntl::renameat(&staging.fd, temporary_name, &parent.fd, name)\n                .wrap_err_with(|| format!(\"failed to install {}\", to.display()))\n            {\n                Ok(()) => {\n                    remove_private_staging_dir(&parent, &staging, staging_name.as_ref())?;\n                    Ok(())\n                }\n                Err(err) => {\n                    remove_all_at(&staging.fd, temporary_name).wrap_err_with(|| {\n                            format!(\n                                \"failed to clean up temporary generic artifact after rename failed: {err:#}\"\n                            )\n                        })?;","sourceCodeStart":1607,"sourceCodeEnd":1643,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/packages/brew/cask/mod.rs#L1607-L1643","documentation":"When copying a generic artifact unprivileged, mise creates a private staging directory (mode 0700) next to the target and then verifies via fstat that the opened directory is owned by the effective UID and has no group/other permission bits. If the directory's owner or mode does not match (uid != euid or mode & 0o077 != 0), the copy is aborted — this guards against a pre-existing or tampered directory letting another local user read the payload or swap in files during the copy/rename.","triggerScenarios":"copy_generic_artifact_unprivileged creates a .mise-copy-XXXX staging dir via mkdirat with S_IRWXU, opens it nofollow, and fstat shows either an owner different from the effective user or permission bits for group/other set. This would indicate filesystem-level tampering, an unusual umask/ACL effect, or a compromised parent directory.","commonSituations":"Running on a system where another user or a setuid-hijacked process manipulates the target directory; exotic mounts (some network/container filesystems) that do not honor requested modes or rewrite ownership; overly permissive umask handling or ACLs on the target's parent directory.","solutions":["Check ownership/permissions on the caskroom target's parent directory (ls -la) and fix so your user owns it with sane modes (0700/0755)","Verify the filesystem backing the caskroom is a normal local filesystem, not a mount that alters modes/uids (NFS, bind mounts, some overlayfs setups)","Re-run the install; if it persists, inspect for other local users/processes interfering with the caskroom","Ensure mise runs as the same user that owns the installation directories (avoid mixing sudo and non-sudo installs)"],"exampleFix":"// before (mixed-ownership caskroom after a sudo install)\nsudo mise use cask:<name>\n// after\nmise use cask:<name>   # run as the owning user; chown -R $(id -u) the caskroom if needed","handlingStrategy":"validation","validationCode":"// pre-check the target parent dir before invoking the install\nlet md = std::fs::metadata(target.parent().unwrap())?;\nif md.uid() != nix::unistd::geteuid().as_raw() || md.mode() & 0o077 != 0 {\n    return Err(format!(\"{} is not private or not owned by you\", target.display()));\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always run mise as the same user that owns the install directories","Avoid installing to directories on network/overlay filesystems that rewrite modes or uids","Never mix sudo and non-sudo cask installs against the same paths","Periodically audit caskroom permissions (find ~/.mise -not -user $(id -un))"],"tags":["security","permissions","filesystem","unix"],"backgroundTag":"permission-denied","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"}