{"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":742,"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":724,"sourceCodeEnd":760,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/toolset/tool_request.rs#L724-L760","documentation":"Ref strings (ref:/branch:/tag:/rev: values) may not contain the '..' path-traversal sequence, since refs feed into path and argument construction. validate_ref_string rejects any such value during ToolRequest creation.","triggerScenarios":"A ref value containing '..' (e.g. ref:../evil, branch:a..b) passed through new_with_options or config parsing.","commonSituations":"Pasting a relative path into a ref field; git refspecs or ranges (a..b) supplied where a single ref name is required; generated config interpolation errors.","solutions":["Use a single concrete ref name without '..'.","For git ranges/refspecs, resolve to a concrete branch/tag/commit first and reference that.","Fix generators/scripts emitting '..' into version fields."],"exampleFix":"// before\nnode = { version = \"ref:main..dev\" }\n// after\nnode = { version = \"ref:dev\" }","handlingStrategy":"validation","validationCode":"function validRef(r) {\n  return typeof r === 'string' && !r.includes('..');\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never put git ranges/refspecs (a..b) in ref fields.","Resolve to a concrete commit SHA when in doubt.","Add config linting that rejects '..' in version/ref values."],"tags":["mise","validation","security","path-traversal"],"backgroundTag":"path-traversal-blocked","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"}