From 367e77012e7ce74e357baffcfd3deeb1fdec9b44 Mon Sep 17 00:00:00 2001 From: Manuel Vaas Date: Tue, 10 Mar 2026 13:28:05 +0100 Subject: [PATCH 1/5] feat(cli): add validation to endpoint urls relates to STACKITCLI-340 --- internal/pkg/auth/utils.go | 9 +++++++++ internal/pkg/utils/utils.go | 9 +++++++++ internal/pkg/utils/utils_test.go | 10 ++++++++++ 3 files changed, 28 insertions(+) diff --git a/internal/pkg/auth/utils.go b/internal/pkg/auth/utils.go index faf24b687..21b6ce451 100644 --- a/internal/pkg/auth/utils.go +++ b/internal/pkg/auth/utils.go @@ -98,12 +98,21 @@ func parseWellKnownConfiguration(httpClient apiClient, wellKnownConfigURL string if wellKnownConfig.Issuer == "" { return nil, fmt.Errorf("found no issuer") } + if utils.ValidateURLDomain(wellKnownConfig.Issuer) != nil { + return nil, err + } if wellKnownConfig.AuthorizationEndpoint == "" { return nil, fmt.Errorf("found no authorization endpoint") } + if utils.ValidateURLDomain(wellKnownConfig.AuthorizationEndpoint) != nil { + return nil, err + } if wellKnownConfig.TokenEndpoint == "" { return nil, fmt.Errorf("found no token endpoint") } + if utils.ValidateURLDomain(wellKnownConfig.TokenEndpoint) != nil { + return nil, err + } err = SetAuthField(IDP_TOKEN_ENDPOINT, wellKnownConfig.TokenEndpoint) if err != nil { diff --git a/internal/pkg/utils/utils.go b/internal/pkg/utils/utils.go index 0cb50a3d4..594f411d3 100644 --- a/internal/pkg/utils/utils.go +++ b/internal/pkg/utils/utils.go @@ -4,6 +4,7 @@ import ( "encoding/base64" "fmt" "net/url" + "slices" "strings" "time" @@ -82,11 +83,19 @@ func ValidateURLDomain(value string) error { if err != nil { return fmt.Errorf("parse url: %w", err) } + urlHost := urlStruct.Hostname() if urlHost == "" { return fmt.Errorf("bad url") } + allowedSchemes := []string{ + "https", + } + if !slices.Contains(allowedSchemes, urlStruct.Scheme) { + return fmt.Errorf("unsupported protocol: %s", urlStruct.Scheme) + } + allowedUrlDomain := viper.GetString(config.AllowedUrlDomainKey) if !strings.HasSuffix(urlHost, allowedUrlDomain) { diff --git a/internal/pkg/utils/utils_test.go b/internal/pkg/utils/utils_test.go index 0b5a656af..d3f9f24a5 100644 --- a/internal/pkg/utils/utils_test.go +++ b/internal/pkg/utils/utils_test.go @@ -97,6 +97,16 @@ func TestValidateURLDomain(t *testing.T) { input: "", isValid: false, }, + { + name: "invalid protocol", + input: "http://example.stackit.cloud", + isValid: false, + }, + { + name: "no protocol", + input: "example.stackit.cloud", + isValid: false, + }, } for _, tt := range tests { From 1d794158514dd71ced3fe0d45b5a17fdde139f93 Mon Sep 17 00:00:00 2001 From: Manuel Vaas Date: Fri, 17 Apr 2026 15:10:08 +0200 Subject: [PATCH 2/5] Add validation --- internal/pkg/auth/user_login.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/internal/pkg/auth/user_login.go b/internal/pkg/auth/user_login.go index 822b7daff..3a46bb9a4 100644 --- a/internal/pkg/auth/user_login.go +++ b/internal/pkg/auth/user_login.go @@ -340,8 +340,12 @@ func cleanup(server *http.Server) { }() } -func openBrowser(pageUrl string) error { - var err error +func openBrowser(pageUrl string) (err error) { + err = utils.ValidateURLDomain(pageUrl) + if err != nil { + return err + } + switch runtime.GOOS { case "freebsd", "linux": // We need to use the windows way on WSL, otherwise we do not pass query From 7467bc085734acd3a5451866ba3e61746ec3abe1 Mon Sep 17 00:00:00 2001 From: Manuel Vaas Date: Mon, 20 Apr 2026 19:21:18 +0200 Subject: [PATCH 3/5] add error messages for wellknown --- internal/pkg/auth/utils.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/pkg/auth/utils.go b/internal/pkg/auth/utils.go index 21b6ce451..a1e7eaf77 100644 --- a/internal/pkg/auth/utils.go +++ b/internal/pkg/auth/utils.go @@ -99,19 +99,19 @@ func parseWellKnownConfiguration(httpClient apiClient, wellKnownConfigURL string return nil, fmt.Errorf("found no issuer") } if utils.ValidateURLDomain(wellKnownConfig.Issuer) != nil { - return nil, err + return nil, fmt.Errorf("issuer is invalid") } if wellKnownConfig.AuthorizationEndpoint == "" { return nil, fmt.Errorf("found no authorization endpoint") } if utils.ValidateURLDomain(wellKnownConfig.AuthorizationEndpoint) != nil { - return nil, err + return nil, fmt.Errorf("authorization endpoint is invalid") } if wellKnownConfig.TokenEndpoint == "" { return nil, fmt.Errorf("found no token endpoint") } if utils.ValidateURLDomain(wellKnownConfig.TokenEndpoint) != nil { - return nil, err + return nil, fmt.Errorf("token endpoint is invalid") } err = SetAuthField(IDP_TOKEN_ENDPOINT, wellKnownConfig.TokenEndpoint) From 3701d9de5f591d72adc9b417deb350cf4e65ae83 Mon Sep 17 00:00:00 2001 From: Manuel Vaas Date: Mon, 20 Apr 2026 19:34:07 +0200 Subject: [PATCH 4/5] Add new testcase --- internal/pkg/utils/utils_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/internal/pkg/utils/utils_test.go b/internal/pkg/utils/utils_test.go index d3f9f24a5..03b270309 100644 --- a/internal/pkg/utils/utils_test.go +++ b/internal/pkg/utils/utils_test.go @@ -107,6 +107,11 @@ func TestValidateURLDomain(t *testing.T) { input: "example.stackit.cloud", isValid: false, }, + { + name: "valid endpoint", + input: "https://service-account.api.stackit.cloud/token", + isValid: true, + }, } for _, tt := range tests { From 898a2879cf893fe6f674ff1b2d656f267a095a04 Mon Sep 17 00:00:00 2001 From: Manuel Vaas Date: Mon, 20 Apr 2026 19:40:10 +0200 Subject: [PATCH 5/5] fixed well-known test --- internal/pkg/auth/utils_test.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/internal/pkg/auth/utils_test.go b/internal/pkg/auth/utils_test.go index 6fd67fc75..228a2c94c 100644 --- a/internal/pkg/auth/utils_test.go +++ b/internal/pkg/auth/utils_test.go @@ -133,12 +133,12 @@ func TestParseWellKnownConfig(t *testing.T) { { name: "success", getFails: false, - getResponse: `{"issuer":"issuer","authorization_endpoint":"auth","token_endpoint":"token"}`, + getResponse: `{"issuer":"https://issuer.stackit.cloud/endpoint","authorization_endpoint":"https://auth.stackit.cloud/enpoint","token_endpoint":"https://token.stackit.cloud/endpoint"}`, isValid: true, expected: &wellKnownConfig{ - Issuer: "issuer", - AuthorizationEndpoint: "auth", - TokenEndpoint: "token", + Issuer: "https://issuer.stackit.cloud/endpoint", + AuthorizationEndpoint: "https://auth.stackit.cloud/enpoint", + TokenEndpoint: "https://token.stackit.cloud/endpoint", }, }, { @@ -158,21 +158,21 @@ func TestParseWellKnownConfig(t *testing.T) { { name: "missing_issuer", getFails: true, - getResponse: `{"authorization_endpoint":"auth","token_endpoint":"token"}`, + getResponse: `{"authorization_endpoint":"https://auth.stackit.cloud/enpoint","token_endpoint":"https://token.stackit.cloud/endpoint"}`, isValid: false, expected: nil, }, { name: "missing_authorization", getFails: true, - getResponse: `{"issuer":"issuer","token_endpoint":"token"}`, + getResponse: `{"issuer":"https://issuer.stackit.cloud/endpoint","token_endpoint":"https://token.stackit.cloud/endpoint"}`, isValid: false, expected: nil, }, { name: "missing_token", getFails: true, - getResponse: `{"issuer":"issuer","authorization_endpoint":"auth"}`, + getResponse: `{"issuer":"https://issuer.stackit.cloud/endpoint","authorization_endpoint":"https://auth.stackit.cloud/enpoint"}`, isValid: false, expected: nil, },