{"record":{"id":"c8ff27bc54aa7f19","repo":"jdx/mise","slug":"brew-cask-refusing-installer-executable-outside-t","errorCode":null,"errorMessage":"brew-cask: refusing installer executable outside trusted installer roots: {}","messagePattern":"brew-cask: refusing installer executable outside trusted installer roots: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/packages/brew/cask.rs","lineNumber":2317,"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":2299,"sourceCodeEnd":2335,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/src/system/packages/brew/cask.rs#L2299-L2335","documentation":"Before executing a cask's installer artifact, mise joins the configured executable name onto the staging directory and requires staged_relative_path to still resolve inside the stage. If the executable value escapes the stage (via '..', an absolute path, or traversal components), the run is refused: only binaries that shipped inside the staged payload may be executed.","triggerScenarios":"An installer executable value like '../../usr/bin/open' or an absolute path; a cask whose executable stanza points above the extracted artifact root; executable names built from untrusted cask metadata.","commonSituations":"Hand-edited or third-party-tap casks with sloppy executable paths; casks written against a different artifact layout than what was actually downloaded.","solutions":["Fix the cask's executable value to a plain path relative to the installer artifact (e.g. \"Installer.app/Contents/MacOS/Installer\")","Verify what the staged artifact actually contains and align the stanza with it","Treat persistent occurrences in a tap cask as a cask bug and report it to the tap"],"exampleFix":"# cask installer stanza -- before\nexecutable: \"../../usr/local/bin/setup\"\n\n# after\nexecutable: \"Installer.app/Contents/MacOS/Installer\"","handlingStrategy":"validation","validationCode":"use std::path::{Path, Component};\n\nfn executable_stays_in_stage(stage: &Path, executable: &str) -> bool {\n    let exe = Path::new(executable);\n    !exe.is_absolute()\n        && !exe.components().any(|c| matches!(c, Component::ParentDir))\n        && stage.join(exe).starts_with(stage)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Write installer executable values as plain paths relative to the artifact root","Never let cask metadata reference binaries outside the staged payload","Lint third-party casks for '..' or absolute executable paths before use"],"tags":["brew-cask","installer","path-validation","security"],"backgroundTag":"path-traversal-guard","analyzedSha":"9dcfcaa0dc8747a2577d3270b69bb9d8313b2807","analyzedAt":"2026-08-17T14:28:50.624Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}