{"record":{"id":"3a7706c610ed3763","repo":"jdx/mise","slug":"brew-cask-invalid-font-target","errorCode":null,"errorMessage":"brew-cask: invalid font target '{}'","messagePattern":"brew-cask: invalid font target '(.+?)'","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/packages/brew/cask.rs","lineNumber":2778,"sourceCode":"            .filter(|entry| entry.file_type().is_ok_and(|ft| ft.is_dir()))\n            .any(|entry| relative.is_some_and(|path| entry.path().join(path).is_file()));\n        if has_staged_copy {\n            file::remove_file(target)?;\n        }\n    }\n    Ok(())\n}\n\nfn font_target_path(font: &FontArtifact) -> Result<PathBuf> {\n    let name = font_filename(font)?;\n    let name_path = Path::new(&name);\n    if name_path.is_absolute()\n        || name_path\n            .components()\n            .any(|component| matches!(component, Component::ParentDir))\n        || name_path.components().next().is_none()\n    {\n        bail!(\"brew-cask: invalid font target '{}'\", name);\n    }\n    Ok(font_dir().join(name_path))\n}\n\nfn font_dir() -> PathBuf {\n    if cfg!(target_os = \"linux\") {\n        crate::env::XDG_DATA_HOME.join(\"fonts\")\n    } else {\n        crate::dirs::HOME.join(\"Library\").join(\"Fonts\")\n    }\n}\n\n#[cfg(test)]\nfn execute_flight_steps(\n    cask: &Cask,\n    steps: &[FlightStep],\n    staged_path: &Path,\n    appdir: &Path,","sourceCodeStart":2760,"sourceCodeEnd":2796,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/src/system/packages/brew/cask.rs#L2760-L2796","documentation":"The final font install path is font_dir().join(name), where the name is derived from the cask's font stanza. font_target_path requires that name to be a non-empty, purely relative filename: not absolute and free of '..' components, so a font can never be written outside the user's font directory.","triggerScenarios":"A font filename that starts with '/' or contains '..' after expansion from the cask stanza; an empty or placeholder filename that resolves to zero components.","commonSituations":"Hand-written casks with absolute font paths; unresolved template variables leaving '.'/'..' fragments; malformed font stanzas in third-party taps.","solutions":["Fix the font stanza to a plain file name like \"MyFont-Regular.otf\"","Strip directory components from the name before it reaches font handling","Report malformed stanzas in third-party taps upstream"],"exampleFix":"# before\nfont: \"/Library/Fonts/MyFont.ttf\"\n\n# after\nfont: \"MyFont.ttf\"","handlingStrategy":"validation","validationCode":"use std::path::{Path, Component};\n\nfn is_plain_filename(name: &str) -> bool {\n    let p = Path::new(name);\n    !p.is_absolute()\n        && p.components().next().is_some()\n        && !p.components().any(|c| matches!(c, Component::ParentDir))\n}","typeGuard":"fn is_plain_font_filename(name: &str) -> bool {\n    let p = std::path::Path::new(name);\n    !p.is_absolute() && p.components().next().is_some()\n        && !p.components().any(|c| matches!(c, std::path::Component::ParentDir))\n}","tryCatchPattern":null,"preventionTips":["Write font stanzas as bare filenames, never paths","Resolve template variables before passing font names","Reject absolute or dot-dot font names in cask linting"],"tags":["brew-cask","font","path-validation"],"backgroundTag":"path-traversal-guard","analyzedSha":"9dcfcaa0dc8747a2577d3270b69bb9d8313b2807","analyzedAt":"2026-08-17T14:28:50.624Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}