diff --git a/backend/internal/pkg/xai/oauth.go b/backend/internal/pkg/xai/oauth.go index ea837fd78a..3d32bb963d 100644 --- a/backend/internal/pkg/xai/oauth.go +++ b/backend/internal/pkg/xai/oauth.go @@ -306,7 +306,7 @@ func normalizeKnownBaseURLPath(raw string) (string, error) { if parsed.User != nil { return "", errors.New("base URL must not include userinfo") } - if parsed.RawQuery != "" { + if parsed.ForceQuery || parsed.RawQuery != "" { return "", errors.New("base URL must not include a query") } if parsed.Fragment != "" { diff --git a/backend/internal/pkg/xai/oauth_test.go b/backend/internal/pkg/xai/oauth_test.go index a82a26e299..f91b5d4937 100644 --- a/backend/internal/pkg/xai/oauth_test.go +++ b/backend/internal/pkg/xai/oauth_test.go @@ -166,6 +166,14 @@ func TestValidateBaseURLAllowsPublicThirdPartyGrokAPI(t *testing.T) { require.Error(t, err) } +func TestValidateBaseURLsRejectEmptyQueryDelimiter(t *testing.T) { + _, err := ValidateBaseURL("https://grok.example.test/v1?") + require.Error(t, err) + + _, err = ValidateTrustedBaseURL("https://api.x.ai/v1?") + require.Error(t, err) +} + func TestBuildResponsesURLWithValidatorUsesCallerPolicy(t *testing.T) { validator := func(raw string) (string, error) { return urlvalidator.ValidateURLFormat(raw, true) @@ -192,6 +200,7 @@ func TestBuildResponsesURLWithValidatorRejectsBaseURLComponents(t *testing.T) { }{ {name: "userinfo", raw: "https://user:secret@grok.example.test/v1"}, {name: "query", raw: "https://grok.example.test/v1?token=secret"}, + {name: "empty query delimiter", raw: "https://grok.example.test/v1?"}, {name: "fragment", raw: "https://grok.example.test/v1#secret"}, }