{"record":{"id":"e42289ed3fc83b39","repo":"jdx/mise","slug":"setup-repository-urls-must-not-contain-credentials","errorCode":null,"errorMessage":"setup repository URLs must not contain credentials, query parameters, or fragments; use a Git credential helper or SSH agent","messagePattern":"setup repository URLs must not contain credentials, query parameters, or fragments; use a Git credential helper or SSH agent","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/history/sync/network.rs","lineNumber":44,"sourceCode":"        .trim_start()\n        .get(..5)\n        .is_some_and(|prefix| prefix.eq_ignore_ascii_case(\"http:\"))\n        || value\n            .trim_start()\n            .get(..6)\n            .is_some_and(|prefix| prefix.eq_ignore_ascii_case(\"https:\"));\n    if http_like && url::Url::parse(value).is_err() {\n        bail!(\"invalid HTTP setup repository URL; use a Git credential helper for authentication\");\n    }\n    if let Ok(url) = url::Url::parse(value) {\n        let http = matches!(url.scheme(), \"http\" | \"https\");\n        if url.password().is_some()\n            || (http\n                && (!url.username().is_empty()\n                    || url.query().is_some()\n                    || url.fragment().is_some()))\n        {\n            bail!(\n                \"setup repository URLs must not contain credentials, query parameters, or fragments; use a Git credential helper or SSH agent\"\n            );\n        }\n    }\n    Ok(())\n}\n\n#[cfg(test)]\nmod tests {\n    use super::{HistoryRepo, PushOutcome, Remote, UPSTREAM_REF, validate_url};\n\n    #[test]\n    fn disposable_tip_probe_is_shallow_but_ordinary_fetch_keeps_ancestry() {\n        let temp = tempfile::tempdir().unwrap();\n        let source = HistoryRepo::open_or_init_in(&temp.path().join(\"source\"))\n            .unwrap()\n            .unwrap();\n        let tree = source.empty_object(\"tree\").unwrap();","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/history/sync/network.rs#L26-L62","documentation":"validate_url checks setup-repository URLs before any network git operation. A URL with embedded credentials (userinfo password, or any username on http/https), a query string, or a fragment is refused because such parts leak secrets into logs/remotes or make the remote address ambiguous; the library demands credential helpers or SSH agents instead.","triggerScenarios":"Passing a remote like https://user:token@host/repo.git, an http(s) URL with any username, or a URL containing ?query or #fragment to validate_url, whether directly or via fetch_with_depth, push, symbolic_head, or ls_remote.","commonSituations":"Developers paste clone URLs copied from a cloud Git UI that embed a personal access token; CI config injects credentials into the remote URL; a stray ?ref= or #anchor is left in the URL from copying a web link.","solutions":["Strip credentials from the URL and configure a git credential helper (git config credential.helper) or use SSH remotes.","Remove the query string and fragment from the URL; reference branches/commits via refspecs instead.","If an http(s) username is needed for auth, switch to SSH (git@host:repo.git) or rely on the credential helper's configured username.","Verify with git ls-remote <cleaned-url> that the sanitized URL still resolves before retrying."],"exampleFix":"// before\nlet url = \"https://ci-bot:ghp_token@example.com/repo.git\";\nnetwork.validate_url(&url)?;\n// after\nlet url = \"https://example.com/repo.git\"; // auth via credential.helper or SSH","handlingStrategy":"validation","validationCode":"let parsed = url::Url::parse(candidate)?;\nlet http = matches!(parsed.scheme(), \"http\" | \"https\");\nif parsed.password().is_some()\n    || (http && (!parsed.username().is_empty() || parsed.query().is_some() || parsed.fragment().is_some()))\n{\n    return Err(\"strip credentials/query/fragment from the setup URL\".into());\n}","typeGuard":"fn is_clean_remote_url(u: &url::Url) -> bool {\n    let http = matches!(u.scheme(), \"http\" | \"https\");\n    u.password().is_none()\n        && (!http || (u.username().is_empty() && u.query().is_none() && u.fragment().is_none()))\n}","tryCatchPattern":null,"preventionTips":["Use credential helpers (git credential-store/cache) or SSH agents instead of embedding tokens in URLs.","Never copy clone URLs straight from a web UI address bar; strip ? and # parts.","Lint CI pipelines for credentials in remote URLs.","Prefer SSH remotes for authenticated access."],"tags":["git","url-validation","security","credentials"],"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"}