{"record":{"id":"4369fcb759977369","repo":"jdx/mise","slug":"invalid-tool-ref-s-contains-path-traversal-se","errorCode":null,"errorMessage":"invalid tool ref {s:?}: contains path-traversal sequence","messagePattern":"invalid tool ref (.+?): contains path-traversal sequence","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/toolset/tool_request.rs","lineNumber":652,"sourceCode":"        bail!(\"invalid tool version {s:?}: contains forbidden character {c:?}\");\n    }\n    Ok(())\n}\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","sourceCodeStart":634,"sourceCodeEnd":670,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/toolset/tool_request.rs#L634-L670","documentation":"validate_ref_string rejects ref values containing '..' to block path-traversal-style input that could escape intended directories or be interpreted as a range. It is raised during ToolRequest construction (new_with_options) for `ref:`/`branch:`/`tag:`/`rev:` values.","triggerScenarios":"A ref string like `node@ref:../../somewhere`, `branch = \"a..b\"` (a git range syntax), or any ref containing a double dot anywhere.","commonSituations":"Users pasting git range expressions (`main..feature`) where a single ref was expected, or attempting traversal via a ref field in mise.toml/.tool-versions.","solutions":["Replace range syntax with the concrete ref you want, e.g. `ref:feature` instead of `ref:main..feature`.","Use a specific commit via `rev:<sha>` if you need a precise revision.","Sanitize generated ref strings (strip/validate '..') before writing them into config."],"exampleFix":"// before\n\"github:owner/repo\" = { branch = \"main..dev\" }\n// after\n\"github:owner/repo\" = { branch = \"dev\" }","handlingStrategy":"validation","validationCode":"function hasNoTraversal(ref) { return typeof ref === 'string' && !ref.includes('..'); }\nif (!hasNoTraversal(ref)) throw new Error(`range syntax not allowed in ref: ${ref}`);","typeGuard":"function isSingleRef(v) { return typeof v === 'string' && !v.includes('..'); }","tryCatchPattern":null,"preventionTips":["Remember git range syntax (a..b) is not a ref — resolve it to a concrete commit first.","Strip '..' from any externally supplied ref strings before writing config."],"tags":["validation","security","path-traversal","git-ref"],"backgroundTag":"path-traversal-blocked","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"}