{"record":{"id":"738ec9c87cdcf9c1","repo":"jdx/mise","slug":"relative-repo-path-path-raw-must-stay-within-t","errorCode":null,"errorMessage":"relative repo path `{path_raw}` must stay within the project root (no `..` or absolute segments)","messagePattern":"relative repo path `(.+?)` must stay within the project root \\(no `\\.\\.` or absolute segments\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/repos.rs","lineNumber":100,"sourceCode":"                \"repo path `{path_raw}` cannot start with `~`; use `~/` for a home-relative path\"\n            );\n        }\n        let path = file::replace_path(&path_raw);\n        let path = if path.is_absolute() {\n            path\n        } else {\n            let Some(root) = project_root else {\n                bail!(\n                    \"relative repo paths are only allowed in a project config; use an absolute path or a `~/` path\"\n                );\n            };\n            if path.components().any(|component| {\n                matches!(\n                    component,\n                    Component::ParentDir | Component::RootDir | Component::Prefix(_)\n                )\n            }) {\n                bail!(\n                    \"relative repo path `{path_raw}` must stay within the project root (no `..` or absolute segments)\"\n                );\n            }\n            if !path\n                .components()\n                .any(|component| matches!(component, Component::Normal(_)))\n            {\n                bail!(\n                    \"relative repo path `{path_raw}` must name a directory inside the project root\"\n                );\n            }\n            // Join only the Normal segments so `./foobar` resolves to\n            // `<root>/foobar` rather than `<root>/./foobar` — a `.` component\n            // survives `Path::join` and leaks into every displayed path.\n            let mut resolved = root.to_path_buf();\n            for component in path.components() {\n                if let Component::Normal(segment) = component {\n                    resolved.push(segment);","sourceCodeStart":82,"sourceCodeEnd":118,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/src/system/repos.rs#L82-L118","documentation":"After a project root is found, each component of the relative repo path is inspected; any `..` (ParentDir), root `/` (RootDir), or Windows drive-prefix (Prefix) component means the path could point outside the project root, and from_toml rejects it. This is a containment guard: with a relative key, bootstrap only manages checkouts inside the project. Repos elsewhere must use `~/` or absolute paths.","triggerScenarios":"Keys like `\"../shared\"`, `\"vendor/../../escape\"`, or (on Windows) a key embedding a `C:\\` prefix inside an otherwise relative path, in a project mise.toml.","commonSituations":"Repos intentionally stored as siblings of the project (`../lib/foo`); refactoring from absolute to relative paths; monorepo layouts where the checkout lives one level above the config.","solutions":["Point the key inside the project (drop the `..` segments)","Use a `~/` path such as `\"~/src/shared\"` for repos that live outside the project","Use an absolute path for machine-specific locations"],"exampleFix":"# before\n[bootstrap.repos]\n\"../shared\" = { url = \"git@github.com:team/shared.git\" }\n\n# after\n[bootstrap.repos]\n\"~/src/shared\" = { url = \"git@github.com:team/shared.git\" }","handlingStrategy":"validation","validationCode":"use std::path::{Component, Path};\n\nfn repo_path_stays_in_root(path_raw: &str) -> bool {\n    !Path::new(path_raw).components().any(|c| {\n        matches!(c, Component::ParentDir | Component::RootDir | Component::Prefix(_))\n    })\n}","typeGuard":"fn is_contained_relative_repo_path(path_raw: &str) -> bool {\n    !std::path::Path::new(path_raw)\n        .components()\n        .any(|c| matches!(c, std::path::Component::ParentDir | std::path::Component::RootDir | std::path::Component::Prefix(_)))\n}","tryCatchPattern":null,"preventionTips":["Reserve relative repo keys for checkouts inside the project tree","Use `~/` paths for sibling or home-based repos","Never build repo keys by concatenating user input containing `..`"],"tags":["mise","bootstrap","repos","path-validation","path-traversal"],"backgroundTag":"path-traversal-blocked","analyzedSha":"9dcfcaa0dc8747a2577d3270b69bb9d8313b2807","analyzedAt":"2026-08-17T14:28:50.624Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}