jdx/mise · critical

brew-cask: invalid

Error message

brew-cask: invalid {kind} '{value}'

What it means

validate_cask_path_component (src/system/packages/brew/cask.rs:968) hardens every string that becomes a filesystem path — the requested token, the API token, and the cask version. A value is valid only if it is a single normal path component (no '/', no '..' resolution, no NUL bytes), is not empty, is not '.metadata', and does not start with '.mise-'. Values failing this are rejected before any path join; this is a path-traversal and metadata-collision guard against hostile tap metadata.

Solutions

  1. Remove path separators, '..', and leading '.mise-' from the requested token or version in your config
  2. If the offending value comes from the tap's metadata, the tap is broken or hostile — stop using it and report it upstream
  3. Never bypass this check; the token/version strings are joined directly into privileged filesystem paths, which is exactly what the guard prevents
Defensive patterns

Strategy: type-guard

Validate before calling

// run on every user-supplied token/version before it reaches install APIs
assert!(is_valid_cask_component(requested_token));

Type guard

fn is_valid_cask_component(value: &str) -> bool {
    let mut c = std::path::Path::new(value).components();
    !value.is_empty()
        && !value.contains('\0')
        && matches!(c.next(), Some(std::path::Component::Normal(_)))
        && c.next().is_none()
        && value != ".metadata"
        && !value.starts_with(".mise-")
}

Prevention

When it happens

Trigger: A cask JSON (typically from a third-party tap) whose token or version contains '/', '..', or a NUL, is empty, resolves as an absolute/rooted path, equals '.metadata', or begins with '.mise-'; also a user-supplied name like ../evil passed as the requested token.

Common situations: Malicious or malformed taps crafting metadata that escapes the caskroom; hand-edited cask JSON; versions like 1.0/beta or tokens copied with a trailing '/'; reserved-name collisions with mise's own metadata directories.

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@6f52dcdf99 (2026-08-22). Data as JSON: /api/errors/5811a7f25399922e. Report an issue: GitHub.

Appendix: source

Thrown at src/system/packages/brew/cask.rs:968

    if cask.token != requested_token && !trusted_alias {
        bail!(
            "brew-cask: requested token '{requested_token}' does not match API token '{}'",
            cask.token
        );
    }
    Ok(())
}

fn validate_cask_path_component(kind: &str, value: &str) -> Result<()> {
    let mut components = Path::new(value).components();
    let valid = !value.is_empty()
        && !value.contains('\0')
        && matches!(components.next(), Some(Component::Normal(_)))
        && components.next().is_none()
        && value != ".metadata"
        && !value.starts_with(".mise-");
    if !valid {
        bail!("brew-cask: invalid {kind} '{value}'");
    }
    Ok(())
}

async fn fetch_and_stage(cask: &Cask, pr: Option<&dyn SingleReport>) -> Result<PathBuf> {
    if cask.url.ends_with(".git") {
        return fetch_git_clone_and_stage(cask, pr).await;
    }
    let archive = fetch_archive(cask, pr).await?;
    extract_archive(cask, &archive, pr)
}

async fn fetch_git_clone_and_stage(cask: &Cask, pr: Option<&dyn SingleReport>) -> Result<PathBuf> {
    let extract_dir = crate::dirs::CACHE
        .join("system-brew")
        .join("cask-extract")
        .join(format!("{}-{}", cask.token, cask.version));
    file::remove_all(&extract_dir)?;

View on GitHub (pinned to 6f52dcdf99)