jdx/mise · error
brew-cask: refusing generic artifact source outside the extr
Error message
brew-cask: refusing generic artifact source outside the extraction root: {} What it means
A cask's generic `artifact` stanza says copy stage/<source> to <target>. mise locates the source with find_artifact_matching, then requires that after resolving symlinks the source still sits inside the extraction root (path_starts_with_resolved_root). If resolution jumps outside the stage, the copy is refused — a symlinked source could otherwise exfiltrate arbitrary files into install targets.
Source
Thrown at src/system/packages/brew/cask.rs:1711
let Ok(relative) = source.strip_prefix(temporary_caskroom) else {
continue;
};
file::remove_file(&target)?;
create_flight_symlink(&final_caskroom.join(relative), &target, FlightSudo::Never)?;
}
Ok(())
}
fn install_generic_artifact(
stage: &Path,
temporary_caskroom: &Path,
artifact: &GenericArtifact,
targets: &mut FlightTargetTransaction,
) -> Result<()> {
let source = find_artifact_matching(stage, &artifact.source, |_| true)
.ok_or_else(|| eyre!("brew-cask: artifact '{}' was not found", artifact.source))?;
if !path_starts_with_resolved_root(&source, stage) {
bail!(
"brew-cask: refusing generic artifact source outside the extraction root: {}",
source.display()
);
}
let target = generic_artifact_target_path(&artifact.target)?;
let relative_source = source.strip_prefix(stage)?;
let caskroom_source = temporary_caskroom.join(relative_source);
if !path_starts_with_resolved_root(&caskroom_source, temporary_caskroom) {
bail!(
"brew-cask: refusing to stage generic artifact through a path outside the caskroom: {}",
caskroom_source.display()
);
}
#[cfg(not(unix))]
if let Some(parent) = target.parent() {
file::create_dir_all(parent)?;
}
let elevated_target = targets.protect_generic(&target)?;View on GitHub (pinned to 9dcfcaa0dc)
Solutions
- Inspect the cask's archive and artifact stanza; verify no matched source entry is an escaping symlink
- If you maintain the tap, point artifact.source at real files inside the archive
- Otherwise report the cask — the guard is preventing an out-of-root copy
Defensive patterns
Strategy: try-catch
Validate before calling
// Tap authors: before publishing, confirm every artifact source is a real file inside the archive
let real = stage.join(&artifact.source);
if real.symlink_metadata().map_or(true, |m| !m.is_symlink()) == false {
panic!("artifact source {} is a symlink", artifact.source);
} Try / catch
Catch 'refusing generic artifact source outside the extraction root' and abort the cask install; inspect the archive and report — the guard exists to stop out-of-root copies, so no remediation in-process is appropriate.
Prevention
- Point artifact.source globs at regular files, never at symlink entries
- Audit third-party casks' artifact stanzas before trusting them in bootstrap
When it happens
Trigger: Cask JSON whose artifact.source matches a symlink in the archive that resolves to a path outside the staged extraction (absolute link or ../../ escape), or a glob that matched such a link.
Common situations: Malicious taps; archives whose entries are symlinks into system paths; tap authoring mistakes where source patterns unintentionally match link entries.
Related errors
- brew-cask: refusing to stage generic artifact through a path
- brew-cask: staged symlink path escaped extraction root: {}
- brew-cask: invalid {kind} '{value}'
- 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/8081e6e653617a67.
Report an issue: GitHub.