{"record":{"id":"3a643f48f708d9bf","repo":"jdx/mise","slug":"invalid-tool-ref-s-contains-forbidden-charact","errorCode":null,"errorMessage":"invalid tool ref {s:?}: contains forbidden character {c:?}","messagePattern":"invalid tool ref (.+?): contains forbidden character (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/toolset/tool_request.rs","lineNumber":655,"sourceCode":"}\n\n/// Validate `ref:`/`branch:`/`tag:`/`rev:` values. Same character rules as\n/// version strings: branch/tag names already use the same broad vocabulary\n/// (`/`, `+`, `-`, etc.), so only shell-quote-breaking characters and leading\n/// dashes need rejection. Kept as a separate function for distinct error\n/// messages.\nfn validate_ref_string(s: &str) -> Result<()> {\n    if s.is_empty() {\n        return Ok(());\n    }\n    if s.starts_with('-') {\n        bail!(\"invalid tool ref {s:?}: must not start with '-'\");\n    }\n    if s.contains(\"..\") {\n        bail!(\"invalid tool ref {s:?}: contains path-traversal sequence\");\n    }\n    if let Some(c) = s.chars().find(|c| is_forbidden_version_char(*c)) {\n        bail!(\"invalid tool ref {s:?}: contains forbidden character {c:?}\");\n    }\n    Ok(())\n}\n\n/// Validate `path:` values. Filesystem paths legitimately contain `/`, spaces,\n/// and many other characters, but the resolved path becomes `ctx.rootPath` /\n/// `installPath` for path-mode tools and is interpolated into shell commands\n/// by some plugin hooks. Reject the same shell-quote-breaking characters as\n/// version strings — `$`, backtick, quotes, and `\\` — so a hostile `path:`\n/// entry in a project config cannot inject shell syntax. Path traversal is\n/// intentionally not rejected here because `path:../tools/foo` is a normal\n/// relative-path use case.\n///\n/// The list is written for a POSIX shell, which is why `\\` is on it. On Windows `\\` is a path\n/// separator instead, so it is rewritten by [`windows_path_separators`] before it gets here rather\n/// than being allowed through — see that function. The shell those hooks run through there is\n/// `cmd.exe`, whose metacharacters are a different set, so a few more are rejected on Windows —\n/// see [`is_forbidden_path_char`].","sourceCodeStart":637,"sourceCodeEnd":673,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/toolset/tool_request.rs#L637-L673","documentation":"validate_ref_string rejects ref values containing any character matched by is_forbidden_version_char; the message names the first offending character. Refs share the version vocabulary to keep them safe as filenames and CLI arguments.","triggerScenarios":"A `ref:`/`branch:`/`tag:`/`rev:` value containing characters like spaces, quotes, semicolons, or other disallowed symbols, e.g. `node@ref:my branch` or `tag = \"v1.0 (rc)\"`.","commonSituations":"Branch names with spaces or special characters produced by templating/shell expansion, quotes copied from web pages, or shell interpolation injecting separators into the ref slot.","solutions":["Remove or rename the offending character in the branch/tag name (the error names it explicitly).","Quote and trim shell expansions before composing the ref string.","For unusual names, pin a commit with `rev:<sha>` instead of the raw name."],"exampleFix":"// before\nnode = { tag = \"v1.0 final\" }\n// after\nnode = { tag = \"v1.0-final\" }","handlingStrategy":"validation","validationCode":"function isValidRefChars(ref) { return typeof ref === 'string' && !/[\\s'\";`$&|<>(){}\\[\\]\\\\]/.test(ref); }\nif (!isValidRefChars(ref)) throw new Error(`ref has forbidden characters: ${ref}`);","typeGuard":"function isPlainRef(v) { return typeof v === 'string' && /^[\\w./+-]+$/.test(v); }","tryCatchPattern":null,"preventionTips":["Avoid branch/tag names containing spaces or shell metacharacters.","Quote and trim template/shell expansions before composing ref strings.","Replace awkward names with `rev:<sha>` pins in automation."],"tags":["validation","git-ref","cli","special-characters"],"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"}