{"record":{"id":"658cc49bfae1bdfd","repo":"jdx/mise","slug":"brew-cask-refusing-installer-executable-outside-t-658cc4","errorCode":null,"errorMessage":"brew-cask: refusing installer executable outside trusted installer roots: {}","messagePattern":"brew-cask: refusing installer executable outside trusted installer roots: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/packages/brew/cask/mod.rs","lineNumber":2382,"sourceCode":"    let relative = appdir.strip_prefix(Path::new(\"/\")).map_err(|_| {\n        eyre!(\n            \"brew-cask: app directory '{}' must be an absolute path\",\n            appdir.display()\n        )\n    })?;\n    // `allow_current_user` is true because a per-user appdir such as\n    // `~/Applications` is legitimately owned by the invoking user.\n    open_trusted_directory(Path::new(\"/\"), relative, true, true)\n}\n\nfn run_installer_artifact(\n    stage: &Path,\n    installer: &InstallerArtifact,\n    copied_files: &BTreeSet<PathBuf>,\n) -> Result<()> {\n    let executable = stage.join(&installer.executable);\n    if staged_relative_path(stage, &executable).is_none() {\n        bail!(\n            \"brew-cask: refusing installer executable outside trusted installer roots: {}\",\n            executable.display()\n        );\n    }\n    if !executable.is_file() {\n        bail!(\n            \"brew-cask: installer executable '{}' was not found\",\n            installer.executable\n        );\n    }\n    let executable = file::desymlink_path(&executable);\n    if !executable.starts_with(file::desymlink_path(stage)) && !copied_files.contains(&executable) {\n        bail!(\n            \"brew-cask: refusing installer executable outside trusted installer roots: {}\",\n            executable.display()\n        );\n    }\n    file::make_executable(&executable)?;","sourceCodeStart":2364,"sourceCodeEnd":2400,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/system/packages/brew/cask/mod.rs#L2364-L2400","documentation":"For casks with an installer artifact, mise validates that the staged installer executable resolves to a path strictly within the trusted installer roots (via `staged_relative_path` against the stage). If the configured executable path escapes the stage, mise refuses to run it, preventing a cask from executing arbitrary binaries outside the controlled extraction area.","triggerScenarios":"A cask's `installer` stanza names an `executable` that, after joining with the stage, cannot be contained by the stage root (e.g. absolute path or one escaping via `..`); detected before checking `is_file`.","commonSituations":"Mis-authored or tampered cask stanzas pointing at /usr/bin/osascript-like absolute paths; hand-modified local casks; upstream cask changes relocating the installer script.","solutions":["Fix the cask's installer `executable` to a path relative to the staged payload (e.g. \"Foo Installer.app/Contents/MacOS/foo\")","Update the cask to the latest version in case the executable location changed upstream","Verify the cask source is trusted; re-fetch rather than hand-editing","Report the cask to maintainers if a stock cask triggers this"],"exampleFix":"// before\n// executable: \"/usr/bin/osascript\"\n// after\n// executable: \"scripts/install.scpt\"  # within the staged payload","handlingStrategy":"validation","validationCode":"let stage = fs::canonicalize(stage_dir)?;\nlet exe = stage.join(&installer.executable);\nlet resolved = fs::canonicalize(&exe)?;\nif !resolved.starts_with(&stage) {\n    return Err(format!(\"installer executable {exe:?} escapes stage {stage:?}\"));\n}","typeGuard":"fn staged_executable(stage: &Path, exe: &Path) -> Option<PathBuf> {\n    fs::canonicalize(exe).ok().filter(|r| r.starts_with(stage))\n}","tryCatchPattern":null,"preventionTips":["Reference installer executables relative to the staged payload","Update casks rather than hand-editing executable paths","Only install casks from trusted sources"],"tags":["security","path-traversal","brew-cask","path-validation"],"backgroundTag":"path-traversal-blocked","analyzedSha":"533346cc374382b41ec5ff70536252b2e96e725c","analyzedAt":"2026-09-17T13:35:38.149Z","contentChangedAt":"2026-09-17T13:35:38.149Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}