jdx/mise · error

brew-cask: binary target contains NUL

Error message

brew-cask: binary target contains NUL

What it means

A cask `binary` artifact target containing an embedded NUL byte is rejected by binary_target_path before any path construction, because NUL terminates C strings on Unix and such a target can never name a valid file (src/system/packages/brew/cask/paths.rs:270). This is a strict input-integrity check on target names coming from cask stanzas or user configuration.

Solutions

  1. Inspect the cask's binary stanza and remove the NUL byte; fix the cask source or re-download it from the trusted tap.
  2. If the target name comes from program data, decode it as UTF-8 text and validate it contains no '\0' before passing it to the cask APIs (e.g. filter |t| !t.contains('\0')).
  3. Check the producing tool/format for a bug that embeds NULs (e.g. reading NUL-padded fixed-size buffers without trimming).
  4. Report tampering if the NUL appears in a cask you did not author — NUL injection in paths is a classic sanitizer-evasion vector.

Example fix

// before
targets.iter().for_each(|t| { let _ = process(t); });
// after
targets.iter().filter(|t| !t.contains('\0')).for_each(|t| { let _ = process(t); });
Defensive patterns

Strategy: validation

Validate before calling

fn sanitize_target(t: &str) -> Option<&str> {
    if t.contains('\0') { None } else { Some(t) }
}

Type guard

fn has_no_nul(t: &str) -> bool { !t.as_bytes().contains(&b'\0') }

Prevention

When it happens

Trigger: A cask's `binary` stanza (or a user-supplied binary target) contains a literal or injected NUL byte, e.g. from binary_target_path being called with a target_name string read from corrupted or malicious cask data. Called via target_path and binary_targets_must_stay_under_an_allowed_root during install/validation.

Common situations: Corrupted or tampered custom cask definitions; binary-safe data (e.g. from a byte buffer or binary file) accidentally passed as the target name instead of decoded UTF-8 text; untrimmed NUL-padded fixed-size fields from cask metadata producers.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

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

    }
    Ok(roots)
}

pub(super) fn is_appdir_binary_target(target_name: &str) -> bool {
    target_name.starts_with("$APPDIR/")
}

pub(super) fn allowed_binary_target_roots_display(roots: &[PathBuf]) -> String {
    roots
        .iter()
        .map(|root| root.display().to_string())
        .collect::<Vec<_>>()
        .join(" or ")
}

pub(super) fn binary_target_path(target_name: &str, appdir: &Path) -> Result<PathBuf> {
    if target_name.contains('\0') {
        bail!("brew-cask: binary target contains NUL");
    }
    if let Some(relative) = target_name.strip_prefix("$APPDIR/") {
        let relative = Path::new(relative);
        reject_appdir_escape(relative, "binary $APPDIR target", target_name)?;
        if !allowed_appdir_roots()?.iter().any(|root| root == appdir) {
            bail!("brew-cask: invalid appdir '{}'", appdir.display());
        }
        return Ok(appdir.join(relative));
    }
    if target_name.contains("$APPDIR") {
        bail!("brew-cask: $APPDIR must prefix a binary target");
    }
    let prefix = prefix::prefix();
    let prefix_str = prefix.to_string_lossy();
    let target_name = target_name.replace("$HOMEBREW_PREFIX", prefix_str.as_ref());
    let path = PathBuf::from(&target_name);
    let target = if path.is_absolute() {
        path

View on GitHub (pinned to 533346cc37)