{"record":{"id":"024c97f093f4ef74","repo":"jdx/mise","slug":"brew-cask-invalid-font-target-024c97","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/mod.rs","lineNumber":2594,"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\nfn stage_completion(\n    stage: &Path,\n    caskroom: &Path,\n    cask: &Cask,\n    apps: &[AppArtifact],\n    completion: &CompletionArtifact,","sourceCodeStart":2576,"sourceCodeEnd":2612,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/packages/brew/cask/mod.rs#L2576-L2612","documentation":"When computing the destination path for a cask font, mise validates the declared font name as a relative path: it must not be absolute, must not contain `..` components, and must not be empty. This error guards against cask definitions (or hostile cask files) writing fonts outside the managed font directory — a path traversal safeguard.","triggerScenarios":"`font_target_path` builds a relative `PathBuf` from the font name and finds `name_path.is_absolute()`, any `Component::ParentDir`, or zero components before returning `font_dir().join(name_path)`.","commonSituations":"A hand-edited or third-party cask declares `font \"~/Library/Fonts/x.ttf\"` (absolute/tilded) or `\"../../evil.ttf\"`; a cask stanza with an empty font name after variable expansion.","solutions":["Fix the font name in the cask definition to a plain relative filename like \"SomeFont.ttf\"","Do not use absolute paths or `..` in font stanzas; place subpath fonts under a relative directory if needed","Only use casks from trusted sources; treat this error as a blocked malicious/incorrect cask"],"exampleFix":"// before (cask stanza)\nfont \"../../evil.ttf\"\n// after\nfont \"MyFont.ttf\"","handlingStrategy":"validation","validationCode":"let p = Path::new(name);\nassert!(!p.is_absolute() && !p.components().any(|c| matches!(c, std::path::Component::ParentDir)) && p.components().next().is_some(), \"font target must be a relative, non-empty path\");","typeGuard":"fn is_safe_relative_name(name: &str) -> bool {\n  let p = std::path::Path::new(name);\n  !p.is_absolute()\n    && p.components().next().is_some()\n    && !p.components().any(|c| matches!(c, std::path::Component::ParentDir))\n}","tryCatchPattern":"if let Err(e) = result {\n  if e.contains(\"invalid font target\") {\n    eprintln!(\"cask font target rejected as unsafe; fix the cask's font stanza to a relative filename\");\n  }\n}","preventionTips":["Write font stanzas as plain relative filenames","Never trust absolute or parent-relative paths in third-party casks","Treat rejection as a sign the cask may be malicious or broken"],"tags":["path-traversal","security","brew-cask","validation"],"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"}