Hmbown/CodeWhale · error

plain http is only allowed for loopback hosts; use https

Error message

plain http is only allowed for loopback hosts; use https

What it means

validate_bundle_url only permits plain http for loopback hosts (localhost, 127.0.0.1, ::1, etc. per is_loopback_bundle_host); any other host must use https. This prevents bundle contents — which become executable configuration — from traveling unencrypted across a network where they could be tampered with or observed.

Solutions

  1. Serve the bundle over https (enable TLS on the host or front it with a TLS-terminating proxy).
  2. For local testing, bind the server to 127.0.0.1/localhost so plain http is permitted.
  3. Use `ssh -L` or another tunnel so the bundle appears on loopback over http.
  4. Install a self-signed/internal-CA certificate and use https with the CA trusted.

Example fix

// before
let url = "http://config.internal.example/bundle.toml";
// after
let url = "https://config.internal.example/bundle.toml";
// local testing only:
// let url = "http://127.0.0.1:8080/bundle.toml";
Defensive patterns

Strategy: validation

Validate before calling

let url = reqwest::Url::parse(input)?;
let host = url.host_str().context("missing host")?;
if url.scheme() == "http" && !(host == "localhost" || host == "127.0.0.1" || host == "::1") {
    return Err(anyhow!("non-loopback http bundle URLs are not allowed"));
}

Type guard

fn is_https_or_loopback(u: &reqwest::Url) -> bool {
    match u.scheme() {
        "https" => true,
        "http" => matches!(u.host_str(), Some("localhost") | Some("127.0.0.1") | Some("::1")),
        _ => false,
    }
}

Prevention

When it happens

Trigger: fetch_bundle called with an http:// URL pointing at a non-loopback host (e.g. http://config.internal.example/bundle.toml); the same check applies to redirect targets via validate_bundle_redirect.

Common situations: Pointing the bundle URL at an internal HTTP-only mirror; using an http LAN address for a quick test from another machine; legacy infrastructure without TLS on the config host.

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/f87dd23e98e7d128. Report an issue: GitHub.

Appendix: source

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

        .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(())
}

fn is_loopback_bundle_host(host: &str) -> bool {
    let normalized = host
        .strip_prefix('[')
        .and_then(|value| value.strip_suffix(']'))
        .unwrap_or(host);
    normalized.eq_ignore_ascii_case("localhost")

View on GitHub (pinned to 73e0f67d83)