jdx/mise · error
brew-cask: staged symlink path escaped extraction root: {}
Error message
brew-cask: staged symlink path escaped extraction root: {} What it means
During installation of a brew cask on Linux/macOS, mise copies staged symlinked artifacts from the extraction (stage) directory into an owned staging area. Before copying each path it verifies the path's parent directory is still resolved-contained within the stage root. If a symlink chain or odd path component resolves to a location whose parent escapes the extraction root, the copy is aborted to prevent a malicious cask from planting files (or writing through symlinks) outside its own staged tree.
Source
Thrown at src/system/packages/brew/cask/mod.rs:1435
}
fn copy_staged_artifact_closure(stage: &Path, owned_stage: &Path, source: &Path) -> Result<()> {
let stage = lexically_normalized_path(stage);
let mut pending = vec![lexically_normalized_path(source)];
let mut visited = BTreeSet::new();
while let Some(source) = pending.pop() {
let relative = staged_relative_path(&stage, &source).ok_or_else(|| {
eyre!(
"brew-cask: staged symlink target escaped extraction root: {}",
source.display()
)
})?;
if relative.components().next().is_some()
&& !source
.parent()
.is_some_and(|parent| path_starts_with_resolved_root(parent, &stage))
{
bail!(
"brew-cask: staged symlink path escaped extraction root: {}",
source.display()
);
}
if !visited.insert(relative.to_path_buf()) {
continue;
}
let destination = owned_stage.join(&relative);
let metadata = source.symlink_metadata()?;
if destination.symlink_metadata().is_err() {
if let Some(parent) = destination.parent() {
file::create_dir_all(parent)?;
}
if metadata.file_type().is_symlink() {
file::make_symlink(&std::fs::read_link(&source)?, &destination)?;
} else {
copy_cask_artifact(&source, &destination)?;
}View on GitHub (pinned to afd2eddd3a)
Solutions
- Inspect the cask's payload (tar -tf or find -type l on the extracted stage) and remove/fix symlinks whose targets escape the extraction root
- Re-download the cask — the archive may be corrupted or tampered with; verify checksums
- Check whether the stage directory itself sits under a symlinked path; extract to a plain (non-symlinked) location
- Report the cask upstream if it legitimately needs out-of-tree links; mise will not stage such artifacts by design
Defensive patterns
Strategy: validation
Validate before calling
// before staging, ensure every symlink under the stage resolves inside it
for link in walk_symlinks(stage) {
let target = resolve_symlink_target(&link, std::fs::read_link(&link)?);
if !target.starts_with(&stage) {
return Err(format!("symlink escapes stage: {} -> {}", link.display(), target.display()));
}
} Prevention
- Extract cask payloads to a plain directory with no symlinked ancestors
- Inspect archives for absolute or out-of-tree symlinks before trusting them
- Re-download casks from trusted sources with checksum verification
- Never manually edit or re-link files inside mise's staged cask directories
When it happens
Trigger: A cask payload contains a symlink whose target — after resolve_symlink_target and lexical normalization — lands outside the stage directory, or the parent of the staged source path resolves (via symlinked ancestors) to a directory not under the stage. Triggered in copy_staged_artifact_closure while expanding the transitive symlink closure during durabilize_staged_symlink_targets.
Common situations: Installing a cask whose archive contains absolute symlinks pointing outside the payload (e.g. /usr/local/...), a crafted/compromised cask attempting symlink-based path traversal, or a corrupted/partially-extracted stage where an intermediate directory is itself a symlink out of the tree.
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: staged symlink path escaped extraction root: {}
- brew-cask: refusing generic artifact source outside the extr
- brew-cask: invalid {kind} '{value}'
- brew-cask: refusing to stage generic artifact through a path
- brew-cask: completion target '{}' must not contain '..'
AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09).
Data as JSON: /api/errors/0b5627bddb9c6065.
Report an issue: GitHub.