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
- Remove the username/password from the URL and rely on a credential-free endpoint (token in header or network-level auth).
- Serve the bundle over plain https without basic auth — e.g. behind SSO, mTLS, or an IP-allowlisted proxy.
- If credentials are unavoidable, pre-fetch the bundle with curl using an auth header and host it locally on loopback HTTP (allowed).
- 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
- Never embed user:password@ in URLs; use headers or network-level auth.
- Rotate any credential that was ever embedded in a URL — it likely reached logs/history.
- Lint CI scripts for the `@` userinfo pattern in bundle URLs.
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
- plain http is only allowed for loopback hosts; use https
- bundle redirects may not change URL scheme
- Codewhale-owned credential file DACL is not…
- Codewhale-owned credential file DACL must grant only one…
- Codewhale-owned credential file
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)