{"record":{"id":"5b6748c080e343d5","repo":"Hmbown/CodeWhale","slug":"codewhale-account-api-base-url-must-not-contain-cr","errorCode":null,"errorMessage":"Codewhale account API base URL must not contain credentials","messagePattern":"Codewhale account API base URL must not contain credentials","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/cli/src/cloud.rs","lineNumber":764,"sourceCode":"        writeln!(out, \"Email: {}\", printable(&user.email))?;\n    }\n    if !user.plan.trim().is_empty() {\n        writeln!(out, \"Plan: {}\", printable(&user.plan))?;\n    }\n    writeln!(out, \"Profile: {}\", printable(profile))?;\n    writeln!(out, \"API: {api_base}\")?;\n    Ok(())\n}\n\nstruct ValidatedApiBase {\n    url: Url,\n    display: String,\n}\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();","sourceCodeStart":746,"sourceCodeEnd":782,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/cli/src/cloud.rs#L746-L782","documentation":"validate_api_base rejects any account API base URL containing userinfo (user:password@host) before it is ever used. Credentials embedded in URLs leak via logs, history, and error messages, and none of the cloud endpoints accept them, so this is an early hard stop with a clear message.","triggerScenarios":"Passing --api-base or config like https://user:pass@api.codewhale.net or http://admin:x@localhost:9000; copy-pasting a URL that includes basic-auth userinfo.","commonSituations":"Users templating authenticated internal URLs into the api-base setting, docs/examples that include userinfo placeholders, muscle memory from tools that support basic auth in the URL.","solutions":["Remove the userinfo portion: use a plain origin such as https://api.codewhale.net.","If the endpoint needs auth, that belongs in headers/tokens, not the base URL (Codewhale cloud auth is the OAuth session, not URL credentials).","Check both the --api-base flag and the stored profile config for the stray credentials."],"exampleFix":"# before\n--api-base https://user:secret@api.codewhale.net\n\n# after\n--api-base https://api.codewhale.net","handlingStrategy":"validation","validationCode":"// Validate before passing to the CLI\nfn base_url_is_safe(value: &str) -> bool {\n    url::Url::parse(value).map(|u| u.username().is_empty() && u.password().is_none()).unwrap_or(false)\n}","typeGuard":"fn is_credential_free_origin(u: &url::Url) -> bool {\n    u.username().is_empty() && u.password().is_none()\n        && matches!(u.path(), \"\" | \"/\")\n        && u.query().is_none() && u.fragment().is_none()\n}","tryCatchPattern":null,"preventionTips":["Lint config for userinfo in URLs before deploy.","Teach templates to emit origins only.","Remember cloud auth is OAuth-session based; URLs never carry credentials."],"tags":["url-validation","security","cloud","cli"],"backgroundTag":"url-contains-credentials","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}