Hmbown/CodeWhale · error · anyhow::Error

Codewhale account API base URL must not contain credentials

Error message

Codewhale account API base URL must not contain credentials

What it means

validate_api_base parses the user-supplied account API base URL and rejects URLs embedding userinfo (username or password). Credentials in the base URL would be sent with every request and can leak into logs and config, so they are prohibited outright.

Solutions

  1. Strip the user:password@ portion and use the host-only origin: https://api.example.com.
  2. Supply credentials another way if the endpoint needs them (headers, a proxy you control) — never in the base URL.
  3. Re-run the command with the cleaned origin.

Example fix

// before
CODEWHALE_API_BASE=https://user:secret@api.example.com
// after
CODEWHALE_API_BASE=https://api.example.com
Defensive patterns

Strategy: validation

Validate before calling

fn has_no_credentials(url: &str) -> bool {
    url::Url::parse(url.trim())
        .map(|u| u.username().is_empty() && u.password().is_none())
        .unwrap_or(false)
}

Prevention

When it happens

Trigger: Setting the account API base URL (env/config/flag) to a URL containing user:pass@host userinfo, e.g. https://admin:secret@api.example.com.

Common situations: Pasting an internal service URL that includes basic-auth credentials; templating base URLs from connection strings that embed credentials.

Understand the failure class

Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/5b6748c080e343d5. Report an issue: GitHub.

Appendix: source

Thrown at crates/cli/src/cloud.rs:1018

        writeln!(out, "Email: {}", printable(&user.email))?;
    }
    if !user.plan.trim().is_empty() {
        writeln!(out, "Plan: {}", printable(&user.plan))?;
    }
    writeln!(out, "Profile: {}", printable(profile))?;
    writeln!(out, "API: {api_base}")?;
    Ok(())
}

struct ValidatedApiBase {
    url: Url,
    display: String,
}

fn validate_api_base(value: &str) -> Result<ValidatedApiBase> {
    let mut url = Url::parse(value.trim()).context("invalid Codewhale account API base URL")?;
    if !url.username().is_empty() || url.password().is_some() {
        bail!("Codewhale account API base URL must not contain credentials");
    }
    if url.query().is_some() || url.fragment().is_some() {
        bail!("Codewhale account API base URL must not contain a query or fragment");
    }
    if !matches!(url.path(), "" | "/") {
        bail!("Codewhale account API base URL must be an origin without a path");
    }
    let host = url
        .host_str()
        .ok_or_else(|| anyhow!("Codewhale account API base URL must include a host"))?;
    let allowed = url.scheme() == "https" || (url.scheme() == "http" && is_loopback_host(host));
    if !allowed {
        bail!(
            "Codewhale account API base URL must use HTTPS (loopback HTTP is allowed for testing)"
        );
    }
    url.set_path("/");
    let display = url.as_str().trim_end_matches('/').to_string();

View on GitHub (pinned to 73e0f67d83)