{"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":745,"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":727,"sourceCodeEnd":763,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/toolset/tool_request.rs#L727-L763","documentation":"Ref strings are held to the same character rules as version strings: validate_ref_string rejects any ref containing a character disallowed by is_forbidden_version_char, naming the offending character. This keeps git ref arguments and derived paths safe.","triggerScenarios":"A ref:/branch:/tag:/rev: value containing shell metacharacters or whitespace (e.g. ref:\"my branch\", rev:$(cmd)) reaching new_with_options.","commonSituations":"Branch names with spaces or special characters pasted unquoted; command substitution accidentally left in config; trailing whitespace/newlines from editor or templating.","solutions":["Rename/choose a ref without forbidden characters (the message names the culprit).","Trim and sanitize templated config values before writing mise.toml.","Reference the commit SHA instead of an oddly named ref when renaming is impossible."],"exampleFix":"// before\nnode = { version = \"ref:feature/foo bar\" }\n// after\nnode = { version = \"ref:feature/foo-bar\" }  # or a rev: SHA","handlingStrategy":"validation","validationCode":"function validRef(r) {\n  return typeof r === 'string' && !r.startsWith('-') && !r.includes('..') && !/[\\s'\";$|&`<>]/.test(r);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Avoid branch names with spaces/special characters, or use the SHA.","Expand command substitution before it reaches config files.","Trim whitespace from templated values."],"tags":["mise","validation","git-ref","input-validation"],"backgroundTag":"invalid-identifier-format","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"}