{"record":{"id":"d3f93209f41a8247","repo":"Hmbown/CodeWhale","slug":"context-returned-an-untrusted-verification-uri","errorCode":null,"errorMessage":"{context} returned an untrusted verification URI","messagePattern":"(.+?) returned an untrusted verification URI","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/config/src/device_code.rs","lineNumber":203,"sourceCode":"/// \"open this\" call, so a malicious or compromised response could otherwise\n/// launch `file:`, a custom app scheme, or a helper with attacker-chosen\n/// arguments. pi requires `https:`; Codewhale additionally allows `http:` on a\n/// loopback host, which is what self-hosted issuers and the device-code tests\n/// use — matching the loopback allowance the account login already makes.\n///\n/// Embedded credentials are rejected in every case.\npub fn validate_browser_verification_uri(raw: &str, context: &str) -> Result<String> {\n    let trimmed = raw.trim();\n    let Ok(url) = url_scheme_and_host(trimmed) else {\n        bail!(\"{context} returned an unusable verification URI\");\n    };\n    let (scheme, host, has_credentials) = url;\n    if has_credentials {\n        bail!(\"{context} returned a verification URI with embedded credentials\");\n    }\n    let allowed = scheme == \"https\" || (scheme == \"http\" && is_loopback_host(&host));\n    if !allowed {\n        bail!(\"{context} returned an untrusted verification URI\");\n    }\n    Ok(trimmed.to_string())\n}\n\n/// Minimal scheme/host/credential split, so this module stays free of a URL\n/// dependency (`codewhale-config` deliberately has no `reqwest`/`url`).\npub(crate) fn url_scheme_and_host(raw: &str) -> Result<(String, String, bool), ()> {\n    let (scheme, rest) = raw.split_once(\"://\").ok_or(())?;\n    if scheme.is_empty()\n        || !scheme\n            .bytes()\n            .all(|b| b.is_ascii_alphanumeric() || b == b'+' || b == b'-' || b == b'.')\n    {\n        return Err(());\n    }\n    let authority = rest\n        .split(['/', '?', '#'])\n        .next()","sourceCodeStart":185,"sourceCodeEnd":221,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/config/src/device_code.rs#L185-L221","documentation":"Thrown when a device-code verification URI uses a scheme/host combination the library does not trust: anything other than https, except plain http which is allowed only for loopback hosts (localhost/127.0.0.1 style). This blocks open-redirect and downgrade attacks where a provider hands back an http:// or arbitrary-scheme URL that could be intercepted or handled by a malicious local handler.","triggerScenarios":"validate_browser_verification_uri receiving http://example.com/activate (non-loopback host), ftp:// or custom-scheme URIs, or a loopback-unqualified http URL on a remote host.","commonSituations":"Self-hosted auth servers behind plain HTTP on a LAN hostname; providers returning app-specific deep links (myapp://activate) instead of web URLs.","solutions":["Serve the verification endpoint over HTTPS and return that URL","If developing locally, use http://localhost or http://127.0.0.1 which are allowed","Put a TLS-terminating proxy in front of an HTTP-only auth server","Replace custom app schemes with an https web URL"],"exampleFix":"// before\nlet uri = \"http://auth.internal.lan/activate\";\nvalidate_browser_verification_uri(uri, \"login\")?;\n// after\nlet uri = \"https://auth.internal.lan/activate\";\nvalidate_browser_verification_uri(uri, \"login\")?;","handlingStrategy":"validation","validationCode":"fn uri_is_trusted(raw: &str) -> bool {\n    let t = raw.trim();\n    let Some((scheme, rest)) = t.split_once(\"://\") else { return false };\n    let host = rest.split('/').next().unwrap_or(\"\");\n    scheme == \"https\" || (scheme == \"http\" && (host == \"localhost\" || host.starts_with(\"127.0.0.1\") || host.starts_with(\"[::1]\")))\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Serve all verification endpoints over HTTPS","Only use plain http for localhost development","Replace custom app-scheme deep links with https web URLs"],"tags":["oauth","security","url","tls"],"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-22T21:17:16.096Z"}