jdx/mise · error

brew-cask: app target contains NUL

Error message

brew-cask: app target contains NUL

What it means

app_target_path rejects app target names containing NUL bytes because Rust PathBuf and the filesystem APIs cannot represent embedded NULs; such a target is necessarily invalid. The check runs before any path construction or install work.

Source

Thrown at src/system/packages/brew/cask/paths.rs:115

        };
        let matches = match (a, b) {
            (Component::Normal(a), Component::Normal(b)) => match (a.to_str(), b.to_str()) {
                (Some(a), Some(b)) => a.eq_ignore_ascii_case(b),
                _ => a == b,
            },
            _ => a == b,
        };
        if !matches {
            return false;
        }
    }
    true
}

pub(super) fn app_target_path(target_name: &str) -> Result<PathBuf> {
    let app_dir = target_app_dir()?;
    if target_name.contains('\0') {
        bail!("brew-cask: app target contains NUL");
    }
    if target_name.contains('/') {
        let target = target_name.replace("$HOMEBREW_PREFIX", &prefix::prefix().to_string_lossy());
        let path = PathBuf::from(target);
        if path
            .components()
            .any(|component| matches!(component, Component::ParentDir))
        {
            bail!("brew-cask: app target '{target_name}' must not contain '..'");
        }
        if path.is_absolute() {
            let prefix_app_dir = prefix::prefix().join("Applications");
            if path.starts_with(&app_dir) || path.starts_with(&prefix_app_dir) {
                return Ok(path);
            }
            // Casks routinely hardcode an absolute `/Applications/Foo.app`
            // target. When an override appdir is configured, relocate such a
            // target into it (preserving any subdirectories) rather than

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Strip or reject NUL characters from the target name before installing the app
  2. Re-fetch the cask metadata from the trusted upstream source
  3. Sanitize inputs at the deserialization boundary so embedded NULs never reach path construction

Example fix

// before
let target_name = raw_target; // may contain '\0'
// after
let target_name = raw_target.trim_end_matches('\0');
Defensive patterns

Strategy: validation

Validate before calling

if target_name.contains('\0') {
    return Err("app target contains NUL byte");
}

Type guard

fn is_safe_target(s: &str) -> bool {
    !s.is_empty() && !s.contains('\0') && !s.contains('\u{0}')
}

Try / catch

match install_result {
    Err(e) if e.to_string().contains("NUL") => {
        eprintln!("cask metadata is corrupt; refetching");
        refetch_cask_metadata();
    }
    _ => {}
}

Prevention

When it happens

Trigger: Calling app_target_path (directly or via installed_skip_reason, app_target_paths, install_app, etc.) with a target_name string containing '\0', typically from untrusted cask metadata or malformed JSON input.

Common situations: Corrupt or hostile cask metadata whose target field embeds a NUL, a truncation bug upstream producing a padded string, or binary data accidentally parsed into the target field.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/4c390c2b5c8b2248. Report an issue: GitHub.