@@ -45,6 +45,66 @@ type apiKeyGatewaySpec struct {
4545 blockedAPIKeyPrefixes []string
4646}
4747
48+ func (s Server ) rejectMisroutedGatewayCredentials (next http.Handler ) http.Handler {
49+ return http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
50+ if s .requestUsesConfiguredGatewayCredential (r ) {
51+ http .Error (w , "gateway credential used outside its gateway route" , http .StatusBadRequest )
52+ return
53+ }
54+ next .ServeHTTP (w , r )
55+ })
56+ }
57+
58+ func (s Server ) requestUsesConfiguredGatewayCredential (r * http.Request ) bool {
59+ if r == nil {
60+ return false
61+ }
62+ var configured []string
63+ if s .Gemini != nil {
64+ configured = append (configured , strings .TrimSpace (s .Gemini .GatewayToken ))
65+ }
66+ if s .AnthropicGateway != nil {
67+ configured = append (configured , strings .TrimSpace (s .AnthropicGateway .GatewayToken ))
68+ }
69+ if s .OpenAIGateway != nil {
70+ configured = append (configured , strings .TrimSpace (s .OpenAIGateway .GatewayToken ))
71+ }
72+ if s .Bedrock != nil {
73+ configured = append (configured , strings .TrimSpace (s .Bedrock .GatewayToken ))
74+ }
75+ var presented []string
76+ presented = append (presented , r .Header .Values ("X-Goog-Api-Key" )... )
77+ presented = append (presented , r .Header .Values ("X-Api-Key" )... )
78+ for _ , header := range r .Header .Values ("Authorization" ) {
79+ if scheme , value , ok := strings .Cut (strings .TrimSpace (header ), " " ); ok && strings .EqualFold (scheme , "Bearer" ) {
80+ presented = append (presented , strings .TrimSpace (value ))
81+ }
82+ }
83+ for _ , value := range r .Header .Values ("Sec-WebSocket-Protocol" ) {
84+ for _ , protocol := range strings .Split (value , "," ) {
85+ protocol = strings .TrimSpace (protocol )
86+ if strings .HasPrefix (protocol , openAIWebSocketCredentialPrefix ) {
87+ presented = append (presented , strings .TrimPrefix (protocol , openAIWebSocketCredentialPrefix ))
88+ }
89+ }
90+ }
91+ query := r .URL .Query ()
92+ presented = append (presented , query ["key" ]... )
93+ presented = append (presented , query ["api_key" ]... )
94+ for _ , candidate := range presented {
95+ candidate = strings .TrimSpace (candidate )
96+ if candidate == "" {
97+ continue
98+ }
99+ for _ , token := range configured {
100+ if len (candidate ) == len (token ) && token != "" && subtle .ConstantTimeCompare ([]byte (candidate ), []byte (token )) == 1 {
101+ return true
102+ }
103+ }
104+ }
105+ return false
106+ }
107+
48108func (s Server ) apiKeyGatewayHandler (config * APIKeyGatewayConfig , spec apiKeyGatewaySpec ) http.Handler {
49109 return http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
50110 if config == nil || ! config .configured () {
0 commit comments