oraios/serena · error · SolidLSPException

Refusing to download from host '{hostname or '<unknown>'}';

Error message

Refusing to download from host '{hostname or '<unknown>'}'; allowed hosts: {sorted(normalized_allowed_hosts)}

What it means

Raised by _validate_download_host when the hostname of the download URL is not in the configured allowlist (allowed_hosts). This SSRF/supply-chain guard refuses downloads from unapproved hosts, using case-insensitive exact hostname match.

Source

Thrown at src/solidlsp/ls_utils.py:585

        if expected_sha256 is None:
            return

        actual_sha256 = FileUtils.calculate_sha256(file_path)
        if actual_sha256.lower() != expected_sha256.lower():
            raise SolidLSPException(f"Checksum verification failed for '{file_path}': expected {expected_sha256}, got {actual_sha256}")

    @staticmethod
    def _validate_download_host(url: str, allowed_hosts: Sequence[str] | None) -> None:
        """
        Validates that a download URL resolves to one of the configured hosts.
        """
        if not allowed_hosts:
            return

        hostname = urlparse(url).hostname
        normalized_allowed_hosts = {host.lower() for host in allowed_hosts}
        if hostname is None or hostname.lower() not in normalized_allowed_hosts:
            raise SolidLSPException(
                f"Refusing to download from host '{hostname or '<unknown>'}'; allowed hosts: {sorted(normalized_allowed_hosts)}"
            )

    @staticmethod
    def _validate_extraction_path(member_name: str, target_path: str) -> str:
        """
        Validates that an archive member stays within the extraction root and returns its destination path.
        """
        normalized_parts = Path(member_name).parts
        if any(part == ".." for part in normalized_parts):
            raise SolidLSPException(f"Unsafe archive member '{member_name}': path traversal is not allowed")

        absolute_target_path = os.path.abspath(target_path)
        absolute_member_path = os.path.abspath(os.path.join(target_path, member_name))
        if not (absolute_member_path.startswith(absolute_target_path + os.sep) or absolute_member_path == absolute_target_path):
            raise SolidLSPException(f"Unsafe archive member '{member_name}': path escapes extraction directory")

        return absolute_member_path

View on GitHub (pinned to 7fcbca7e62)

Solutions

  1. Add the new hostname (lowercase, exact) to the allowed_hosts configuration
  2. Fix the download URL to point at an allowlisted host
  3. If the URL is malformed, correct it so urlparse can extract a hostname

Example fix

// before
allowed_hosts = ["github.com"]
// after
allowed_hosts = ["github.com", "objects.githubusercontent.com"]
Defensive patterns

Strategy: validation

Validate before calling

from urllib.parse import urlparse
hostname = urlparse(url).hostname
allowed = {h.lower() for h in allowed_hosts}
assert hostname and hostname.lower() in allowed, f"host {hostname!r} not in allowlist"

Type guard

def host_is_allowed(url: str, allowed_hosts: list[str]) -> bool:
    h = urlparse(url).hostname
    return bool(h and h.lower() in {x.lower() for x in allowed_hosts})

Try / catch

try:
    download_file_verified(url, path, allowed_hosts=allowed_hosts)
except SolidLSPException as e:
    if "Refusing to download from host" in str(e):
        raise ConfigError(f"URL host not allowlisted; update allowed_hosts for {url}") from e
    raise

Prevention

When it happens

Trigger: download_file_verified called with allowed_hosts configured while the URL points to a different host, a subdomain not exactly listed, or a URL with no hostname (e.g. a malformed/relative URL).

Common situations: Upstream download URL moves to a new CDN domain while the config allowlist still lists the old host; typo in configured host; mirror URLs not added to the allowlist.

Related errors


AI-assisted analysis of oraios/serena@7fcbca7e62 (2026-08-29). Data as JSON: /api/errors/74a0ee7048ef2662. Report an issue: GitHub.