{"record":{"id":"c7f6f270130c4f3a","repo":"jdx/mise","slug":"brew-cask-invalid-generic-artifact-parent-c7f6f2","errorCode":null,"errorMessage":"brew-cask: invalid generic artifact parent","messagePattern":"brew-cask: invalid generic artifact parent","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/packages/brew/cask/mod.rs","lineNumber":2073,"sourceCode":"            || current_groups.contains(&stat.st_gid);\n        let writable_by_untrusted = stat.st_mode & 0o002 != 0\n            || (stat.st_mode & 0o020 != 0 && (!allow_current_user || !trusted_group));\n        if !SFlag::from_bits_truncate(stat.st_mode).contains(SFlag::S_IFDIR)\n            || !trusted_owner\n            || writable_by_untrusted\n        {\n            bail!(\n                \"brew-cask: refusing operation through untrusted directory {}\",\n                directory.display()\n            );\n        }\n        Ok(())\n    };\n    let mut directory = resolved_root.to_path_buf();\n    verify(&fd, &directory)?;\n    for component in relative.components() {\n        let Component::Normal(name) = component else {\n            bail!(\"brew-cask: invalid generic artifact parent\");\n        };\n        directory.push(name);\n        fd = match openat(&fd, name, flags, Mode::empty()) {\n            Ok(fd) => fd,\n            Err(nix::errno::Errno::ENOENT) if create_missing => {\n                match nix::sys::stat::mkdirat(\n                    &fd,\n                    name,\n                    Mode::S_IRWXU | Mode::S_IRGRP | Mode::S_IXGRP | Mode::S_IROTH | Mode::S_IXOTH,\n                ) {\n                    Ok(()) | Err(nix::errno::Errno::EEXIST) => {}\n                    Err(err) => {\n                        return Err(err).wrap_err_with(|| {\n                            format!(\n                                \"brew-cask: cannot create operation directory {}\",\n                                directory.display()\n                            )\n                        });","sourceCodeStart":2055,"sourceCodeEnd":2091,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/packages/brew/cask/mod.rs#L2055-L2091","documentation":"The brew-cask installer validates every component of an artifact's path relative to its resolved parent directory before descending into it. Only plain path components (Component::Normal) are allowed; `..`, `.`-style or root/prefix components mean the artifact would escape or address outside the intended directory. The library bails to prevent path traversal during cask staging.","triggerScenarios":"Installing a cask whose generic artifact relative path contains a parent-directory component (`..`), a root component, or another non-Normal component (e.g. prefix, CurDir) while iterating relative.components() to open/create each directory level.","commonSituations":"A cask formula (or a hand-edited / malicious cask JSON) declares an artifact target like `../../etc/foo` or an absolute path instead of a path relative to the staged root; corrupted cached cask metadata after a Homebrew format change.","solutions":["Inspect the cask definition's artifact paths and remove any `..`, absolute-path, or `.` components so targets are relative and below the stage root","Update/refresh the cask source (brew tap update / mise update) in case the metadata is outdated","Report the cask as unsafe to the tap maintainer if the traversal is intentional"],"exampleFix":"// before (cask artifact declaration)\n{\"artifacts\": [{\"type\": \"generic\", \"target\": \"../../usr/local/bin/app\"}]}\n// after\n{\"artifacts\": [{\"type\": \"generic\", \"target\": \"bin/app\"}]}","handlingStrategy":"validation","validationCode":"use std::path::{Component, Path};\nfn is_safe_relative(p: &Path) -> bool {\n    p.is_relative()\n        && p.components().all(|c| matches!(c, Component::Normal(_)))\n}","typeGuard":"fn has_only_normal_components(p: &Path) -> bool {\n    p.components().all(|c| matches!(c, Component::Normal(_)))\n}","tryCatchPattern":"match result {\n    Err(e) if e.to_string().contains(\"invalid generic artifact parent\") => {\n        eprintln!(\"cask artifact path contains traversal components; fix the cask JSON\");\n    }\n    Err(e) => return Err(e),\n    Ok(v) => v,\n}","preventionTips":["Only use relative, forward paths in cask artifact declarations","Never reference paths outside the staged bundle with `..`","Validate third-party cask JSON before installing from untrusted taps"],"tags":["security","path-traversal","brew-cask"],"backgroundTag":"path-traversal-blocked","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"}