jdx/mise · critical

refusing to send credentials over an HTTPS-to-HTTP URL repla

Error message

refusing to send credentials over an HTTPS-to-HTTP URL replacement

What it means

The HTTP layer validates URL redirects/replacements (src/http.rs, credential-downgrade check) and refuses to carry credentials onto a URL that has been rewritten from HTTPS to plain HTTP. Sending auth material (userinfo credentials or host auth headers) over an unencrypted downgrade would leak it on the wire. The guard fires when `downgraded && has_credentials`.

Source

Thrown at src/http.rs:1847

pub(crate) fn ensure_secure_replacement_credentials(
    original_url: &Url,
    url: &Url,
    headers: &HeaderMap,
) -> Result<()> {
    let has_credentials = headers
        .iter()
        .any(|(name, value)| is_credential_header(name, value));
    ensure_secure_url_replacement(original_url, url, has_credentials)
}

pub(crate) fn ensure_secure_url_replacement(
    original_url: &Url,
    url: &Url,
    has_credentials: bool,
) -> Result<()> {
    let downgraded = original_url.scheme() == "https" && url.scheme() == "http";
    let has_credentials = has_credentials || !url.username().is_empty() || url.password().is_some();
    ensure!(
        !downgraded || !has_credentials,
        "refusing to send credentials over an HTTPS-to-HTTP URL replacement"
    );
    Ok(())
}

/// Get HTTP Basic authentication headers from netrc file for the given URL
pub(crate) fn netrc_headers(url: &Url) -> HeaderMap {
    let mut headers = HeaderMap::new();
    if let Some(host) = url.host_str()
        && let Some((login, password)) = netrc::get_credentials(host)
    {
        let credentials = BASE64_STANDARD.encode(format!("{login}:{password}"));
        if let Ok(value) = HeaderValue::from_str(&format!("Basic {credentials}")) {
            headers.insert(reqwest::header::AUTHORIZATION, value);
        }
    }
    headers

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Fix the target URL to stay HTTPS end-to-end; replace the http:// replacement/redirect target with https://.
  2. Remove credentials from the URL (use a token header or a credential helper bound to the HTTPS host instead of user:pass@ userinfo).
  3. If the endpoint truly only supports HTTP, host it behind TLS or use a local trusted proxy that terminates HTTPS, and do not send credentials to it.

Example fix

// before
url = "https://mirror.example.com/artifact.tgz" -> redirect to "http://insecure.example.com/artifact.tgz" (with auth header attached)

// after
url = "https://mirror.example.com/artifact.tgz" -> redirect to "https://insecure.example.com/artifact.tgz" (enable TLS on the mirror)
Defensive patterns

Strategy: validation

Validate before calling

// Verify scheme before attaching credentials or following a replacement URL
if original.scheme() == "https" && replacement.scheme() != "https" {
    panic!("refusing to send credentials over downgraded URL: {}", replacement);
}

Type guard

fn is_https(url: &Url) -> bool { url.scheme() == "https" }

Prevention

When it happens

Trigger: A request starts at an `https://` URL that embeds credentials (username/password) or resolves host auth headers, and a redirect/response handler replaces the URL with an `http://` one, or the replacement URL itself carries username/password userinfo; the safety check `!downgraded || !has_credentials` then fails.

Common situations: Misconfigured artifact mirrors or proxies behind plain HTTP that issue redirects from an HTTPS URL; hand-written registry/tool URLs with `user:pass@http://...`; a middlebox rewriting URLs to http during asset resolution.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/992be2326d25dad0. Report an issue: GitHub.