{"record":{"id":"49f406ef51a5df8b","repo":"jdx/mise","slug":"brew-cask-app-target-contains-nul","errorCode":null,"errorMessage":"brew-cask: app target contains NUL","messagePattern":"brew-cask: app target contains NUL","errorType":"validation","errorClass":"eyre::Report","httpStatus":null,"severity":"error","filePath":"src/system/packages/brew/cask.rs","lineNumber":5980,"sourceCode":"        };\n        let matches = match (a, b) {\n            (Component::Normal(a), Component::Normal(b)) => match (a.to_str(), b.to_str()) {\n                (Some(a), Some(b)) => a.eq_ignore_ascii_case(b),\n                _ => a == b,\n            },\n            _ => a == b,\n        };\n        if !matches {\n            return false;\n        }\n    }\n    true\n}\n\nfn app_target_path(target_name: &str) -> Result<PathBuf> {\n    let app_dir = target_app_dir()?;\n    if target_name.contains('\\0') {\n        bail!(\"brew-cask: app target contains NUL\");\n    }\n    if target_name.contains('/') {\n        let target = target_name.replace(\"$HOMEBREW_PREFIX\", &prefix::prefix().to_string_lossy());\n        let path = PathBuf::from(target);\n        if path\n            .components()\n            .any(|component| matches!(component, Component::ParentDir))\n        {\n            bail!(\"brew-cask: app target '{target_name}' must not contain '..'\");\n        }\n        if path.is_absolute() {\n            let prefix_app_dir = prefix::prefix().join(\"Applications\");\n            if path.starts_with(&app_dir) || path.starts_with(&prefix_app_dir) {\n                return Ok(path);\n            }\n            // Casks routinely hardcode an absolute `/Applications/Foo.app`\n            // target. When an override appdir is configured, relocate such a\n            // target into it (preserving any subdirectories) rather than","sourceCodeStart":5962,"sourceCodeEnd":5998,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/src/system/packages/brew/cask.rs#L5962-L5998","documentation":"app_target_path converts a cask 'app' artifact target name into the PathBuf used for (possibly privileged) linking into the app dir. Before any filesystem work it rejects target names containing a NUL byte, because NUL cannot appear in a valid filename and would truncate the path at the OS syscall boundary. Seeing this error almost always means the cask JSON or receipt is corrupted or deliberately crafted.","triggerScenarios":"A cask 'app' artifact target string (from cask JSON, tap data, or a stored receipt) containing '\\0' reaching app_target_path during install, upgrade, or prune.","commonSituations":"Corrupted cask cache or partially-written receipt JSON; a malicious/typosquatted tap trying path injection; binary garbage ending up in a tap's artifact stanza.","solutions":["Inspect the cask definition (`brew info --json=v2 --cask <token>` plus the tap source) for a corrupted artifact target string","Remove the suspect tap (`brew untap <tap>`) if the malformed target comes from a third-party source","Clear mise's cached cask data (`mise cache clear`) and the stale receipt, then retry","Update mise — security hardening and parser fixes land regularly"],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":null,"typeGuard":"fn is_clean_app_target(name: &str) -> bool {\n    !name.contains('\\0')\n}","tryCatchPattern":"match app_target_path(name) {\n    Ok(p) => p,\n    Err(e) if e.to_string().contains(\"contains NUL\") => {\n        warn!(\"dropping corrupted app target {name:?}\");\n        return Ok(None);\n    }\n    Err(e) => Err(e),\n}","preventionTips":["Sanitize any programmatically generated target strings before storing them in cask overrides","Treat NUL-in-path errors as data corruption signals — investigate the tap/cache rather than retrying","Keep cask JSON sources limited to trusted taps"],"tags":["brew-cask","path-validation","hardening","corruption"],"backgroundTag":"invalid-path-characters","analyzedSha":"9dcfcaa0dc8747a2577d3270b69bb9d8313b2807","analyzedAt":"2026-08-17T14:28:50.624Z","schemaVersion":2},"datasetVersion":"2026-08-22T04:17:13.399Z"}