{"record":{"id":"8081e6e653617a67","repo":"jdx/mise","slug":"brew-cask-refusing-generic-artifact-source-outsid","errorCode":null,"errorMessage":"brew-cask: refusing generic artifact source outside the extraction root: {}","messagePattern":"brew-cask: refusing generic artifact source outside the extraction root: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/packages/brew/cask.rs","lineNumber":1711,"sourceCode":"        let Ok(relative) = source.strip_prefix(temporary_caskroom) else {\n            continue;\n        };\n        file::remove_file(&target)?;\n        create_flight_symlink(&final_caskroom.join(relative), &target, FlightSudo::Never)?;\n    }\n    Ok(())\n}\n\nfn install_generic_artifact(\n    stage: &Path,\n    temporary_caskroom: &Path,\n    artifact: &GenericArtifact,\n    targets: &mut FlightTargetTransaction,\n) -> Result<()> {\n    let source = find_artifact_matching(stage, &artifact.source, |_| true)\n        .ok_or_else(|| eyre!(\"brew-cask: artifact '{}' was not found\", artifact.source))?;\n    if !path_starts_with_resolved_root(&source, stage) {\n        bail!(\n            \"brew-cask: refusing generic artifact source outside the extraction root: {}\",\n            source.display()\n        );\n    }\n    let target = generic_artifact_target_path(&artifact.target)?;\n    let relative_source = source.strip_prefix(stage)?;\n    let caskroom_source = temporary_caskroom.join(relative_source);\n    if !path_starts_with_resolved_root(&caskroom_source, temporary_caskroom) {\n        bail!(\n            \"brew-cask: refusing to stage generic artifact through a path outside the caskroom: {}\",\n            caskroom_source.display()\n        );\n    }\n    #[cfg(not(unix))]\n    if let Some(parent) = target.parent() {\n        file::create_dir_all(parent)?;\n    }\n    let elevated_target = targets.protect_generic(&target)?;","sourceCodeStart":1693,"sourceCodeEnd":1729,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/src/system/packages/brew/cask.rs#L1693-L1729","documentation":"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.","triggerScenarios":"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.","commonSituations":"Malicious taps; archives whose entries are symlinks into system paths; tap authoring mistakes where source patterns unintentionally match link entries.","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"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Tap authors: before publishing, confirm every artifact source is a real file inside the archive\nlet real = stage.join(&artifact.source);\nif real.symlink_metadata().map_or(true, |m| !m.is_symlink()) == false {\n    panic!(\"artifact source {} is a symlink\", artifact.source);\n}","typeGuard":null,"tryCatchPattern":"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.","preventionTips":["Point artifact.source globs at regular files, never at symlink entries","Audit third-party casks' artifact stanzas before trusting them in bootstrap"],"tags":["homebrew","cask","artifact","zip-slip","security","mise"],"backgroundTag":"zip-slip","analyzedSha":"9dcfcaa0dc8747a2577d3270b69bb9d8313b2807","analyzedAt":"2026-08-17T14:28:50.624Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}