Skip to content

fix(cli): reject a malformed --endpoint-url with a clear VALIDATION_ERROR#19

Open
Davidson3556 wants to merge 1 commit into
TestSprite:mainfrom
Davidson3556:fix/validate-endpoint-url
Open

fix(cli): reject a malformed --endpoint-url with a clear VALIDATION_ERROR#19
Davidson3556 wants to merge 1 commit into
TestSprite:mainfrom
Davidson3556:fix/validate-endpoint-url

Conversation

@Davidson3556

@Davidson3556 Davidson3556 commented Jun 24, 2026

Copy link
Copy Markdown

Fixes #18

Describe the changes you have made in this PR -

A malformed API endpoint was not validated, so it failed in a way that didn't point at the real (config) problem:

  • --endpoint-url "not a url"Error: Invalid URL (exit 1) — a raw new URL() throw with no guidance.
  • --endpoint-url "localhost:3000" (missing scheme — new URL() parses it as scheme localhost:) and --endpoint-url "ftp://x.com" (wrong scheme) → fetch failed / Service temporarily unavailable, emitted only after a full retry-and-backoff cycle — it looks like a network outage, not a typo.

The endpoint can come from --endpoint-url, TESTSPRITE_API_URL, or the credentials file api_url.

Changes:

  • Add assertValidEndpointUrl(rawUrl) in client-factory.ts and run it in both the real and dry-run paths of makeHttpClient, on the resolved endpoint — so all three sources are covered. A malformed value now throws a typed VALIDATION_ERROR (exit 5) with an actionable message, before any fetch/retry.
  • Validate before emitting the dry-run banner so a rejected endpoint doesn't first announce a "sample response".
  • Deliberately not an SSRF guard. Unlike the --target-url check in target-url.ts, this does not reject localhost or private hosts: the API endpoint legitimately points at self-hosted, local-dev, or mock backends (the test suite itself targets http://localhost). Only unparseable values or non-http(s) schemes are rejected, so existing self-hosted/CI configs are unaffected.
  • Add unit coverage for assertValidEndpointUrl and both makeHttpClient paths, plus subprocess regressions.

Demo/Screenshot for feature changes and bug fixes -

Before (main) — opaque / misleading:

image

After (this branch) — fast, actionable config error:

image

Tests:

image

Code Understanding and AI Usage

Did you use AI assistance (ChatGPT, Claude, Copilot, etc.) to write any part of this code?

  • No, I wrote all the code myself
  • Yes, I used AI assistance (continue below)

If you used AI assistance:

  • I have reviewed every single line of the AI-generated code
  • I can explain the purpose and logic of each function/component I added
  • I have tested edge cases and understand how the code handles them
  • I have modified the AI output to follow this project's coding standards and conventions

Explain your implementation approach:

Problem: a typo in the API endpoint is a config error, but the CLI surfaced it as either a bare Invalid URL (an unhandled new URL() throw → generic exit 1) or, for a missing/wrong scheme, as a fetch failed UNAVAILABLE after burning a retry cycle. Neither tells the user their endpoint is malformed.

Alternatives considered:

  1. Reuse assertNotLocal from target-url.ts. Rejected: that guard rejects localhost / RFC1918 hosts (it's an SSRF guard for the test target), but the API endpoint legitimately points at private/self-hosted/mock backends — reusing it would break self-hosted users and the project's own localhost test backend.
  2. Validate only the --endpoint-url flag in each command's resolveCommonOptions. Rejected: it would miss TESTSPRITE_API_URL and the credentials-file api_url, and duplicate the check across files.
  3. (chosen) One assertValidEndpointUrl at the single chokepoint where the endpoint becomes a base URL (makeHttpClient), validating the resolved value so all three sources are covered in both the real and dry-run paths.

Key function: assertValidEndpointUrl(rawUrl) — parses with new URL(); an unparseable value or a non-http:/https: scheme throws localValidationError('endpoint-url', …, 'field') (the typed VALIDATION_ERROR used elsewhere, exit 5). It is intentionally syntax-only (no host-class checks). In makeHttpClient it runs before the auth check (so a config typo is reported even without a key) and, in dry-run, before the banner.

Edge cases handled/tested: valid http/https including localhost, 127.0.0.1, and internal hostnames pass; "not a url" (unparseable) throws; "localhost:3000" (parses as a bogus scheme) and ftp:///file:// throw; validation happens before any network call, so no retry cycle is wasted.


Checklist before requesting a review

  • I have added proper PR title and linked to the issue
  • I have performed a self-review of my code
  • I can explain the purpose of every function, class, and logic block I added
  • I understand why my changes work and have tested them thoroughly
  • I have considered potential edge cases and how my code handles them
  • If it is a core feature, I have added thorough tests
  • My code follows the project's style guidelines and conventions

Note: Please check Allow edits from maintainers if you would like us to assist in the PR.

…RROR

A malformed API endpoint produced an opaque or misleading failure instead
of a clear config error:

  --endpoint-url "not a url"   -> `Error: Invalid URL` (exit 1, a raw
                                  `new URL()` throw with no guidance)
  --endpoint-url "localhost:3000" (missing scheme, parses as scheme
                                  "localhost:") and "ftp://x" (wrong scheme)
                               -> `fetch failed` / Service unavailable,
                                  emitted only after a full retry+backoff
                                  cycle — looks like a network outage, not a
                                  config typo.

Add `assertValidEndpointUrl` in client-factory.ts and run it in both the
real and dry-run paths of `makeHttpClient`, on the resolved endpoint (so it
covers --endpoint-url, TESTSPRITE_API_URL, and the credentials file). A
malformed value now throws a typed VALIDATION_ERROR (exit 5) with an
actionable message.

Crucially, and unlike the `--target-url` SSRF guard, this does NOT reject
localhost or private hosts — the API endpoint legitimately points at a
self-hosted, local-dev, or mock backend. Only syntactically invalid values
(unparseable, or a non-http(s) scheme) are rejected, so existing
self-hosted/CI configs and the test suite's localhost mock backend are
unaffected.

Adds unit coverage for assertValidEndpointUrl and the two makeHttpClient
paths, plus subprocess regressions.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Malformed --endpoint-url fails with an opaque "Invalid URL" / retried "fetch failed" instead of a clear validation error

1 participant