can1357/oh-my-pi · error · HTTPException

remote url contains forbidden control bytes

Error message

remote url contains forbidden control bytes

What it means

The proxy rejects remote URLs containing control bytes (\x00-\x1f, \x7f) or their percent-encoded forms (%00, %0a, %0d) with HTTP 400. Control characters in URLs enable argument injection and header/response splitting when passed to git or HTTP layers, so any URL matched by _FORBIDDEN_URL_BYTES_RE is refused before use.

Source

Thrown at python/robomp/src/proxy/server.py:344

        raise HTTPException(400, "remote url must not specify a port")
    if (parsed.hostname or "").lower() != "github.com":
        raise HTTPException(400, f"remote url host must be github.com for repo {expected_repo!r}")
    if parsed.params or parsed.query or parsed.fragment:
        raise HTTPException(400, "remote url must not contain params, query, or fragment")
    path = parsed.path.strip("/")
    if path.endswith(".git"):
        path = path[:-4]
    if path.lower() != expected_repo.lower():
        raise HTTPException(400, f"remote url does not match repo {expected_repo!r}")
    return _github_url_for_repo(expected_repo)


def _remote_auth_for_url(url: str, expected_repo: str, token: str) -> _RemoteAuth:
    raw = url.strip()
    if not raw or raw != url:
        raise HTTPException(400, "remote url must not be empty or padded")
    if _FORBIDDEN_URL_BYTES_RE.search(raw):
        raise HTTPException(400, "remote url contains forbidden control bytes")
    if raw.startswith("-"):
        raise HTTPException(400, "remote url must not start with '-'")
    if _REMOTE_HELPER_RE.match(raw):
        raise HTTPException(400, "git remote helper transports are disabled")
    scheme = (urlparse(raw).scheme or "").lower()
    if scheme in ("http", "https"):
        normalized = _normalized_github_https_url(raw, expected_repo)
        return _RemoteAuth(url=normalized, token=token, auth_url=normalized)
    return _RemoteAuth(url=raw, token=None, auth_url=None)


def _clone_remote_auth(clone_url: str, expected_repo: str, token: str) -> _RemoteAuth:
    try:
        return _remote_auth_for_url(clone_url, expected_repo, token)
    except HTTPException:
        log.warning(
            "gh-proxy: refusing clone — clone_url is not permitted",
            extra={"expected_repo": expected_repo},

View on GitHub (pinned to 9690622007)

Solutions

  1. Remove all control characters from the URL; keep it on a single printable line
  2. If a value may contain them, sanitize: url = re.sub(r'[\x00-\x1f\x7f]', '', url) and re-encode intentionally
  3. Quote YAML scalar values and use single-line strings for URLs

Example fix

// before
const url = `https://github.com/o/r.git\n`;
// after
const url = "https://github.com/o/r.git".replace(/[\x00-\x1f\x7f]/g, "");
Defensive patterns

Strategy: validation

Validate before calling

if /[\x00-\x1f\x7f]|%(?:00|0a|0d)/i.test(url) throw new Error("control bytes in remote url");

Type guard

function hasNoControlBytes(u: unknown): u is string {
  return typeof u === "string" && !/[\x00-\x1f\x7f]/.test(u) && !/%(?:00|0a|0d)/i.test(u);
}

Prevention

When it happens

Trigger: Supplying a URL containing raw newlines, carriage returns, tabs, NUL bytes, or percent-encoded %0a/%0d/%00 sequences to an endpoint that resolves to _clone_remote_auth or _origin_remote_auth.

Common situations: Multi-line YAML strings folding newlines into a URL value; log-file scraped values with embedded \r\n; crafted URLs from untrusted input testing CRLF injection; copying URLs from terminals that wrapped lines.

Understand the failure class

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/b3399b9819049b7a. Report an issue: GitHub.