diff --git a/cmd/harbor/root/webhook/create.go b/cmd/harbor/root/webhook/create.go index 5a7371aa8..e6f0deb96 100644 --- a/cmd/harbor/root/webhook/create.go +++ b/cmd/harbor/root/webhook/create.go @@ -69,7 +69,7 @@ or leave them out and be guided through an interactive prompt to input each fiel opts.NotifyType != "" && len(opts.EventType) != 0 && opts.EndpointURL != "" { - err = utils.ValidateURL(opts.EndpointURL) + err = utils.ValidateHTTPURL(opts.EndpointURL) if err != nil { return err } diff --git a/cmd/harbor/root/webhook/edit.go b/cmd/harbor/root/webhook/edit.go index 4ba716006..1057217a0 100644 --- a/cmd/harbor/root/webhook/edit.go +++ b/cmd/harbor/root/webhook/edit.go @@ -85,7 +85,7 @@ or leave them out and use the interactive prompt to select and update a webhook. opts.NotifyType != "" && len(opts.EventType) != 0 && opts.EndpointURL != "" { - if err := utils.ValidateURL(opts.EndpointURL); err != nil { + if err := utils.ValidateHTTPURL(opts.EndpointURL); err != nil { return err } err = api.UpdateWebhook(&opts) diff --git a/pkg/utils/helper.go b/pkg/utils/helper.go index 9255129c7..5504ee132 100644 --- a/pkg/utils/helper.go +++ b/pkg/utils/helper.go @@ -207,6 +207,25 @@ func ValidateURL(rawURL string) error { return nil } +// ValidateHTTPURL checks if the URL is valid and uses the http or https scheme. +func ValidateHTTPURL(rawURL string) error { + if err := ValidateURL(rawURL); err != nil { + return err + } + + parsedURL, err := url.ParseRequestURI(rawURL) + if err != nil { + return fmt.Errorf("invalid URL format: %v", err) + } + + scheme := strings.ToLower(parsedURL.Scheme) + if scheme != "http" && scheme != "https" { + return fmt.Errorf("URL scheme must be http or https") + } + + return nil +} + func PrintFormat[T any](resp T, format string) error { if format == "json" { PrintPayloadInJSONFormat(resp) diff --git a/pkg/utils/helper_test.go b/pkg/utils/helper_test.go index 33c3aba4d..8251e191a 100644 --- a/pkg/utils/helper_test.go +++ b/pkg/utils/helper_test.go @@ -167,6 +167,33 @@ func TestValidateURL(t *testing.T) { } } +func TestValidateHTTPURL(t *testing.T) { + tests := []struct { + name string + input string + wantErr bool + }{ + {"valid https domain", "https://demo.goharbor.io", false}, + {"valid http domain", "http://registry.example.com", false}, + {"valid localhost", "http://localhost:8080", false}, + {"unsupported ftp scheme", "ftp://example.com/webhook", true}, + {"unsupported file scheme", "file://example.com/webhook", true}, + {"missing scheme", "example.com/webhook", true}, + {"invalid host", "https://invalid..domain", true}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + err := utils.ValidateHTTPURL(tc.input) + if tc.wantErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + }) + } +} + func TestPrintFormat(t *testing.T) { // capture stdout var buf bytes.Buffer diff --git a/pkg/views/webhook/create/view.go b/pkg/views/webhook/create/view.go index 0142f47a1..74c395a1d 100644 --- a/pkg/views/webhook/create/view.go +++ b/pkg/views/webhook/create/view.go @@ -83,7 +83,7 @@ func WebhookCreateView(createView *CreateView) error { Title("Endpoint URL"). Value(&createView.EndpointURL). Validate(func(str string) error { - return utils.ValidateURL(str) + return utils.ValidateHTTPURL(str) }), huh.NewInput(). Title("Auth Header"). diff --git a/pkg/views/webhook/edit/view.go b/pkg/views/webhook/edit/view.go index 6c2b5e3d9..61c9b1afb 100644 --- a/pkg/views/webhook/edit/view.go +++ b/pkg/views/webhook/edit/view.go @@ -109,7 +109,7 @@ func WebhookEditView(editView *EditView) { if strings.TrimSpace(str) == "" { return errors.New("endpoint URL cannot be empty") } - if err := utils.ValidateURL(str); err != nil { + if err := utils.ValidateHTTPURL(str); err != nil { return err } return nil