jdx/mise · error

brew-cask: invalid generic artifact parent

Error message

brew-cask: invalid generic artifact parent

What it means

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.

Solutions

  1. Fix the cask's `target` to a clean relative path without `..` or `.` components
  2. Normalize the target with a path library before installing if you generate the stanza programmatically
  3. Update the cask to a version with a corrected target
  4. Report the cask upstream if it ships an invalid target

Example fix

// before
// target: "../shared/Foo.app"
// after
// target: "shared/Foo.app"
Defensive patterns

Strategy: validation

Validate before calling

let rel = Path::new(&cask_target);
for c in rel.components() {
    if !matches!(c, std::path::Component::Normal(_)) {
        return Err(format!("target contains non-normal component: {c:?}"));
    }
}

Type guard

fn is_clean_relative(p: &Path) -> bool {
    p.is_relative() && p.components().all(|c| matches!(c, std::path::Component::Normal(_)))
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


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

Appendix: source

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

            || 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,
                    name,
                    Mode::S_IRWXU | Mode::S_IRGRP | Mode::S_IXGRP | Mode::S_IROTH | Mode::S_IXOTH,
                ) {
                    Ok(()) | Err(nix::errno::Errno::EEXIST) => {}
                    Err(err) => {
                        return Err(err).wrap_err_with(|| {
                            format!(
                                "brew-cask: cannot create operation directory {}",
                                directory.display()
                            )
                        });

View on GitHub (pinned to 533346cc37)