{"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":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/packages/brew/cask/mod.rs","lineNumber":2290,"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":2272,"sourceCodeEnd":2308,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/system/packages/brew/cask/mod.rs#L2272-L2308","documentation":"While resolving (and optionally creating) the parent directories of a generic artifact target via fd-based `openat` traversal, mise requires every path component to be a plain `Normal` component. Components like `..`, `.`, or a root/empty component abort the traversal, since relative paths with up-level components could escape the resolved root.","triggerScenarios":"A cask generic artifact `target` normalizes to a relative path containing `..`, `.`, or non-normal components when walking from the resolved root in `open_trusted_operation_parent`-style traversal.","commonSituations":"Hand-edited cask stanzas using targets like \"../share/foo\" or \"/abs/path\" where a relative path is expected; generated targets built by string concatenation including parent references.","solutions":["Fix the cask's `target` to a clean relative path without `..` or `.` components","Normalize the target with a path library before installing if you generate the stanza programmatically","Update the cask to a version with a corrected target","Report the cask upstream if it ships an invalid target"],"exampleFix":"// before\n// target: \"../shared/Foo.app\"\n// after\n// target: \"shared/Foo.app\"","handlingStrategy":"validation","validationCode":"let rel = Path::new(&cask_target);\nfor c in rel.components() {\n    if !matches!(c, std::path::Component::Normal(_)) {\n        return Err(format!(\"target contains non-normal component: {c:?}\"));\n    }\n}","typeGuard":"fn is_clean_relative(p: &Path) -> bool {\n    p.is_relative() && p.components().all(|c| matches!(c, std::path::Component::Normal(_)))\n}","tryCatchPattern":null,"preventionTips":["Never use `..` or `.` in artifact targets","Normalize generated paths before writing cask stanzas","Validate targets in CI when authoring casks"],"tags":["security","path-traversal","brew-cask","path-validation"],"backgroundTag":"path-traversal-blocked","analyzedSha":"533346cc374382b41ec5ff70536252b2e96e725c","analyzedAt":"2026-09-17T13:35:38.149Z","contentChangedAt":"2026-09-17T13:35:38.149Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}