Hmbown/CodeWhale · error

bundle redirects may not change URL scheme

Error message

bundle redirects may not change URL scheme

What it means

validate_bundle_redirect enforces that an HTTP redirect during a bundle fetch does not change the URL scheme (e.g. https → http, or http → https crossing onto the wrong class of host). Scheme changes mid-fetch would let a redirect silently downgrade security (TLS to plaintext) or bypass the loopback-only-http rule, so the fetch is aborted.

Solutions

  1. Point the bundle URL directly at the final destination so no redirect occurs.
  2. Configure the server to keep redirects scheme-preserving (https→https, http-loopback→http-loopback).
  3. Have ops fix the redirect chain so the https location does not downgrade to http.
  4. Test the redirect chain with `curl -IL <url>` and correct the Location header on the server.

Example fix

// before (server config)
# server: redirect https://a/bundle -> http://mirror/bundle
// after
# redirect https://a/bundle -> https://mirror/bundle  (scheme preserved)
Defensive patterns

Strategy: validation

Validate before calling

// Before configuring a bundle URL, check the redirect chain:
// curl -sIL -o /dev/null -w '%{url_effective} %{scheme}\n' <bundle-url>
// Ensure every hop keeps the same scheme.

Type guard

fn redirect_is_scheme_preserving(initial: &str, next: &reqwest::Url) -> bool {
    next.scheme() == initial
}

Prevention

When it happens

Trigger: A bundle server responds with 301/302/307/308 whose Location header targets a different scheme than the originally requested scheme; fetch_bundle validates each hop with validate_bundle_redirect, which also validates the redirect target itself.

Common situations: An https bundle URL that redirects to an http mirror; a load balancer redirecting http→https (fails if the client originally used plain http on a non-loopback target anyway); a CDN rewriting to a different scheme.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


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

Appendix: source

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

    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")
        || normalized.to_ascii_lowercase().ends_with(".localhost")
        || normalized
            .parse::<std::net::IpAddr>()
            .is_ok_and(|address| address.is_loopback())
}

// ---------------------------------------------------------------------------
// Export

View on GitHub (pinned to 73e0f67d83)