summary
specify bundle catalog add (add_source in src/specify_cli/bundler/commands_impl/catalog_config.py) persists remote catalog URLs into catalogs.yaml without any HTTPS or host validation, and crashes with a raw ValueError traceback on a malformed IPv6 URL.
The other two catalog-URL validation sites enforce this:
add_source is the third copy of this contract and has neither check, so junk enters the persisted config and only fails later at fetch time, far from the command that caused it.
reproduction
from pathlib import Path
import tempfile
from specify_cli.bundler.commands_impl.catalog_config import add_source
d = Path(tempfile.mkdtemp())
add_source(d, "http://evil.example.com/catalog.json", policy="install-allowed", priority=10) # accepted; plain HTTP, non-localhost
add_source(d, "https://:8080", policy="install-allowed", priority=10) # accepted; no host
add_source(d, "https://user@", policy="install-allowed", priority=10) # accepted; no host
add_source(d, "https://[::1/c.json", policy="install-allowed", priority=10) # raw ValueError: Invalid IPv6 URL
Via the CLI this is specify bundle catalog add <url>; the first three print ✓ Added catalog ... and write the entry to .specify/catalogs.yaml, the fourth escapes the except BundlerError handler in catalog_add and produces a traceback. remove_source hits the same unguarded urlparse through its _canonicalize_url fallback.
impact
proposed fix
Mirror the catalogs.py validation in add_source for http(s) schemes (HTTPS-only with localhost exception, check parsed.hostname not netloc), and wrap the urlparse calls so malformed URLs raise BundlerError. Fully additive; file://, builtin:// and local paths unaffected. I have a patch with tests ready and can open a PR.
summary
specify bundle catalog add(add_sourceinsrc/specify_cli/bundler/commands_impl/catalog_config.py) persists remote catalog URLs intocatalogs.yamlwithout any HTTPS or host validation, and crashes with a rawValueErrortraceback on a malformed IPv6 URL.The other two catalog-URL validation sites enforce this:
specify_cli.catalogs._validate_catalog_url— HTTPS-only (HTTP for localhost) +hostnamecheck (Host-less catalog URLs (e.g. https://:8080) pass URL validation in base and preset validators #3209/fix(catalogs): reject host-less catalog URLs in base and preset validators #3210)bundler/services/adapters.py:_validate_remote_url— same, host-less gap being fixed in fix(bundler): reject host-less catalog URLs in adapters (use hostname, not netloc) #3333 (Bug: bundler adapters accept host-less catalog URLs (https://:8080), unlike catalogs.py #3332)add_sourceis the third copy of this contract and has neither check, so junk enters the persisted config and only fails later at fetch time, far from the command that caused it.reproduction
Via the CLI this is
specify bundle catalog add <url>; the first three print✓ Added catalog ...and write the entry to.specify/catalogs.yaml, the fourth escapes theexcept BundlerErrorhandler incatalog_addand produces a traceback.remove_sourcehits the same unguardedurlparsethrough its_canonicalize_urlfallback.impact
BundlerError.proposed fix
Mirror the
catalogs.pyvalidation inadd_sourceforhttp(s)schemes (HTTPS-only with localhost exception, checkparsed.hostnamenotnetloc), and wrap theurlparsecalls so malformed URLs raiseBundlerError. Fully additive;file://,builtin://and local paths unaffected. I have a patch with tests ready and can open a PR.