From 118f2a7a42f909d0cd928ef3d8025e2971503db1 Mon Sep 17 00:00:00 2001 From: Klesh Wong Date: Tue, 11 Aug 2026 15:31:35 +0800 Subject: [PATCH] refactor(api): rename GetURIStringPointer to ResolveRequestURI and reject absolute relativePath url.URL.ResolveReference silently discards the base URL when the reference is absolute or protocol-relative, which would make the request target the wrong host without any error. Validate relativePath up front and document the resolution semantics on the renamed function. --- backend/helpers/pluginhelper/api/api_client.go | 13 ++++++++++--- backend/plugins/github/token/refresh_api_client.go | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/backend/helpers/pluginhelper/api/api_client.go b/backend/helpers/pluginhelper/api/api_client.go index 7354a29db78..9e731f3fe70 100644 --- a/backend/helpers/pluginhelper/api/api_client.go +++ b/backend/helpers/pluginhelper/api/api_client.go @@ -324,7 +324,7 @@ func (apiClient *ApiClient) Do( body interface{}, headers http.Header, ) (*http.Response, errors.Error) { - uri, err := GetURIStringPointer(apiClient.endpoint, path, query) + uri, err := ResolveRequestURI(apiClient.endpoint, path, query) if err != nil { return nil, errors.Default.Wrap(err, fmt.Sprintf("Unable to construct URI from %s, %s, %s", apiClient.endpoint, path, query)) } @@ -453,8 +453,12 @@ func UnmarshalResponseXML(res *http.Response, v interface{}) errors.Error { return nil } -// GetURIStringPointer FIXME ... -func GetURIStringPointer(baseUrl string, relativePath string, query url.Values) (*string, errors.Error) { +// ResolveRequestURI combines baseUrl, relativePath and query into the absolute URI used +// for an API request. relativePath must be a relative reference (no scheme or host of its +// own): url.URL.ResolveReference resolves an absolute reference by discarding the base +// entirely (RFC 3986 ยง5.3), so an absolute or protocol-relative relativePath would silently +// ignore baseUrl and target whatever host it carries instead. +func ResolveRequestURI(baseUrl string, relativePath string, query url.Values) (*string, errors.Error) { // If the base URL doesn't end with a slash, and has a relative path attached // the values will be removed by the Go package, therefore we need to add a missing slash. AddMissingSlashToURL(&baseUrl) @@ -468,6 +472,9 @@ func GetURIStringPointer(baseUrl string, relativePath string, query url.Values) if err != nil { return nil, errors.Convert(err) } + if u.IsAbs() || u.Host != "" { + return nil, errors.BadInput.New(fmt.Sprintf("relativePath must be a relative path, not an absolute URL: %s", relativePath)) + } if query != nil { queryString := u.Query() for key, values := range query { diff --git a/backend/plugins/github/token/refresh_api_client.go b/backend/plugins/github/token/refresh_api_client.go index 1db6f691712..134ab2f7dd8 100644 --- a/backend/plugins/github/token/refresh_api_client.go +++ b/backend/plugins/github/token/refresh_api_client.go @@ -74,7 +74,7 @@ func (c *refreshApiClient) Post(path string, query url.Values, body interface{}, } func (c *refreshApiClient) do(method, path string, query url.Values, body interface{}, headers http.Header) (*http.Response, errors.Error) { - uri, err := api.GetURIStringPointer(c.endpoint, path, query) + uri, err := api.ResolveRequestURI(c.endpoint, path, query) if err != nil { return nil, err }