fix(api-proxy): fix Gemini API_KEY_INVALID with credential isolation#1995
fix(api-proxy): fix Gemini API_KEY_INVALID with credential isolation#1995
Conversation
- Add x-goog-api-key to STRIPPED_HEADERS to ensure placeholder is always stripped before the real key is injected - Add stripGeminiKeyParam() to remove ?key= query params from URLs (the @google/genai SDK may append key= in addition to the header) - Apply stripGeminiKeyParam() in both HTTP and WebSocket Gemini handlers - Extend auth_inject debug logging to cover x-goog-api-key - Export shouldStripHeader and stripGeminiKeyParam for unit testing - Add tests for shouldStripHeader and stripGeminiKeyParam Agent-Logs-Url: https://github.com/github/gh-aw-firewall/sessions/e48bf273-4302-49fe-acde-42cbd46c679c
There was a problem hiding this comment.
Pull request overview
This PR updates the API proxy sidecar’s Gemini handling to avoid forwarding invalid/placeholder API keys by stripping Gemini-specific auth inputs before proxying requests upstream.
Changes:
- Add
x-goog-api-keyto the proxy’s stripped header list and exposeshouldStripHeader()for tests. - Strip the
key=query parameter from Gemini HTTP and WebSocket upgrade request URLs viastripGeminiKeyParam(). - Add unit tests covering
x-goog-api-keyheader stripping andkey=query parameter stripping.
Show a summary per file
| File | Description |
|---|---|
| containers/api-proxy/server.js | Strips x-goog-api-key, adds stripGeminiKeyParam(), strips key= for Gemini HTTP/WS, and exports helpers for tests. |
| containers/api-proxy/server.test.js | Adds unit tests for shouldStripHeader() and stripGeminiKeyParam(). |
Copilot's findings
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 2/2 changed files
- Comments generated: 2
|
|
||
| // Log auth header injection for debugging credential-isolation issues | ||
| const injectedKey = injectHeaders['x-api-key'] || injectHeaders['authorization']; | ||
| const injectedKey = injectHeaders['x-api-key'] || injectHeaders['authorization'] || injectHeaders['x-goog-api-key']; |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
✅ Coverage Check PassedOverall Coverage
📁 Per-file Coverage Changes (1 files)
Coverage comparison generated by |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Address PR review feedback: - Make injected-key detection case-insensitive so auth_inject debug logs fire for OpenAI/Copilot (which use capital-A 'Authorization') in addition to Anthropic and Gemini. - Clarify stripGeminiKeyParam guard comments explaining why absolute/protocol-relative URLs are rejected before parsing. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Smoke Test Results
Overall: PASS
|
Smoke Test Results — OpenCode
Overall: PASS
|
🔥 Smoke Test: Copilot Engine — PASS
PR: fix(api-proxy): fix Gemini API_KEY_INVALID with credential isolation
|
|
Smoke test results:
|
Chroot Version Comparison Results
Overall: ❌ Not all tests passed — Python and Node.js versions differ between host and chroot.
|
Smoke Test: GitHub Actions Services Connectivity ✅
All checks passed. (
|
🏗️ Build Test Suite Results
Overall: 8/8 ecosystems passed — ✅ PASS
|
When
--enable-api-proxyis active, the Gemini proxy on port 10003 was forwarding the placeholderx-goog-api-keyheader and/or a?key=query parameter to Google verbatim, causingAPI_KEY_INVALIDerrors even though the real key was being injected.Root causes
x-goog-api-keynot inSTRIPPED_HEADERS— the placeholder sent by the Gemini CLI was being copied into the forwarded headers, then overwritten byObject.assign. If the SDK sent it as a separate header entry or Google read the first occurrence, the placeholder could survive injection.?key=query parameter not stripped — the@google/genaiSDK appends?key=<value>to request URLs in addition to setting the header. The proxy had no mechanism to strip this, so the placeholder was forwarded as-is.Changes
STRIPPED_HEADERS: addsx-goog-api-keyso the placeholder is unconditionally removed before the real key is injectedstripGeminiKeyParam(): new helper that deletes thekeysearch param from Gemini request URLs; applied in both the HTTP and WebSocket upgrade handlers:auth_injectlog event to also capturex-goog-api-keyinjection (parity withx-api-key/authorization)shouldStripHeaderandstripGeminiKeyParamand adds unit tests covering both, including thex-goog-api-keystripping behaviour