{"record":{"id":"c743010e65aebd84","repo":"jdx/mise","slug":"invalid-tap-formula-name-name","errorCode":null,"errorMessage":"invalid tap formula name '{name}'","messagePattern":"invalid tap formula name '(.+?)'","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/packages/brew/tap.rs","lineNumber":364,"sourceCode":"    ];\n    let mut last_error = None;\n    for path in paths {\n        match HTTP_FETCH.get_text(format!(\"{raw_base}/{path}\")).await {\n            Ok(source) => return Ok((source, path)),\n            Err(err) => last_error = Some(err),\n        }\n    }\n    Err(last_error.unwrap()).wrap_err_with(|| format!(\"tap has no {directory}/{name}.rb\"))\n}\n\nfn validate_name(name: &str) -> Result<()> {\n    if name.is_empty()\n        || name.contains(['/', '\\\\', '\\0'])\n        || name == \".\"\n        || name == \"..\"\n        || PathBuf::from(name).components().count() != 1\n    {\n        bail!(\"invalid tap formula name '{name}'\");\n    }\n    Ok(())\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    async fn test_ruby() -> Result<Option<PathBuf>> {\n        if let Some(ruby) = usable_system_ruby().await {\n            return Ok(Some(ruby));\n        }\n        super::super::source::installed_ruby_bin().await\n    }\n\n    #[test]\n    fn rejects_unsafe_formula_names() {\n        for name in [\"\", \".\", \"..\", \"../oops\", \"a/b\", \"a\\\\b\"] {","sourceCodeStart":346,"sourceCodeEnd":382,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/packages/brew/tap.rs#L346-L382","documentation":"validate_name rejects tap formula/cask names that are empty, contain path separators ('/', '\\\\', NUL), equal '.' or '..', or whose PathBuf representation is not exactly one component. This guards against path traversal and malformed names being used to construct file paths inside the tap checkout.","triggerScenarios":"formula_from_ruby or cask_from_ruby is given a name extracted from a tap's file path or user request that is empty, includes slashes/backslashes, is '.'/'..', or otherwise spans multiple path components.","commonSituations":"A formula name passed as 'user/repo/formula' instead of just 'formula'; malicious or corrupted tap file names containing '..' or separators; misparsed Ruby filenames; passing a fully qualified tap+name string where only the bare name is expected.","solutions":["Pass only the bare formula/cask file stem (no directories, no tap prefix).","Strip the tap name and directory components before calling, e.g. take the file_stem of the .rb path.","Reject or sanitize input containing '/' or '\\\\' before invoking.","If the name comes from an untrusted tap, treat this as an invalid/tampered tap and skip or re-clone it."],"exampleFix":"// before\nformula_from_ruby(tap, \"homebrew/core/wget\", ruby_source).await?;\n// after\nformula_from_ruby(tap, \"wget\", ruby_source).await?;","handlingStrategy":"validation","validationCode":"fn is_valid_tap_formula_name(name: &str) -> bool {\n    !name.is_empty()\n        && !name.contains(['/', '\\\\', '\\0'])\n        && name != \".\"\n        && name != \"..\"\n        && std::path::Path::new(name).components().count() == 1\n}","typeGuard":"fn as_bare_formula_name(s: &str) -> Option<&str> {\n    let n = std::path::Path::new(s).file_name()?.to_str()?;\n    let n = n.strip_suffix(\".rb\").unwrap_or(n);\n    if n.is_empty() || n == \".\" || n == \"..\" { None } else { Some(n) }\n}","tryCatchPattern":"match formula_from_ruby(tap, name, src).await {\n    Err(e) if e.to_string().starts_with(\"invalid tap formula name\") => {\n        eprintln!(\"skipping malformed entry: {name}\");\n        Ok(None)\n    }\n    other => other.map(Some),\n}","preventionTips":["Always pass the bare formula/cask file stem, never a tap-prefixed or path-qualified name","Derive names via Path::file_name()/file_stem() from tap file paths","Treat names containing '/' or '..' from untrusted taps as suspicious and skip them"],"tags":["validation","homebrew","path-traversal","input-validation"],"backgroundTag":"invalid-argument-value","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}