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);
}
}
headersView on GitHub (pinned to afd2eddd3a)
Solutions
- Fix the target URL to stay HTTPS end-to-end; replace the http:// replacement/redirect target with https://.
- Remove credentials from the URL (use a token header or a credential helper bound to the HTTPS host instead of user:pass@ userinfo).
- 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
- Never embed user:pass@ in URLs; use HTTPS token headers.
- Enforce https-only mirrors in config and disable plain-HTTP redirects.
- Test mirror/proxy setups with curl -IL to confirm no http:// redirects.
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
- remote cache URL must use HTTPS except for loopback developm
- unsupported redirect
- invalid HTTP setup repository URL; use a Git credential help
- setup repository URLs must not contain credentials, query pa
- remote action manifest ETag does not match its body
AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09).
Data as JSON: /api/errors/992be2326d25dad0.
Report an issue: GitHub.