github/spec-kit · error · BundlerError

Refusing to download {label} over non-HTTPS URL: {url}

Error message

Refusing to download {label} over non-HTTPS URL: {url}

What it means

Spec Kit enforces HTTPS for every bundle download URL. Plain HTTP is accepted only for the exact hosts localhost, 127.0.0.1, and ::1; every other scheme and host combination is rejected as BundlerError before network access. Redirect and final response URLs must satisfy the same policy.

Source

Thrown at src/specify_cli/commands/bundle/__init__.py:923

def _require_https(label: str, url: str) -> None:
    from urllib.parse import urlparse

    # urlparse / hostname access raise ValueError on a malformed authority;
    # keep the documented BundlerError contract (older Pythons surface this via
    # the .hostname access below rather than at the urlparse call).
    try:
        parsed = urlparse(url)
        hostname = parsed.hostname
        # Accessing ``port`` performs urllib's syntax/range validation.
        _ = parsed.port
    except ValueError:
        raise BundlerError(
            f"Refusing to download {label}: URL is malformed: {url}"
        ) from None
    is_localhost = hostname in ("localhost", "127.0.0.1", "::1")
    if parsed.scheme != "https" and not (parsed.scheme == "http" and is_localhost):
        raise BundlerError(
            f"Refusing to download {label} over non-HTTPS URL: {url}"
        )
    if not parsed.hostname:
        raise BundlerError(f"Refusing to download {label} from URL with no host: {url}")


def _download_remote_manifest(
    entry_id: str,
    url: str,
    *,
    expected_sha256: str | None = None,
):
    """Fetch a remote bundle artifact over HTTPS and extract its manifest."""
    import io
    import tempfile
    from pathlib import PurePosixPath
    from urllib.parse import urlparse as _urlparse

View on GitHub (pinned to bf88c9f9a8)

Solutions

  1. Serve the artifact over HTTPS and update the catalog download_url to the HTTPS URL.
  2. For a local test server, use exactly `http://localhost`, `http://127.0.0.1`, or `http://[::1]`; `0.0.0.0` and other host names are intentionally rejected.
  3. If a redirect is involved, configure the redirect target to HTTPS so every hop passes the guard.
  4. If you do not control the catalog, install from a local bundle path or choose a catalog source with an HTTPS URL.

Example fix

# before
"download_url": "http://artifacts.internal/my-bundle-1.0.0.zip"

# after
"download_url": "https://artifacts.internal/my-bundle-1.0.0.zip"
Defensive patterns

Strategy: validation

Validate before calling

from urllib.parse import urlparse

LOCAL_HOSTS = {"localhost", "127.0.0.1", "::1"}

def is_allowed_download_url(url: str) -> bool:
    try:
        p = urlparse(url)
        host = p.hostname
        _ = p.port
    except ValueError:
        return False
    if not host:
        return False
    return p.scheme == "https" or (p.scheme == "http" and host in LOCAL_HOSTS)

Try / catch

except BundlerError as exc:
    if "non-HTTPS URL" in str(exc):
        report_that_https_or_exact_localhost_is_required()
    else:
        raise

Prevention

When it happens

Trigger: A catalog entry has `ftp://`, `http://`, `git://`, or another non-HTTPS download_url for a non-localhost host, or an HTTPS download redirects to HTTP. The check also runs under --offline before the offline gate.

Common situations: Using an internal Artifactory/Nexus/GitLab server that is configured for HTTP only, testing against `http://0.0.0.0:8000` or an alternate localhost name, or a redirect chain that downgrades to HTTP.

Related errors


AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14). Data as JSON: /api/errors/158f99583dd00529. Report an issue: GitHub.