fix(catalogs): raise catalog error, not raw ValueError, on a malformed URL (#3435)

CatalogStackBase._validate_catalog_url read parsed.hostname, which raises
ValueError on a malformed authority (e.g. an unclosed ipv6 bracket
"https://[::1"). every other reject path raises the class catalog error, so
the raw ValueError leaked to the caller. wrap the parse + hostname access and
convert ValueError to the normal error via cls._error.

twin of the bundler fix in adapters._validate_remote_url.
This commit is contained in:
Quratulain-bilal
2026-07-10 23:39:01 +05:00
committed by GitHub
parent 1736f0746b
commit 14fa3ada08
2 changed files with 21 additions and 3 deletions

View File

@@ -71,8 +71,12 @@ class CatalogStackBase:
"""Validate that a catalog URL uses HTTPS, except localhost HTTP."""
from urllib.parse import urlparse
parsed = urlparse(url)
is_localhost = parsed.hostname in ("localhost", "127.0.0.1", "::1")
try:
parsed = urlparse(url)
hostname = parsed.hostname
except ValueError:
raise cls._error(f"Catalog 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 cls._error(
f"Catalog URL must use HTTPS (got {parsed.scheme}://). "
@@ -81,7 +85,7 @@ class CatalogStackBase:
# Check hostname, not netloc: netloc is truthy for host-less URLs like
# "https://:8080" or "https://user@", so the host guarantee this error
# promises would not actually hold. hostname is None in those cases (#3209).
if not parsed.hostname:
if not hostname:
raise cls._error("Catalog URL must be a valid URL with a host.")
def _load_catalog_config(self, config_path: Path) -> list[CatalogEntry] | None:

View File

@@ -84,6 +84,20 @@ class TestCatalogURLValidation:
with pytest.raises(IntegrationCatalogError, match="valid URL"):
IntegrationCatalog._validate_catalog_url(url)
@pytest.mark.parametrize(
"url",
[
"https://[::1", # unclosed ipv6 bracket
"https://[not-an-ip]/c.json", # bracketed non-ip host
],
)
def test_malformed_url_rejected_cleanly(self, url):
# A malformed authority makes urlparse/hostname raise ValueError. The
# validator must turn that into its normal catalog error, not leak a
# raw ValueError to the caller.
with pytest.raises(IntegrationCatalogError, match="malformed"):
IntegrationCatalog._validate_catalog_url(url)
# ---------------------------------------------------------------------------
# IntegrationCatalog — active catalogs