{"record":{"id":"5a626ed9f618262a","repo":"jdx/mise","slug":"brew-cask-refusing-to-stage-generic-artifact-thro-5a626e","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/mod.rs","lineNumber":1574,"sourceCode":"            \"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    // Not a lexical `strip_prefix`: the lookup resolves symlinks it had to\n    // traverse, so a source reached that way can be contained by the stage\n    // without sharing its literal prefix — as it is whenever `stage` itself\n    // has a symlinked ancestor. `staged_relative_path` retries against the\n    // resolved stage, matching the containment check above.\n    let relative_source = staged_relative_path(stage, &source).ok_or_else(|| {\n        eyre!(\n            \"brew-cask: generic artifact source is not contained by the extraction root: {}\",\n            source.display()\n        )\n    })?;\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":1556,"sourceCodeEnd":1592,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/packages/brew/cask/mod.rs#L1556-L1592","documentation":"After resolving the artifact's relative position inside the stage, mise builds the corresponding path under the temporary caskroom and re-verifies containment with path_starts_with_resolved_root. If the constructed caskroom path resolves outside the temporary caskroom (e.g. a component of temporary_caskroom or the relative path is a symlink escaping it), staging is refused so the artifact can never be planted or linked through a location outside mise-controlled space.","triggerScenarios":"install_generic_artifact computes caskroom_source = temporary_caskroom.join(relative_source), and path_starts_with_resolved_root determines that this joined path resolves outside the temporary caskroom — e.g. due to a symlinked directory at the join point or a relative_source containing traversal-like components that survive normalization.","commonSituations":"A caskroom parent directory containing symlinks; a compromised or unusual filesystem layout where the temporary caskroom path traverses a link; an artifact source path in the stage that, after staged_relative_path mapping, resolves through a symlink inside the caskroom.","solutions":["Check the temporary caskroom location for symlinked parents and use a plain directory path","Re-run the install after removing any unexpected symlinks inside the caskroom","Re-download/re-extract the cask to rule out a corrupted payload","Report the issue to mise if your caskroom layout is conventional — this should not trigger on normal setups"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"let caskroom_source = temporary_caskroom.join(&relative_source);\nlet resolved = dunce::canonicalize(temporary_caskroom)?.join(&relative_source);\nif !resolved.starts_with(dunce::canonicalize(&temporary_caskroom)?) {\n    return Err(format!(\"staging path escapes caskroom: {}\", caskroom_source.display()));\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Place the caskroom under a plain, non-symlinked directory (e.g. ~/.local/share/mise/cask)","Do not create symlinks inside the caskroom that point elsewhere","Keep consistent: install all casks as the same user","If a layout triggers this unexpectedly, report it with the exact paths"],"tags":["security","symlink","brew-cask","containment"],"backgroundTag":"path-traversal-blocked","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}