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
- Remove path separators, '..', and leading '.mise-' from the requested token or version in your config
- If the offending value comes from the tap's metadata, the tap is broken or hostile — stop using it and report it upstream
- 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
- Reject tokens containing '/', '..', NUL, or the reserved .metadata / .mise-* prefixes at the config boundary
- Never join untrusted cask metadata into filesystem paths without a single-component check
- Lint tap-provided cask JSON (token and version fields) before trusting it in CI
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
- brew-cask: app target
- brew-cask: binary target
- brew-cask: completion target
- brew-cask: completion target
- brew-cask: completion target
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)