jdx/mise · error
brew-cask: invalid {kind} '{value}'
Error message
brew-cask: invalid {kind} '{value}' What it means
Defense-in-depth path validation applied to the requested token, the API token from the cask JSON, and its version: the value must be a single normal path component — non-empty, no NUL bytes, no '/' or '..' components — and must not equal the reserved '.metadata' marker or start with '.mise-' (mise's bookkeeping names). Any violation aborts before the value is ever joined into a filesystem path.
Source
Thrown at src/system/packages/brew/cask.rs:903
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 9dcfcaa0dc)
Solutions
- Use plain alphanumeric/dash tokens when requesting casks (the cask's filename in its tap)
- If you maintain the tap, fix token/version fields to be single path components
- If you don't control the metadata, do not install that cask — the guard is protecting you
Defensive patterns
Strategy: validation
Validate before calling
fn valid_cask_component(value: &str) -> bool {
use std::path::{Component, Path};
let mut c = Path::new(value).components();
!value.is_empty()
&& !value.contains('\0')
&& matches!(c.next(), Some(Component::Normal(_)))
&& c.next().is_none()
&& value != ".metadata"
&& !value.starts_with(".mise-")
} Type guard
fn is_safe_cask_token(token: &str) -> bool {
!token.is_empty() && !token.contains('/') && !token.contains('\0')
&& !token.starts_with('.') && token != ".metadata" && !token.contains("..")
} Try / catch
Catch the 'invalid {kind}' bail and reject the offending request/tap metadata immediately; never sanitize and continue — a bad token or version means the metadata is untrustworthy. Prevention
- Validate user-supplied cask names before passing them to mise
- Tap authors: keep token and version to single path components (letters, digits, dashes, dots)
When it happens
Trigger: A user requests a cask name containing slashes or '..' (brew-cask:../evil), or a malicious/corrupted tap publishes cask JSON whose token or version embeds path separators or claims the reserved '.metadata' name — which could otherwise escape the Caskroom or collide with Homebrew's marker.
Common situations: Script-generated config with unescaped names; hand-edited third-party taps; supply-chain probing of mise's cask pipeline.
Related errors
- brew-cask: staged symlink path escaped extraction root: {}
- brew-cask: refusing generic artifact source outside the extr
- brew-cask: refusing to stage generic artifact through a path
- brew-cask: temporary artifact directory is not private
- brew-cask: refusing elevated operation through mutable direc
AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17).
Data as JSON: /api/errors/5811a7f25399922e.
Report an issue: GitHub.