{"record":{"id":"3c010f8061bc1a79","repo":"zeroclaw-labs/zeroclaw","slug":"unsupported-url-scheme","errorCode":null,"errorMessage":"unsupported URL scheme: {}","messagePattern":"unsupported URL scheme: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-tools/src/browser_delegate.rs","lineNumber":90,"sourceCode":"    fn validate_url(&self, url: &str) -> anyhow::Result<()> {\n        let parsed = url.parse::<reqwest::Url>().map_err(|e| {\n            ::zeroclaw_log::record!(\n                WARN,\n                ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Reject)\n                    .with_outcome(::zeroclaw_log::EventOutcome::Failure)\n                    .with_attrs(::serde_json::json!({\n                        \"url\": url,\n                        \"error\": format!(\"{}\", e),\n                    })),\n                \"browser_delegate: invalid URL\"\n            );\n            anyhow::Error::msg(format!(\"invalid URL '{}': {}\", url, e))\n        })?;\n\n        // Only allow http/https schemes\n        let scheme = parsed.scheme();\n        if scheme != \"http\" && scheme != \"https\" {\n            anyhow::bail!(\"unsupported URL scheme: {}\", scheme);\n        }\n\n        let domain = parsed.host_str().unwrap_or(\"\").to_string();\n\n        if domain.is_empty() {\n            anyhow::bail!(\"URL has no host: {}\", url);\n        }\n\n        // Check blocked domains first (deny takes precedence)\n        for blocked in &self.config.blocked_domains {\n            if domain_matches(&domain, blocked) {\n                anyhow::bail!(\"domain '{}' is blocked by browser_delegate policy\", domain);\n            }\n        }\n\n        // If allowed_domains is non-empty, it acts as an allowlist\n        if !self.config.allowed_domains.is_empty() {\n            let allowed = self","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-tools/src/browser_delegate.rs#L72-L108","documentation":"browser_delegate validates every URL it touches — both explicit task URLs and URLs extracted from free-form task text by a regex — through reqwest::Url parsing followed by a scheme allowlist: only http and https pass. Any other scheme (ftp, file, ws, chrome, javascript, ...) is rejected before the browser CLI subprocess sees it.","triggerScenarios":"A task containing `ftp://mirror.example.com/file` or `file:///etc/passwd`. Note that validate_task_urls scans the entire prompt with a https?:// regex, so a scheme-violating link buried in prose also triggers this check.","commonSituations":"Agents pasting README links that mix ftp/file schemes into the delegated task; users expecting desktop-browser scheme tolerance; test fixtures using about:blank or chrome:// URLs.","solutions":["Remove or rewrite non-http(s) links in the task text (use https:// mirrors)","If another scheme is genuinely needed, handle it outside browser_delegate — the allowlist is a security boundary","Pre-scan task text with the same https?:// extraction and reject before submitting"],"exampleFix":"// before\ntask = \"Download ftp://mirror.example.org/pkg.tar.gz\"\n// after\ntask = \"Download https://mirror.example.org/pkg.tar.gz\"","handlingStrategy":"validation","validationCode":"for m in regex::Regex::new(r\"https?://[^\\s\\)\\]\\},\\\"'`<>\")\n    .unwrap()\n    .find_iter(&task)\n{\n    let parsed: reqwest::Url = m.as_str().parse()?;\n    if parsed.scheme() != \"http\" && parsed.scheme() != \"https\" {\n        return Err(format!(\"non-http(s) URL in task: {}\", m.as_str()));\n    }\n}","typeGuard":null,"tryCatchPattern":"match delegate.execute(args).await {\n    Ok(res) if res.success => { /* ... */ }\n    Ok(res) => {\n        if res.error.as_deref().unwrap_or_default().contains(\"unsupported URL scheme\") {\n            // strip or rewrite the offending link from the task text\n        }\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Sanitize free-form task text for URLs before delegation","Keep http/https as the only schemes in agent-facing documentation","Test tasks containing mixed links before enabling browser_delegate"],"tags":["browser-delegate","url","scheme","security","validation"],"backgroundTag":"url-scheme-not-allowed","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}