{"record":{"id":"7e18ad336e898bff","repo":"jdx/mise","slug":"brew-cask-refusing-to-stage-generic-artifact-thro","errorCode":null,"errorMessage":"brew-cask: refusing to stage generic artifact through a path outside the caskroom: {}","messagePattern":"brew-cask: refusing to stage generic artifact through a path outside the caskroom: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/packages/brew/cask.rs","lineNumber":1720,"sourceCode":"fn 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)?;\n    copy_generic_artifact(&source, &target, elevated_target.as_deref())?;\n    if let Some(parent) = caskroom_source.parent() {\n        file::create_dir_all(parent)?;\n    }\n    file::make_symlink(&target, &caskroom_source)?;\n    targets.record_installed(target);\n    Ok(())\n}\n","sourceCodeStart":1702,"sourceCodeEnd":1738,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/src/system/packages/brew/cask.rs#L1702-L1738","documentation":"Generic artifacts are also copied into a temporary Caskroom so Homebrew-style layouts stay intact. After joining the source's stage-relative path onto the temporary caskroom, mise re-verifies that the joined path is still inside that caskroot after resolution. If the relative path escapes (via symlinks or '..'), staging is refused — otherwise the trusted caskroom copy could write outside the caskroom it was scoped to.","triggerScenarios":"An artifact whose relative_source, once symlinks under the temporary caskroom resolve, points outside the temporary caskroom — e.g. a staged symlink chained to ../../../, or a directory swapped during staging.","commonSituations":"Maliciously crafted cask archives; taps that package symlink trees; races where staged content is replaced between extraction and staging.","solutions":["Audit the cask's archive for symlinks in generic artifact paths and report the cask if any escape","If you maintain the tap, ship real files (dereference symlinks when packaging)","Do not bypass the guard by pre-creating caskroom paths — the check is scoped exactly to what the cask may write"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"Catch 'refusing to stage generic artifact through a path outside the caskroom' as a terminal security failure for that cask; log token and artifact and report. Do not pre-create or relax caskroom directories to work around it.","preventionTips":["Keep generic artifact targets/targets relative within the cask layout","Package artifacts without symlink chains crossing the extraction root"],"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"}