{"record":{"id":"fd65a5970837eec6","repo":"zeroclaw-labs/zeroclaw","slug":"url-cannot-contain-whitespace-fd65a5","errorCode":null,"errorMessage":"URL cannot contain whitespace","messagePattern":"URL cannot contain whitespace","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"warning","filePath":"crates/zeroclaw-tools/src/text_browser.rs","lineNumber":222,"sourceCode":"    fn build_dump_args(_browser: &str, url: &str) -> Vec<String> {\n        // All supported browsers (lynx, links, w3m) use the same `-dump` flag\n        vec![\"-dump\".to_string(), url.to_string()]\n    }\n}\n\nfn validate_text_browser_url(\n    url: &str,\n    allowed_private_hosts: &[String],\n    validate_dns: impl FnOnce(&str, bool) -> anyhow::Result<()>,\n) -> anyhow::Result<String> {\n    let url = url.trim();\n\n    if url.is_empty() {\n        anyhow::bail!(\"URL cannot be empty\");\n    }\n\n    if url.chars().any(char::is_whitespace) {\n        anyhow::bail!(\"URL cannot contain whitespace\");\n    }\n\n    if !url.starts_with(\"http://\") && !url.starts_with(\"https://\") {\n        anyhow::bail!(\"Only http:// and https:// URLs are allowed\");\n    }\n\n    let parsed = reqwest::Url::parse(url)\n        .map_err(|e| anyhow::Error::msg(format!(\"Invalid URL format: {e}\")))?;\n\n    if !parsed.username().is_empty() || parsed.password().is_some() {\n        anyhow::bail!(\"URL userinfo is not allowed\");\n    }\n\n    let host_str = parsed\n        .host_str()\n        .ok_or_else(|| anyhow::Error::msg(\"URL must include a host\"))?;\n\n    let bare_host = host_str.trim_start_matches('[').trim_end_matches(']');","sourceCodeStart":204,"sourceCodeEnd":240,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-tools/src/text_browser.rs#L204-L240","documentation":"validate_text_browser_url rejects any URL containing whitespace characters after trimming (spaces, tabs, newlines anywhere inside the string). This catches malformed URLs that would otherwise be split, misparsed, or smuggled past later host checks.","triggerScenarios":"URLs like \"https://example.com/a b\", copy-pasted URLs containing a newline or tab, or a space before the fragment/query; also unescaped spaces in query parameters built via string concatenation.","commonSituations":"Pasting URLs from PDFs or chat messages that carry soft line breaks; building URLs with format! and forgetting percent-encoding for values that contain spaces.","solutions":["Percent-encode spaces and other unsafe characters (%20) before submitting","Strip stray newlines/tabs from pasted input (the tool only trims ends, not inner whitespace)","Build URLs with a proper encoder (e.g. urlencoding::encode for query values) instead of string concatenation"],"exampleFix":"// before\nlet url = format!(\"https://example.com/search?q={query}\"); // q=\"rust web\"\n// after\nlet url = format!(\"https://example.com/search?q={}\", urlencoding::encode(&query));","handlingStrategy":"validation","validationCode":"if url.chars().any(char::is_whitespace) {\n    let cleaned: String = url.split_whitespace().collect(); // or percent-encode properly\n}","typeGuard":"fn url_has_no_inner_whitespace(u: &str) -> bool { !u.trim().chars().any(char::is_whitespace) }","tryCatchPattern":"Err(e) if e.to_string() == \"URL cannot contain whitespace\" => {\n    // percent-encode the offending segments and retry once\n}","preventionTips":["Always build URLs with an encoder (urlencoding::encode) for query values","Sanitize pasted strings by stripping line breaks before use","Lint CI inputs for raw spaces in URL fields"],"tags":["url","validation","encoding","whitespace"],"backgroundTag":"url-validation-failed","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}