{"record":{"id":"4c390c2b5c8b2248","repo":"jdx/mise","slug":"brew-cask-app-target-contains-nul-4c390c","errorCode":null,"errorMessage":"brew-cask: app target contains NUL","messagePattern":"brew-cask: app target contains NUL","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/packages/brew/cask/paths.rs","lineNumber":115,"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\npub(super) fn 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":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/system/packages/brew/cask/paths.rs#L97-L133","documentation":"`app_target_path` validates the destination path for an app artifact before any filesystem work. If the target name contains a NUL byte (`\\0`), which no Unix path can contain, the function bails immediately. This is an early input-validation guard against unusable or malicious path strings.","triggerScenarios":"Calling any of `installed_skip_reason`, `app_target_paths`, `install_app`, `validate_adoptable_apps`, or `appdir_artifact_source` with an app target string containing an embedded NUL byte — typically from corrupted config, hostile cask metadata, or a bad string decode.","commonSituations":"Hand-edited mise.toml or a cask override where a pasted value includes a hidden NUL; data read from a binary-corrupted file; an attacker-supplied cask name probing path handling.","solutions":["Inspect the app target string for non-printable bytes (e.g. `grep -P '\\\\x00'` or `xxd`) in your config or the cask definition and remove/repair it.","Re-write the affected config value by hand rather than pasting, to eliminate invisible control characters.","If the value comes from a downloaded cask definition, refresh it or pin a known-good cask version."],"exampleFix":"// before (target read from corrupted source contains \\0)\nlet target = String::from_utf8_lossy(raw_bytes).to_string();\n\n// after (strip NUL/control bytes before use)\nlet target: String = raw.chars().filter(|c| !c.is_control()).collect();","handlingStrategy":"validation","validationCode":"if target_name.contains('\\0') {\n    return Err(\"app target contains NUL byte\".into());\n}","typeGuard":"fn is_nul_free(s: &str) -> bool { !s.contains('\\0') }","tryCatchPattern":"if let Err(e) = app_target_path(&target_name) {\n    if e.to_string().contains(\"NUL\") {\n        // sanitize or reject the input before retrying\n    }\n}","preventionTips":["Sanitize strings read from files/network: strip control characters before using them as paths.","Never paste target values from binary sources; retype them.","Treat cask metadata as untrusted input and validate it before use."],"tags":["brew-cask","path-validation","input-validation"],"backgroundTag":"invalid-argument-value","analyzedSha":"533346cc374382b41ec5ff70536252b2e96e725c","analyzedAt":"2026-09-17T13:35:38.149Z","contentChangedAt":"2026-09-17T13:35:38.149Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}