Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/specify_cli/catalogs.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ def _validate_catalog_url(cls, url: str) -> None:
try:
parsed = urlparse(url)
hostname = parsed.hostname
# Accessing ``port`` performs urllib's syntax/range validation;
# ``hostname`` alone does not, so a non-numeric or out-of-range
# port would otherwise pass validation here and escape as a raw
# http.client.InvalidURL at fetch time.
Comment on lines +79 to +80
_ = parsed.port
except ValueError:
raise cls._error(f"Catalog URL is malformed: {url}") from None
is_localhost = hostname in ("localhost", "127.0.0.1", "::1")
Expand Down
9 changes: 6 additions & 3 deletions tests/integrations/test_integration_catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,12 +116,15 @@ def test_hostless_url_with_truthy_netloc_rejected(self, url):
[
"https://[::1", # unclosed ipv6 bracket
"https://[not-an-ip]/c.json", # bracketed non-ip host
"https://example.com:notaport/c.json", # non-numeric port
"https://example.com:65536/c.json", # out-of-range port
],
)
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.
# A malformed authority makes urlparse/hostname raise ValueError, and a
# bad port makes ``parsed.port`` raise it. The validator must turn that
# into its normal catalog error, not leak a raw ValueError to the caller
# (or, for a bad port, accept the URL and fail later at fetch time).
with pytest.raises(IntegrationCatalogError, match="malformed"):
IntegrationCatalog._validate_catalog_url(url)

Expand Down