jdx/mise · error

brew-cask: invalid generic artifact parent

Error message

brew-cask: invalid generic artifact parent

What it means

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.

Source

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

            || 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 afd2eddd3a)

Solutions

  1. Inspect the cask definition's artifact paths and remove any `..`, absolute-path, or `.` components so targets are relative and below the stage root
  2. Update/refresh the cask source (brew tap update / mise update) in case the metadata is outdated
  3. Report the cask as unsafe to the tap maintainer if the traversal is intentional

Example fix

// before (cask artifact declaration)
{"artifacts": [{"type": "generic", "target": "../../usr/local/bin/app"}]}
// after
{"artifacts": [{"type": "generic", "target": "bin/app"}]}
Defensive patterns

Strategy: validation

Validate before calling

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

Type guard

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

Try / catch

match result {
    Err(e) if e.to_string().contains("invalid generic artifact parent") => {
        eprintln!("cask artifact path contains traversal components; fix the cask JSON");
    }
    Err(e) => return Err(e),
    Ok(v) => v,
}

Prevention

When it happens

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

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

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@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/c7f6f270130c4f3a. Report an issue: GitHub.