Skip to content

fix(security): require API keys for Gemini CLI routes#26

Merged
Arron196 merged 3 commits intomainfrom
codex/fix-v1internal-auth-upstream-audit-20260406
Apr 7, 2026
Merged

fix(security): require API keys for Gemini CLI routes#26
Arron196 merged 3 commits intomainfrom
codex/fix-v1internal-auth-upstream-audit-20260406

Conversation

@Arron196
Copy link
Copy Markdown
Owner

@Arron196 Arron196 commented Apr 6, 2026

Summary

  • require access-manager API key auth for /v1internal:* Gemini CLI routes
  • remove the handler-level localhost RemoteAddr gate that breaks authenticated reverse-proxy usage
  • add regression tests covering unauthenticated rejection and authenticated non-local access

Testing

  • go test ./internal/api -run 'TestGeminiCLIRouteRequiresAPIKey|TestGeminiCLIRouteAllowsAuthenticatedNonLocalRequest' -count=1
  • go test ./internal/api ./sdk/api/handlers/... -count=1
  • go test ./...

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR tightens access control around the Gemini CLI-compatible /v1internal:* endpoints by moving enforcement to the server router, removing the handler’s localhost-only restriction so authenticated traffic can traverse reverse proxies.

Changes:

  • Add AuthMiddleware to the /v1internal:method Gemini CLI route.
  • Remove the handler-level RemoteAddr localhost gate in the Gemini CLI handler.
  • Add regression tests for unauthenticated rejection and authenticated non-local access.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
sdk/api/handlers/gemini/gemini-cli_handlers.go Removes localhost-only access logic; updates handler comment to reflect router-level access control.
internal/api/server.go Wraps /v1internal:method with AuthMiddleware(s.accessManager) to require access-manager auth.
internal/api/server_test.go Adds tests asserting /v1internal:* rejects missing API key and allows authenticated non-local requests.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 48 to 53
// CLIHandler handles CLI-specific requests for Gemini API operations.
// It restricts access to localhost only and routes requests to appropriate internal handlers.
// Access control is enforced at the router level so authenticated clients can
// use Gemini CLI-compatible routes through the public proxy.
func (h *GeminiCLIAPIHandler) CLIHandler(c *gin.Context) {
if !strings.HasPrefix(c.Request.RemoteAddr, "127.0.0.1:") {
c.JSON(http.StatusForbidden, handlers.ErrorResponse{
Error: handlers.ErrorDetail{
Message: "CLI reply only allow local access",
Type: "forbidden",
},
})
return
}

rawJSON, _ := c.GetRawData()
requestRawURI := c.Request.URL.Path
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that this handler is reachable through the public proxy, the fallback branch in CLIHandler that proxies to cloudcode-pa.googleapis.com copies all incoming headers to the upstream request. That includes the client’s Authorization header (which in this setup is the proxy API key), causing credential leakage to the upstream Google endpoint. Strip sensitive auth headers (Authorization / X-Api-Key / X-Goog-Api-Key, etc.) before forwarding, or explicitly whitelist safe headers to forward.

Copilot uses AI. Check for mistakes.
})
})
s.engine.POST("/v1internal:method", geminiCLIHandlers.CLIHandler)
s.engine.POST("/v1internal:method", AuthMiddleware(s.accessManager), geminiCLIHandlers.CLIHandler)
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AuthMiddleware() intentionally allows requests when the access manager has no providers configured (legacy behavior). With the localhost-only gate removed from the Gemini CLI handler, this means /v1internal:* becomes publicly reachable whenever API keys aren’t configured, which is a security regression. Consider using a dedicated middleware for /v1internal:* that fails closed when no access providers are configured (or preserves local-only access as a fallback).

Copilot uses AI. Check for mistakes.

if rr.Code == http.StatusUnauthorized || rr.Code == http.StatusForbidden {
t.Fatalf("status = %d, want request to pass auth and localhost gate; body=%s", rr.Code, rr.Body.String())
}
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TestGeminiCLIRouteAllowsAuthenticatedNonLocalRequest only asserts the response is not 401/403, so it would still pass if the route were mis-registered and returned 404 (or if some other unrelated error occurred). Tighten the assertion to prove the request actually reached the intended route/handler (e.g., fail on 404, and/or assert the body is not the auth error).

Suggested change
}
}
if rr.Code == http.StatusNotFound {
t.Fatalf("status = %d, want request to reach the Gemini CLI route; body=%s", rr.Code, rr.Body.String())
}
if strings.Contains(rr.Body.String(), "Missing API key") {
t.Fatalf("body = %q, want request not to fail API key auth", rr.Body.String())
}

Copilot uses AI. Check for mistakes.
Comment on lines 48 to +50
// CLIHandler handles CLI-specific requests for Gemini API operations.
// It restricts access to localhost only and routes requests to appropriate internal handlers.
// Access control is enforced at the router level so authenticated clients can
// use Gemini CLI-compatible routes through the public proxy.
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The package-level comment at the top of this file still says the Gemini CLI handlers “restrict access to localhost only”, but this PR moves access control to router-level API key auth. Update/remove that file header comment to avoid misleading documentation.

Copilot uses AI. Check for mistakes.
@Arron196 Arron196 merged commit ce53a53 into main Apr 7, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants