jdx/mise · error · eyre::Report

GitHub Actions OIDC request URL must use HTTPS

Error message

GitHub Actions OIDC request URL must use HTTPS

What it means

The GitHub Actions OIDC request URL (normally injected via ACTIONS_ID_TOKEN_REQUEST_URL) must use https; plain http is accepted only for loopback hosts (localhost, 127.0.0.1, ::1). validate_oidc_request_url enforces this at credential construction so the ACTIONS_ID_TOKEN_REQUEST_TOKEN bearer value is never sent over an unencrypted channel to a remote host.

Source

Thrown at crates/mise-cache-core/src/lib.rs:792

fn unix_timestamp() -> Result<u64> {
    Ok(SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .map_err(|err| eyre!("system clock is before the Unix epoch: {err}"))?
        .as_secs())
}

fn validate_oidc_request_url(url: &Url) -> Result<()> {
    if url.scheme() == "https"
        || url.scheme() == "http"
            && url.host().is_some_and(|host| match host {
                Host::Domain(host) => host.eq_ignore_ascii_case("localhost"),
                Host::Ipv4(address) => address.is_loopback(),
                Host::Ipv6(address) => address.is_loopback(),
            })
    {
        Ok(())
    } else {
        bail!("GitHub Actions OIDC request URL must use HTTPS")
    }
}

fn validate_remote_url(base_url: &Url, authenticated: bool) -> Result<()> {
    if base_url.scheme() == "https" {
        return Ok(());
    }
    if base_url.scheme() != "http" {
        bail!("remote cache URL must use HTTPS");
    }
    let is_loopback = base_url.host().is_some_and(|host| match host {
        Host::Domain(host) => host.eq_ignore_ascii_case("localhost"),
        Host::Ipv4(address) => address.is_loopback(),
        Host::Ipv6(address) => address.is_loopback(),
    });
    if !is_loopback && authenticated {
        bail!("remote cache URL must use HTTPS except for loopback development servers");
    }

View on GitHub (pinned to 9dcfcaa0dc)

Solutions

  1. In real CI, use the https URL GitHub injects — do not override ACTIONS_ID_TOKEN_REQUEST_URL
  2. For local mocks, bind the server to a loopback host (localhost/127.0.0.1) so http is accepted, or serve it over https
  3. Audit entrypoint scripts and proxies for rewriting of the variable

Example fix

# before
ACTIONS_ID_TOKEN_REQUEST_URL=http://oidc.internal:9990/token

# after (real CI: unset the override and use GitHub's injected value)
unset ACTIONS_ID_TOKEN_REQUEST_URL

# after (local mock: keep it loopback)
ACTIONS_ID_TOKEN_REQUEST_URL=http://127.0.0.1:9990/token
Defensive patterns

Strategy: validation

Validate before calling

fn oidc_url_is_acceptable(url: &url::Url) -> bool {
    let loopback = url.host().is_some_and(|h| match h {
        url::Host::Domain(h) => h.eq_ignore_ascii_case("localhost"),
        url::Host::Ipv4(a) => a.is_loopback(),
        url::Host::Ipv6(a) => a.is_loopback(),
        _ => false,
    });
    url.scheme() == "https" || (url.scheme() == "http" && loopback)
}

// check before constructing the client with an oidc_audience set
let url: url::Url = std::env::var("ACTIONS_ID_TOKEN_REQUEST_URL")?.parse()?;
anyhow::ensure!(oidc_url_is_acceptable(&url), "OIDC URL must be https (or loopback http)");

Prevention

When it happens

Trigger: Setting ACTIONS_ID_TOKEN_REQUEST_URL to an http:// URL with a non-loopback host; a GithubActionsOidcCredential::new call with such a URL; a proxy or entrypoint script rewriting the injected https URL to http; leaking a dev-mode value into CI.

Common situations: Custom runner images that export the variable to point at a mock OIDC server; local development containers with stale env vars; self-hosted setups trying to reach an internal non-TLS endpoint on another machine.

Related errors


AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17). Data as JSON: /api/errors/07283582b98ab450. Report an issue: GitHub.