Hmbown/CodeWhale · error · anyhow::Error

Codewhale account API base URL must use HTTPS (loopback HTTP

Error message

Codewhale account API base URL must use HTTPS (loopback HTTP is allowed for testing)

What it means

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.

Source

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

}

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();
    Ok(ValidatedApiBase { url, display })
}

fn validate_verification_url(
    value: &str,
    api_base: &str,
    user_code: &str,
    complete: bool,
) -> Result<String> {
    let url =
        Url::parse(value).context("The Codewhale service returned an invalid verification URL")?;
    if value != url.as_str() {
        bail!("The Codewhale service returned an unsafe verification URL");

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Use https:// for any remote API base.
  2. For local testing, keep the server on loopback and use http://127.0.0.1:PORT (or http://localhost).
  3. If you must test on another machine, put TLS in front (self-signed cert with CA config) instead of widening to plain HTTP.

Example fix

# before
--api-base http://192.168.1.20:9000

# after
--api-base http://127.0.0.1:9000   # loopback http is allowed
# or https://my-test-host.internal:9000
Defensive patterns

Strategy: validation

Validate before calling

fn scheme_is_allowed(u: &url::Url) -> bool {
    u.scheme() == "https" || (u.scheme() == "http" && is_loopback_host(u.host_str().unwrap_or("")))
}

Type guard

fn is_valid_api_base(value: &str) -> bool {
    url::Url::parse(value.trim()).map(|u| {
        (u.scheme() == "https" || u.scheme() == "http" && is_loopback_host(u.host_str().unwrap_or("")))
            && matches!(u.path(), "" | "/") && u.query().is_none() && u.fragment().is_none()
    }).unwrap_or(false)
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/239a306d0e33be3b. Report an issue: GitHub.