Hmbown/CodeWhale · error

bundle URLs may not include credentials

Error message

bundle URLs may not include credentials

What it means

validate_bundle_url rejects any bundle URL that embeds credentials (userinfo) — a username or password in the URL itself (e.g. https://user:pass@host/bundle.toml). Embedding secrets in URLs leaks them into logs, shell history, redirect targets, and process listings, so the bundle fetcher refuses them outright rather than forwarding them.

Solutions

  1. Remove the username/password from the URL and rely on a credential-free endpoint (token in header or network-level auth).
  2. Serve the bundle over plain https without basic auth — e.g. behind SSO, mTLS, or an IP-allowlisted proxy.
  3. If credentials are unavoidable, pre-fetch the bundle with curl using an auth header and host it locally on loopback HTTP (allowed).
  4. Never place the token in the URL; audit shell history and logs for the leaked credential and rotate it.

Example fix

// before
let url = "https://ci-bot:s3cret@config.internal.example/bundle.toml";
// after
let url = "https://config.internal.example/bundle.toml"; // auth via network layer, not URL
Defensive patterns

Strategy: validation

Validate before calling

let url = reqwest::Url::parse(input)?;
if !url.username().is_empty() || url.password().is_some() {
    return Err(anyhow!("strip userinfo from bundle URL"));
}

Type guard

fn is_credential_free(u: &reqwest::Url) -> bool {
    u.username().is_empty() && u.password().is_none()
}

Prevention

When it happens

Trigger: Calling fetch_bundle with a URL containing `user:password@` before the host, or a URL with just a username component; validate_bundle_redirect rejects the same shape when a server redirects to a credential-bearing URL.

Common situations: Users pasting a URL copied from a tool that embeds an API token in the path userinfo; internal artifact servers that historically used basic-auth-in-URL; CI secrets templated directly into the bundle URL.

Related errors


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

Appendix: source

Thrown at crates/cli/src/config_bundles.rs:819

    // Read at most MAX_BUNDLE_BYTES + 1 so an oversize body is detected
    // rather than silently truncated.
    let mut buffer = Vec::new();
    let body = response;
    body.take(MAX_BUNDLE_BYTES + 1)
        .read_to_end(&mut buffer)
        .map_err(|_| anyhow!("reading remote bundle failed"))?;
    if buffer.len() as u64 > MAX_BUNDLE_BYTES {
        bail!("remote bundle exceeds the {MAX_BUNDLE_BYTES} byte limit; refused");
    }
    Ok(buffer)
}

fn validate_bundle_url(url: &reqwest::Url) -> Result<()> {
    if !matches!(url.scheme(), "http" | "https") {
        bail!("unsupported bundle URL scheme; use https");
    }
    if !url.username().is_empty() || url.password().is_some() {
        bail!("bundle URLs may not include credentials");
    }
    let host = url.host_str().context("bundle URL must include a host")?;
    match url.scheme() {
        "https" => Ok(()),
        "http" if is_loopback_bundle_host(host) => Ok(()),
        "http" => bail!("plain http is only allowed for loopback hosts; use https"),
        _ => unreachable!("scheme was validated above"),
    }
}

fn validate_bundle_redirect(initial_scheme: &str, next_url: &reqwest::Url) -> Result<()> {
    validate_bundle_url(next_url)?;
    if next_url.scheme() != initial_scheme {
        bail!("bundle redirects may not change URL scheme");
    }
    Ok(())
}

View on GitHub (pinned to 73e0f67d83)