{"record":{"id":"0a16aee07e9cfb45","repo":"Hmbown/CodeWhale","slug":"remote-url-contains-control-characters","errorCode":null,"errorMessage":"remote url contains control characters","messagePattern":"remote url contains control characters","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/cloud_dispatch.rs","lineNumber":414,"sourceCode":"/// and network remotes that do not classify as a supported forge. Local\n/// path remotes (offline fixtures) are allowed when they do not start\n/// with `-` and carry no userinfo.\npub fn safe_git_remote_url(raw: &str) -> bool {\n    validate_git_remote_url(raw).is_ok()\n}\n\n/// Classify and validate `job.remote_url` before any `git clone` or\n/// sandbox clone. Returns the trimmed URL on success.\npub fn validate_git_remote_url(raw: &str) -> Result<String> {\n    let url = raw.trim();\n    if url.is_empty() || url.len() > MAX_REMOTE_BYTES {\n        bail!(\"remote url is empty or oversized\");\n    }\n    if url.starts_with('-') {\n        bail!(\"remote url must not start with '-'\");\n    }\n    if url.chars().any(char::is_control) {\n        bail!(\"remote url contains control characters\");\n    }\n    if remote_has_userinfo(url) {\n        bail!(\"remote url must not embed userinfo\");\n    }\n    if looks_like_network_git_url(url) && classify_url(url).is_none() {\n        bail!(\"remote url is not a supported forge\");\n    }\n    Ok(url.to_string())\n}\n\n/// Display form of a remote: userinfo is never printed.\npub fn redact_remote_url(raw: &str) -> String {\n    redact_url_userinfo(raw)\n}\n\nfn remote_has_userinfo(url: &str) -> bool {\n    if let Ok(parsed) = reqwest::Url::parse(url) {\n        return !parsed.username().is_empty() || parsed.password().is_some();","sourceCodeStart":396,"sourceCodeEnd":432,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/cloud_dispatch.rs#L396-L432","documentation":"validate_git_remote_url rejects URLs containing control characters (per char::is_control). Control bytes in a URL can smuggle newlines, escapes, or terminal sequences into git commands and logs, so they are refused before any clone.","triggerScenarios":"Calling validate_git_remote_url / safe_git_remote_url / clone_repository with a string containing e.g. \\n, \\r, \\t, or other C0/C1 control bytes — typically from multiline paste, binary-corrupted config, or concatenation with log output.","commonSituations":"Pasting a URL from a terminal where a line wrap introduced a newline; reading a value from a file that kept a trailing \\r (CRLF); string building that accidentally joined a URL with log lines.","solutions":["Trim and strip control characters (or reject) at input time before validation.","Re-enter the URL as a single clean line; copy it from the forge's official clone widget.","If reading from a file, normalize line endings and split on whitespace, taking one token.","Log the sanitized value when reporting the failure; never echo raw control bytes."],"exampleFix":"// before\nlet url = format!(\"https://github.com/org/repo.git\\n{}\");\nvalidate_git_remote_url(&url)?;\n// after\nlet url = raw.trim().chars().filter(|c| !c.is_control()).collect::<String>();\nvalidate_git_remote_url(&url)?;","handlingStrategy":"validation","validationCode":"fn has_control_chars(s: &str) -> bool {\n    s.chars().any(char::is_control)\n}","typeGuard":"fn clean_url(s: &str) -> Option<String> {\n    let cleaned: String = s.trim().chars().filter(|c| !c.is_control()).collect();\n    (cleaned == s.trim()).then_some(cleaned)\n}","tryCatchPattern":"match validate_git_remote_url(raw) {\n    Err(e) if e.to_string().contains(\"control characters\") => {\n        let sanitized = raw.trim().chars().filter(|c| !c.is_control()).collect::<String>();\n        eprintln!(\"URL contained control chars; sanitized: {sanitized}\");\n    }\n    other => { /* ... */ }\n}","preventionTips":["Normalize CRLF and strip trailing newlines when reading URLs from files.","Paste URLs as a single line; avoid terminal selection that spans wraps.","Sanitize or reject control bytes at every input boundary, not just for remotes.","Never log raw unvalidated URL input."],"tags":["validation","git","url","security"],"backgroundTag":"invalid-url-format","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}