fix(cli): exit cleanly on malformed IPv6 URLs in extension/preset/workflow add#3369
fix(cli): exit cleanly on malformed IPv6 URLs in extension/preset/workflow add#3369marcelsafin wants to merge 2 commits into
extension/preset/workflow add#3369Conversation
…kflow add extension add --from, preset add --from, and workflow add <url> parsed the user-supplied URL with a bare urlparse before their HTTPS/host validation, so an unclosed IPv6 bracket escaped as a raw ValueError traceback. Wrap each parse and emit the surrounding validation's clean error style + typer.Exit(1) instead. Fixes github#3368 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR fixes a CLI crash path where malformed IPv6 URLs (e.g., missing a closing ]) caused urllib.parse.urlparse(...) to raise a raw ValueError, bypassing existing validation and producing a traceback. It adds try/except ValueError guards at the three direct CLI entry points that accept URLs and introduces regression tests to ensure a clean error + exit code 1.
Changes:
- Guard
urlparse(...)inextension add --from,preset add --from, andworkflow add <url>to exit cleanly onValueError. - Print a consistent “Invalid URL: …” error message (with Rich markup escaping) and exit with
typer.Exit(1). - Add one regression test per command to assert clean failure behavior for malformed IPv6 URLs.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
src/specify_cli/extensions/_commands.py |
Wraps urlparse(from_url) in try/except ValueError to avoid raw tracebacks on malformed IPv6 input. |
src/specify_cli/presets/_commands.py |
Wraps initial urlparse(from_url) in try/except ValueError and prints a clean “Invalid URL” error. |
src/specify_cli/workflows/_commands.py |
Wraps urlparse(source) in try/except ValueError for the direct URL-install branch. |
tests/test_extensions.py |
Adds a regression test asserting extension add --from exits cleanly on malformed IPv6 URLs. |
tests/test_presets.py |
Adds a regression test asserting preset add --from exits cleanly on malformed IPv6 URLs and does not attempt network access. |
tests/test_workflows.py |
Adds a regression test asserting workflow add <url> exits cleanly on malformed IPv6 URLs. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| try: | ||
| _parsed = _urlparse(from_url) | ||
| except ValueError: | ||
| from rich.markup import escape as _escape_markup | ||
|
|
||
| console.print(f"[red]Error:[/red] Invalid URL: {_escape_markup(from_url)}") | ||
| raise typer.Exit(1) |
There was a problem hiding this comment.
Fixed in 90442cf. I moved the parse into _StripAuthOnRedirect.redirect_request itself: it parses the redirect target once before the validator and the stdlib handler run, and converts ValueError into URLError, which every download path already catches. This covers _validate_download_redirect and any other caller of open_url in one place. Also escaped from_url in the preset install message; the extension path already used _escape_markup. Added a regression test in TestRedirectStripping.
…ect handler Parse the redirect target once in _StripAuthOnRedirect.redirect_request before the validator and stdlib handler run, converting ValueError into URLError which every download path already catches. Also escape from_url in the preset install message so IPv6 brackets don't break Rich markup. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Description
Fixes #3368
specify extension add --from,specify preset add --from, andspecify workflow add <url>crashed with a rawValueError: Invalid IPv6 URLtraceback when given a malformed IPv6 URL (unclosed bracket, e.g.https://[::1/ext.zip).Root cause: each site calls a bare
urlparse(...)on the user-supplied URL before its HTTPS/host validation runs, so theValueErrorescapes the handler and the friendly validation error is never reached.Fix: wrap the parse at each of the three sites in
try/except ValueErrorand emit the same clean[red]Error:[/red] Invalid URL: ...+typer.Exit(1)style the surrounding validation uses (with Rich markup escaping of the user input, matching each file's conventions).Same crash class as the IPv6 half of #3366/#3367, which covered the bundler catalog-config path; these are the remaining direct CLI entry points where argv reaches an unguarded
urlparse.Testing
uv run specify --helpuv sync && uv run pytest— 3825 passed, 87 skippedOne regression test per command (
tests/test_extensions.py,tests/test_presets.py,tests/test_workflows.py), each asserting exit code 1, no non-SystemExitexception, and the clean error message. All three fail onmainwithValueError('Invalid IPv6 URL').AI Disclosure
Bug found, patch and tests written with GitHub Copilot CLI; reproduction and full test suite run and verified locally by me.