{"record":{"id":"e82059685551519e","repo":"jdx/mise","slug":"brew-cask-structured-flight-glob-matched-out-e82059","errorCode":null,"errorMessage":"brew-cask: structured flight glob '{}' matched outside staged path","messagePattern":"brew-cask: structured flight glob '(.+?)' matched outside staged path","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/packages/brew/cask/flight.rs","lineNumber":1034,"sourceCode":"    let escaped_root = glob::Pattern::escape(staged_path.to_string_lossy().as_ref());\n    for pattern in expand_braces(pattern) {\n        validate_flight_relative_path(&pattern)?;\n        let rooted_pattern = Path::new(&escaped_root)\n            .join(Path::new(&pattern))\n            .to_string_lossy()\n            .to_string();\n        for path in glob::glob_with(\n            &rooted_pattern,\n            glob::MatchOptions {\n                require_literal_separator: true,\n                ..Default::default()\n            },\n        )\n        .wrap_err_with(|| format!(\"brew-cask: invalid structured flight glob '{pattern}'\"))?\n        {\n            let path = path?;\n            if !path.starts_with(staged_path) {\n                bail!(\n                    \"brew-cask: structured flight glob '{}' matched outside staged path\",\n                    pattern\n                );\n            }\n            matches.push(path);\n        }\n    }\n    matches.sort();\n    matches.dedup();\n    Ok(matches)\n}\n\npub(super) fn is_flight_glob(path: &str) -> bool {\n    path.chars()\n        .any(|c| matches!(c, '*' | '?' | '[' | ']' | '{' | '}'))\n}\n\npub(super) fn resolve_flight_path(staged_path: &Path, path: &FlightPath) -> Result<PathBuf> {","sourceCodeStart":1016,"sourceCodeEnd":1052,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/packages/brew/cask/flight.rs#L1016-L1052","documentation":"A structured flight glob expanded to a path outside the cask's staged directory. Homebrew restricts all glob-based flight operations to within the staging area to prevent casks from reading/moving arbitrary filesystem paths, and throws this as a safety guard (potential malicious or buggy cask).","triggerScenarios":"A cask glob uses `..` components, absolute expansion, or symlinks such that matched paths escape `staged_path` — e.g. pattern `../Shared/*` or matching a symlink pointing outside the stage.","commonSituations":"Malicious or mis-authored casks attempting to escape staging; casks whose glob matches symlinked directories that point elsewhere on disk.","solutions":["Remove any `..` or absolute components from the glob pattern","Ensure matched paths are real staged artifacts, not out-of-stage symlinks","Anchor the glob under the intended staged subdirectory","Audit the cask with `brew audit` before installing"],"exampleFix":"// before\nglob: staged_path.join(\"../shared/*.framework\")\n// after\nglob: staged_path.join(\"lib/*.framework\")","handlingStrategy":"validation","validationCode":"fn stays_in_stage(staged: &Path, m: &Path) -> bool {\n    m.starts_with(staged) && !m.components().any(|c| c == std::path::Component::ParentDir)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never use `..` or absolute components in flight globs","Resolve symlinks in staging before globbing to detect escapes","Restrict glob bases to fixed subdirectories of staged_path","Audit third-party casks for suspicious glob patterns"],"tags":["brew","cask","glob","security","path-traversal"],"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"}