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_pathView on GitHub (pinned to 7fcbca7e62)
Solutions
- Add the new hostname (lowercase, exact) to the allowed_hosts configuration
- Fix the download URL to point at an allowlisted host
- 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
- Keep allowed_hosts in sync with upstream CDN domains (including subdomains)
- Use exact hostnames — the check is exact-match, not suffix match
- Centralize download URLs in config so allowlist and URLs change together
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
- Unknown archive type '{archive_type}'
- Checksum verification failed for '{file_path}': expected {ex
- Dashboard is not running.
- Cannot activate project '{project.project_name}': it require
- Project '{project_root_or_name}' not found: Not a valid proj
AI-assisted analysis of oraios/serena@7fcbca7e62 (2026-08-29).
Data as JSON: /api/errors/74a0ee7048ef2662.
Report an issue: GitHub.