{"record":{"id":"2e122f383a522573","repo":"can1357/oh-my-pi","slug":"remote-url-must-not-be-empty-or-padded","errorCode":null,"errorMessage":"remote url must not be empty or padded","messagePattern":"remote url must not be empty or padded","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"python/robomp/src/proxy/server.py","lineNumber":342,"sourceCode":"        raise HTTPException(400, \"remote url has invalid port\") from exc\n    if port is not None:\n        raise HTTPException(400, \"remote url must not specify a port\")\n    if (parsed.hostname or \"\").lower() != \"github.com\":\n        raise HTTPException(400, f\"remote url host must be github.com for repo {expected_repo!r}\")\n    if parsed.params or parsed.query or parsed.fragment:\n        raise HTTPException(400, \"remote url must not contain params, query, or fragment\")\n    path = parsed.path.strip(\"/\")\n    if path.endswith(\".git\"):\n        path = path[:-4]\n    if path.lower() != expected_repo.lower():\n        raise HTTPException(400, f\"remote url does not match repo {expected_repo!r}\")\n    return _github_url_for_repo(expected_repo)\n\n\ndef _remote_auth_for_url(url: str, expected_repo: str, token: str) -> _RemoteAuth:\n    raw = url.strip()\n    if not raw or raw != url:\n        raise HTTPException(400, \"remote url must not be empty or padded\")\n    if _FORBIDDEN_URL_BYTES_RE.search(raw):\n        raise HTTPException(400, \"remote url contains forbidden control bytes\")\n    if raw.startswith(\"-\"):\n        raise HTTPException(400, \"remote url must not start with '-'\")\n    if _REMOTE_HELPER_RE.match(raw):\n        raise HTTPException(400, \"git remote helper transports are disabled\")\n    scheme = (urlparse(raw).scheme or \"\").lower()\n    if scheme in (\"http\", \"https\"):\n        normalized = _normalized_github_https_url(raw, expected_repo)\n        return _RemoteAuth(url=normalized, token=token, auth_url=normalized)\n    return _RemoteAuth(url=raw, token=None, auth_url=None)\n\n\ndef _clone_remote_auth(clone_url: str, expected_repo: str, token: str) -> _RemoteAuth:\n    try:\n        return _remote_auth_for_url(clone_url, expected_repo, token)\n    except HTTPException:\n        log.warning(","sourceCodeStart":324,"sourceCodeEnd":360,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/python/robomp/src/proxy/server.py#L324-L360","documentation":"The GitHub proxy validates every remote URL before handing it to git. A URL that is empty after no-strip-difference or differs from its stripped form (leading/trailing whitespace) is rejected with HTTP 400, because padded URLs cause git argument/credential confusion and empty URLs are meaningless. This guard runs in _remote_auth_for_url, used by both clone and origin-remote auth paths.","triggerScenarios":"Calling an endpoint that resolves to _clone_remote_auth or _origin_remote_auth with a repo/remote URL that is '' or contains leading/trailing spaces, tabs, or newlines (e.g. ' https://github.com/o/r.git').","commonSituations":"YAML or env config values picking up trailing whitespace/newlines; string concatenation adding a stray space; shell scripts capturing 'git config --get remote.origin.url' output without trimming; empty placeholder values in CI templates.","solutions":["Trim the URL before sending, e.g. url.strip(), and skip empty values entirely","Fix the config file or env var so the value has no surrounding whitespace or newlines","If the URL comes from a subprocess/file read, use .strip() on the captured output"],"exampleFix":"// before\nclone({ \"repo\": \" https://github.com/org/repo.git \" })\n// after\nconst url = (process.env.REPO_URL ?? \"\").trim();\nif (url) clone({ repo: url });","handlingStrategy":"validation","validationCode":"const trimmed = (url ?? \"\").trim();\nif (!trimmed) throw new Error(\"remote url is empty\");\nif (trimmed !== url) throw new Error(`remote url has padding: ${JSON.stringify(url)}`);","typeGuard":"function isCleanUrl(u: unknown): u is string {\n  return typeof u === \"string\" && u.length > 0 && u === u.trim();\n}","tryCatchPattern":null,"preventionTips":["Always .trim() URLs read from env vars, config files, or subprocess output","Use structured config parsers that strip scalar values","Fail fast on empty values instead of forwarding them"],"tags":["http-400","input-validation","git-url"],"backgroundTag":"url-validation-rejected","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}