# Tolerate trailing slash in cluster publicUrl

`8fc7b1c`→[main](/content/gh/entireio/cli/commits/main/index.html)·

toothbrush·3w ago·2 files·+8 added/-3 removed

hostFromPublicURL rejected any non-empty path, so a catalog entry like
https://aws-us-east-2.entire.io/ (Path == "/") was dropped in clustersToRegions
— silently removing clusters and risking "no regions available to mirror into"
if the control plane emits trailing slashes. publicUrl is a trusted catalog
field, so accept a bare "/" path; real paths/queries/fragments/userinfo are
still refused. (Trail review)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

## Sessions

32af87a69f30View transcript

[?\\
Interactive Repo Mirror Onboarding WizardClaude Code·1 step](/content/gh/entireio/cli/session/b1f77cad-1b19-4ba8-bc01-a412c9c54e2a#timeline-32af87a69f30/index.html)

## Changes

2

- cmd/entire/cli

- Mrepo_mirror_create_wizard.go+6/-3
  
  - Mrepo_mirror_create_wizard_test.go+2

```
102 unmodified lines

103
104
105
106
107
108
106
107
108
109
110
111
112
113
114

102 unmodified lines

// Reject anything beyond scheme://host[:port]. url.Parse demotes the
	// `host@evil.com` userinfo trick into u.User (leaving u.Host=evil.com) and
	// stashes a trailing path in u.Path, neither of which validateClusterHost
	// would otherwise see. Same belt-and-suspenders the positional <cluster-host>
	// arg gets — the host flows into clone URLs and the STS audience.
	if u.User != nil || u.Path != "" || u.RawQuery != "" || u.Fragment != "" {
	// would otherwise see. A bare "/" path is tolerated: publicUrl is a trusted
	// catalog field, and a trailing slash (https://host/) is benign — rejecting
	// it would silently drop the cluster and could leave the wizard with no
	// regions. Anything richer (a real path, query, fragment, userinfo) is still
	// refused, since the host flows into clone URLs and the STS audience.
	if u.User != nil || (u.Path != "" && u.Path != "/") || u.RawQuery != "" || u.Fragment != "" {
		return "", fmt.Errorf("public_url %q must be scheme://host[:port] only", raw)
	}
	if err := validateClusterHost(u.Host); err != nil {
```

Mcmd/entire/cli/repo_mirror_create_wizard.go+6/-3

```
66 unmodified lines

67
68
69
70
71
72
73
74

66 unmodified lines

{name: "bare host", in: "eu-west-1.entire.io", want: "eu-west-1.entire.io"},
		{name: "host with port", in: "https://localhost:8080", want: "localhost:8080"},
		{name: "trims space", in: "  https://aws-us-east-2.entire.io  ", want: "aws-us-east-2.entire.io"},
		// A trailing slash is a benign catalog shape, not a path injection.
		{name: "trailing slash", in: "https://aws-us-east-2.entire.io/", want: "aws-us-east-2.entire.io"},
		{name: "empty", in: "", wantErr: true},
		// userinfo trick rejected by validateClusterHost
		{name: "userinfo injection", in: "https://aws-us-east-2.entire.io@evil.com", wantErr: true},
```

Mcmd/entire/cli/repo_mirror_create_wizard_test.go+2
