{"record":{"id":"7bc498c3c1f32439","repo":"jdx/mise","slug":"invalid-repository-origin","errorCode":null,"errorMessage":"invalid repository origin","messagePattern":"invalid repository origin","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/remote_repository.rs","lineNumber":46,"sourceCode":"async fn git_async(path: &Path, args: &[&str]) -> Result<String> {\n    let mut command = Command::new(\"git\");\n    crate::git::sanitize_git_command(&mut command);\n    command.arg(\"-C\").arg(path).args(args);\n    let output = tokio::process::Command::from(command)\n        .kill_on_drop(true)\n        .output()\n        .await?;\n    if !output.status.success() {\n        bail!(\"repository operation failed ({})\", output.status);\n    }\n    Ok(String::from_utf8(output.stdout)?\n        .trim_end_matches('\\n')\n        .to_string())\n}\n\npub(crate) fn validate_origin(origin: &str) -> Result<()> {\n    if origin.starts_with('-') || origin.chars().any(char::is_control) {\n        bail!(\"invalid repository origin\");\n    }\n    // Explicit local paths may contain colons; otherwise :: selects a Git helper.\n    let explicit_local = std::path::Path::new(origin).is_absolute()\n        || origin.starts_with(\"./\")\n        || origin.starts_with(\"../\");\n    if !explicit_local\n        && origin.split_once(\"::\").is_some_and(|(prefix, _)| {\n            !prefix.is_empty() && !prefix.contains(['/', '\\\\', '[', ']', '@', ':'])\n        })\n    {\n        bail!(\"Git remote helpers are not supported for remote onboarding\");\n    }\n    if !explicit_local && origin.contains(\"://\") {\n        let url = url::Url::parse(origin).wrap_err(\"invalid repository URL\")?;\n        if !matches!(url.scheme(), \"https\" | \"ssh\" | \"file\") {\n            bail!(\"remote bootstrap requires HTTPS, SSH, or a local path\");\n        }\n    }","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/remote_repository.rs#L28-L64","documentation":"Before using a repository origin for remote onboarding, mise validates it (validate_origin). Origins starting with '-' (which git could parse as flags) or containing any control character are rejected outright as invalid, protecting the later git invocation from option injection and malformed input.","triggerScenarios":"Configuring a remote repository origin that begins with a dash or contains control bytes (e.g. a stray \\n, \\t, or escaped terminal sequences) in fetch or install_at flows.","commonSituations":"Copy-pasting an origin from terminal output that included ANSI codes; accidentally prefixing the URL with a dash; generating the origin from untrusted/unsanitized input.","solutions":["Remove the leading '-' and any control characters from the origin value","Re-type the origin manually rather than pasting from terminal output","Validate the origin with a plain `git remote -v`-style check before configuring"],"exampleFix":"// before\norigin = \"-https://git.example.com/repo.git\"\n// after\norigin = \"https://git.example.com/repo.git\"","handlingStrategy":"validation","validationCode":"const safeOrigin = (o) => typeof o === 'string' && o.length > 0 && !o.startsWith('-') && !/[^\\x20-\\x7E]/.test(o);","typeGuard":"const isPrintableNonDashOrigin = (s) => typeof s === 'string' && !s.startsWith('-') && [...s].every(ch => ch.charCodeAt(0) >= 32 && ch.charCodeAt(0) < 127);","tryCatchPattern":"try { setOrigin(o); } catch (e) { o = sanitizeOrigin(o); setOrigin(o); }","preventionTips":["Sanitize pasted URLs to strip ANSI/control sequences","Never prefix origin values with dashes","Store origins in plain ASCII in config files"],"tags":["git","validation","injection-prevention"],"backgroundTag":"invalid-url","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}