{"record":{"id":"239a306d0e33be3b","repo":"Hmbown/CodeWhale","slug":"codewhale-account-api-base-url-must-use-https-loo","errorCode":null,"errorMessage":"Codewhale account API base URL must use HTTPS (loopback HTTP is allowed for testing)","messagePattern":"Codewhale account API base URL must use HTTPS \\(loopback HTTP is allowed for testing\\)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/cli/src/cloud.rs","lineNumber":777,"sourceCode":"}\n\nfn validate_api_base(value: &str) -> Result<ValidatedApiBase> {\n    let mut url = Url::parse(value.trim()).context(\"invalid Codewhale account API base URL\")?;\n    if !url.username().is_empty() || url.password().is_some() {\n        bail!(\"Codewhale account API base URL must not contain credentials\");\n    }\n    if url.query().is_some() || url.fragment().is_some() {\n        bail!(\"Codewhale account API base URL must not contain a query or fragment\");\n    }\n    if !matches!(url.path(), \"\" | \"/\") {\n        bail!(\"Codewhale account API base URL must be an origin without a path\");\n    }\n    let host = url\n        .host_str()\n        .ok_or_else(|| anyhow!(\"Codewhale account API base URL must include a host\"))?;\n    let allowed = url.scheme() == \"https\" || (url.scheme() == \"http\" && is_loopback_host(host));\n    if !allowed {\n        bail!(\n            \"Codewhale account API base URL must use HTTPS (loopback HTTP is allowed for testing)\"\n        );\n    }\n    url.set_path(\"/\");\n    let display = url.as_str().trim_end_matches('/').to_string();\n    Ok(ValidatedApiBase { url, display })\n}\n\nfn validate_verification_url(\n    value: &str,\n    api_base: &str,\n    user_code: &str,\n    complete: bool,\n) -> Result<String> {\n    let url =\n        Url::parse(value).context(\"The Codewhale service returned an invalid verification URL\")?;\n    if value != url.as_str() {\n        bail!(\"The Codewhale service returned an unsafe verification URL\");","sourceCodeStart":759,"sourceCodeEnd":795,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/cli/src/cloud.rs#L759-L795","documentation":"validate_api_base enforces transport security: the scheme must be HTTPS, with plain HTTP allowed only when the host is loopback (for local testing). This prevents access tokens and provider keys from being sent in cleartext to a remote host.","triggerScenarios":"Configuring --api-base http://api.example.com (remote, non-loopback, plain HTTP); using an http:// test server bound to a non-loopback interface/LAN hostname; typoing the scheme.","commonSituations":"Pointing at a local dev server via the machine's LAN IP or a hostname that resolves externally, corporate http-only mirrors, or forgetting the s in https.","solutions":["Use https:// for any remote API base.","For local testing, keep the server on loopback and use http://127.0.0.1:PORT (or http://localhost).","If you must test on another machine, put TLS in front (self-signed cert with CA config) instead of widening to plain HTTP."],"exampleFix":"# before\n--api-base http://192.168.1.20:9000\n\n# after\n--api-base http://127.0.0.1:9000   # loopback http is allowed\n# or https://my-test-host.internal:9000","handlingStrategy":"validation","validationCode":"fn scheme_is_allowed(u: &url::Url) -> bool {\n    u.scheme() == \"https\" || (u.scheme() == \"http\" && is_loopback_host(u.host_str().unwrap_or(\"\")))\n}","typeGuard":"fn is_valid_api_base(value: &str) -> bool {\n    url::Url::parse(value.trim()).map(|u| {\n        (u.scheme() == \"https\" || u.scheme() == \"http\" && is_loopback_host(u.host_str().unwrap_or(\"\")))\n            && matches!(u.path(), \"\" | \"/\") && u.query().is_none() && u.fragment().is_none()\n    }).unwrap_or(false)\n}","tryCatchPattern":null,"preventionTips":["Default to https:// for every remote base.","Keep test servers bound to 127.0.0.1 to stay within the http allowance.","Put TLS in front of any non-loopback test endpoint."],"tags":["url-validation","security","tls","cloud","cli"],"backgroundTag":"insecure-scheme-rejected","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}