From 9afdc089a02c16810f2000d1d8d1c44a8670dfc0 Mon Sep 17 00:00:00 2001 From: Zixin Zhou Date: Mon, 2 Jun 2025 23:03:05 +0800 Subject: [PATCH 1/2] safe GraphQL client for non-CLI calling scenarios --- pkg/graphql/client/client.go | 43 +++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/pkg/graphql/client/client.go b/pkg/graphql/client/client.go index 29e0910c..e8646f81 100644 --- a/pkg/graphql/client/client.go +++ b/pkg/graphql/client/client.go @@ -29,10 +29,19 @@ import ( "github.com/apache/skywalking-cli/pkg/logger" ) +// newClient creates a new GraphQL client with configuration from context. +// +// CLI has its own defaults, but for calls from other servers, +// we also add validation and certain default values here. func newClient(ctx context.Context) *graphql.Client { options := []graphql.ClientOption{} - insecure := ctx.Value(contextkey.Insecure{}).(bool) + var insecure bool + if val := ctx.Value(contextkey.Insecure{}); val != nil { + if flag, ok := val.(bool); ok { + insecure = flag + } + } if insecure { customTransport := http.DefaultTransport.(*http.Transport).Clone() customTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: insecure} // #nosec G402 @@ -40,7 +49,17 @@ func newClient(ctx context.Context) *graphql.Client { options = append(options, graphql.WithHTTPClient(httpClient)) } - client := graphql.NewClient(ctx.Value(contextkey.BaseURL{}).(string), options...) + var baseURL string + if val := ctx.Value(contextkey.BaseURL{}); val != nil { + if str, ok := val.(string); ok { + baseURL = str + } + } + if baseURL == "" { + baseURL = "http://127.0.0.1:12800/graphql" + } + + client := graphql.NewClient(baseURL, options...) client.Log = func(msg string) { logger.Log.Debugln(msg) } @@ -49,9 +68,23 @@ func newClient(ctx context.Context) *graphql.Client { // ExecuteQuery executes the `request` and parse to the `response`, returning `error` if there is any. func ExecuteQuery(ctx context.Context, request *graphql.Request, response any) error { - username := ctx.Value(contextkey.Username{}).(string) - password := ctx.Value(contextkey.Password{}).(string) - authorization := ctx.Value(contextkey.Authorization{}).(string) + var username, password, authorization string + if val := ctx.Value(contextkey.Username{}); val != nil { + if str, ok := val.(string); ok { + username = str + } + } + if val := ctx.Value(contextkey.Password{}); val != nil { + if str, ok := val.(string); ok { + password = str + } + } + if val := ctx.Value(contextkey.Authorization{}); val != nil { + if str, ok := val.(string); ok { + authorization = str + } + } + if authorization == "" && username != "" && password != "" { authorization = "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password)) } From dc35db7c479ff3d4063d6cdbcde5f154a91f198c Mon Sep 17 00:00:00 2001 From: Zixin Zhou Date: Tue, 3 Jun 2025 23:12:19 +0800 Subject: [PATCH 2/2] polish the code --- pkg/graphql/client/client.go | 49 +++++++++++------------------------- 1 file changed, 14 insertions(+), 35 deletions(-) diff --git a/pkg/graphql/client/client.go b/pkg/graphql/client/client.go index e8646f81..a2ad60a6 100644 --- a/pkg/graphql/client/client.go +++ b/pkg/graphql/client/client.go @@ -30,18 +30,10 @@ import ( ) // newClient creates a new GraphQL client with configuration from context. -// -// CLI has its own defaults, but for calls from other servers, -// we also add validation and certain default values here. func newClient(ctx context.Context) *graphql.Client { options := []graphql.ClientOption{} - var insecure bool - if val := ctx.Value(contextkey.Insecure{}); val != nil { - if flag, ok := val.(bool); ok { - insecure = flag - } - } + insecure := ctx.Value(contextkey.Insecure{}).(bool) if insecure { customTransport := http.DefaultTransport.(*http.Transport).Clone() customTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: insecure} // #nosec G402 @@ -49,16 +41,7 @@ func newClient(ctx context.Context) *graphql.Client { options = append(options, graphql.WithHTTPClient(httpClient)) } - var baseURL string - if val := ctx.Value(contextkey.BaseURL{}); val != nil { - if str, ok := val.(string); ok { - baseURL = str - } - } - if baseURL == "" { - baseURL = "http://127.0.0.1:12800/graphql" - } - + baseURL := getValue(ctx, contextkey.BaseURL{}, "http://127.0.0.1:12800/graphql") client := graphql.NewClient(baseURL, options...) client.Log = func(msg string) { logger.Log.Debugln(msg) @@ -68,22 +51,9 @@ func newClient(ctx context.Context) *graphql.Client { // ExecuteQuery executes the `request` and parse to the `response`, returning `error` if there is any. func ExecuteQuery(ctx context.Context, request *graphql.Request, response any) error { - var username, password, authorization string - if val := ctx.Value(contextkey.Username{}); val != nil { - if str, ok := val.(string); ok { - username = str - } - } - if val := ctx.Value(contextkey.Password{}); val != nil { - if str, ok := val.(string); ok { - password = str - } - } - if val := ctx.Value(contextkey.Authorization{}); val != nil { - if str, ok := val.(string); ok { - authorization = str - } - } + username := getValue(ctx, contextkey.Username{}, "") + password := getValue(ctx, contextkey.Password{}, "") + authorization := getValue(ctx, contextkey.Authorization{}, "") if authorization == "" && username != "" && password != "" { authorization = "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password)) @@ -96,3 +66,12 @@ func ExecuteQuery(ctx context.Context, request *graphql.Request, response any) e err := client.Run(ctx, request, response) return err } + +// getValue safely extracts a value from the context. +func getValue[T any](ctx context.Context, key any, defaultValue T) T { + val := ctx.Value(key) + if v, ok := val.(T); ok { + return v + } + return defaultValue +}