Skip to content

fix(api-proxy): strip all Gemini auth query param variants to prevent API_KEY_INVALID#2182

Merged
lpcox merged 3 commits intomainfrom
copilot/awf-api-proxy-fix-key-injection
Apr 24, 2026
Merged

fix(api-proxy): strip all Gemini auth query param variants to prevent API_KEY_INVALID#2182
lpcox merged 3 commits intomainfrom
copilot/awf-api-proxy-fix-key-injection

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 24, 2026

stripGeminiKeyParam only removed ?key= but the @google/genai SDK also appends ?apiKey= and ?api_key=. When those pass through with the placeholder value, Google sees the placeholder param and ignores the real x-goog-api-key header injected by the proxy, producing API_KEY_INVALID.

Changes

  • stripGeminiKeyParam — now deletes all three auth param variants (key, apiKey, api_key) before forwarding
  • Startup logging — sidecar now logs GEMINI_API_KEY configured (length=N) on start (key present) or a warn entry (key absent), making misconfiguration visible in docker logs awf-api-proxy rather than only at first inference call
  • Tests — six new cases covering each variant individually, with mixed params preserved, and all three variants present simultaneously
// Before: only ?key= was stripped
parsed.searchParams.delete('key');

// After: all known SDK auth param names are stripped
parsed.searchParams.delete('key');
parsed.searchParams.delete('apiKey');
parsed.searchParams.delete('api_key');

Copilot AI changed the title [WIP] Fix Gemini API key injection issue resulting in API_KEY_INVALID fix(api-proxy): strip all Gemini auth query param variants to prevent API_KEY_INVALID Apr 24, 2026
Copilot AI requested a review from lpcox April 24, 2026 01:42
@lpcox lpcox marked this pull request as ready for review April 24, 2026 04:31
@lpcox lpcox requested a review from Mossaka as a code owner April 24, 2026 04:31
Copilot AI review requested due to automatic review settings April 24, 2026 04:31
Copy link
Copy Markdown
Contributor

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 hardens the api-proxy’s Google Gemini forwarding path by stripping additional SDK-injected API-key query parameter variants so the proxy-injected x-goog-api-key header isn’t ignored upstream (avoiding API_KEY_INVALID errors).

Changes:

  • Extend stripGeminiKeyParam() to delete key, apiKey, and api_key query parameters.
  • Add startup logging to make Gemini key configuration (present/absent) visible in container logs.
  • Add new Jest test cases covering the additional query parameter variants and combinations.
Show a summary per file
File Description
containers/api-proxy/server.js Expands Gemini auth query-param stripping and adds Gemini configuration startup logs.
containers/api-proxy/server.test.js Adds test coverage for the new apiKey / api_key stripping behavior and multi-variant cases.

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

Comment on lines 1089 to 1091

logRequest('info', 'server_start', { message: `GEMINI_API_KEY configured (length=${GEMINI_API_KEY.length})` });
geminiServer.listen(10003, '0.0.0.0', () => {
Comment on lines +543 to +566
it('should remove the apiKey= query parameter', () => {
expect(stripGeminiKeyParam('/v1/models/gemini-pro:generateContent?apiKey=placeholder'))
.toBe('/v1/models/gemini-pro:generateContent');
});

it('should remove the api_key= query parameter', () => {
expect(stripGeminiKeyParam('/v1/models/gemini-pro:generateContent?api_key=placeholder'))
.toBe('/v1/models/gemini-pro:generateContent');
});

it('should remove apiKey= while preserving other query parameters', () => {
expect(stripGeminiKeyParam('/v1/models/gemini-pro:generateContent?apiKey=placeholder&alt=json'))
.toBe('/v1/models/gemini-pro:generateContent?alt=json');
});

it('should remove api_key= while preserving other query parameters', () => {
expect(stripGeminiKeyParam('/v1/models/gemini-pro:generateContent?api_key=placeholder&alt=json'))
.toBe('/v1/models/gemini-pro:generateContent?alt=json');
});

it('should remove all auth params when multiple variants are present', () => {
expect(stripGeminiKeyParam('/v1/models/gemini-pro:generateContent?key=foo&apiKey=bar&api_key=baz&alt=json'))
.toBe('/v1/models/gemini-pro:generateContent?alt=json');
});
@github-actions

This comment has been minimized.

@lpcox
Copy link
Copy Markdown
Collaborator

lpcox commented Apr 24, 2026

@copilot address the review feedback #2182 (review)

@github-actions

This comment has been minimized.

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 24, 2026

@copilot address the review feedback #2182 (review)

Done in 06dfc83:

  • Inline comments in both the HTTP and WebSocket Gemini handlers now list all three stripped param variants (?key=, ?apiKey=, ?api_key=)
  • Added the missing 6th test case (should handle path with only api_key= param, leaving no trailing ?), bringing the new cases to exactly 6 (258 total passing)

@github-actions
Copy link
Copy Markdown
Contributor

Smoke Test Results

✅ GitHub MCP: Listed 2 merged PRs
✅ Playwright: GitHub page title confirmed
✅ File Writing: Test file created
✅ Bash Verification: File content validated

Status: PASS

💥 [THE END] — Illustrated by Smoke Claude

@github-actions github-actions Bot mentioned this pull request Apr 24, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Smoke test report
PR titles: "fix: check binary existence for gh-aw install instead of gh aw --version" | "fix(api-proxy): strip all Gemini auth query param variants to prevent API_KEY_INVALID"
GitHub merged PR review: ✅
Safeinputs GH CLI query: ❌
Playwright github.com title check: ✅
Tavily search: ❌
File write + bash cat: ✅
Discussion oracle comment: ✅
AWF build (npm ci && npm run build): ✅
Overall status: FAIL

Warning

⚠️ Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • registry.npmjs.org

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "registry.npmjs.org"

See Network Configuration for more information.

🔮 The oracle has spoken through Smoke Codex

@github-actions
Copy link
Copy Markdown
Contributor

🔥 Smoke Test Results

Test Status
GitHub MCP (list_pull_requests#2200 "feat(api-proxy): add startup API key validation")
GitHub.com connectivity (HTTP 200)
File write/read (smoke-test-copilot-24872448467.txt)

Overall: PASS

PR by @app/copilot-swe-agent · Assignees: @lpcox, @Copilot

📰 BREAKING: Report filed by Smoke Copilot

@github-actions
Copy link
Copy Markdown
Contributor

🔥 Smoke Test: Copilot BYOK (Offline) Mode

Test Result
GitHub MCP (list merged PRs) ✅ PR #2200 "feat(api-proxy): add startup API key validation"
GitHub.com connectivity ⚠️ Pre-step data not expanded (raw template vars)
File write/read ⚠️ Pre-step data not expanded (raw template vars)
BYOK inference (agent → api-proxy → api.githubcopilot.com)

Running in BYOK offline mode (COPILOT_OFFLINE=true) via api-proxy → api.githubcopilot.com.

PR: fix(api-proxy): strip all Gemini auth query param variants to prevent API_KEY_INVALID by @app/copilot-swe-agent, assigned to @lpcox @Copilot.

Overall: PARTIAL — BYOK inference ✅, pre-step data unavailable (template vars not expanded).

🔑 BYOK report filed by Smoke Copilot BYOK

@github-actions
Copy link
Copy Markdown
Contributor

🏗️ Build Test Suite Results

⚠️ ALL CLONES FAILED — Unable to clone any test repositories

All repository clone attempts failed with:

remote: access denied: unrecognized endpoint
fatal: unable to access '(localhost/redacted) The requested URL returned error: 403
Ecosystem Project Build/Install Tests Status
Bun elysia ❌ CLONE_FAILED N/A ❌ FAIL
Bun hono ❌ CLONE_FAILED N/A ❌ FAIL
C++ fmt ❌ CLONE_FAILED N/A ❌ FAIL
C++ json ❌ CLONE_FAILED N/A ❌ FAIL
Deno oak ❌ CLONE_FAILED N/A ❌ FAIL
Deno std ❌ CLONE_FAILED N/A ❌ FAIL
.NET hello-world ❌ CLONE_FAILED N/A ❌ FAIL
.NET json-parse ❌ CLONE_FAILED N/A ❌ FAIL
Go color ❌ CLONE_FAILED N/A ❌ FAIL
Go env ❌ CLONE_FAILED N/A ❌ FAIL
Go uuid ❌ CLONE_FAILED N/A ❌ FAIL
Java gson ❌ CLONE_FAILED N/A ❌ FAIL
Java caffeine ❌ CLONE_FAILED N/A ❌ FAIL
Node.js clsx ❌ CLONE_FAILED N/A ❌ FAIL
Node.js execa ❌ CLONE_FAILED N/A ❌ FAIL
Node.js p-limit ❌ CLONE_FAILED N/A ❌ FAIL
Rust fd ❌ CLONE_FAILED N/A ❌ FAIL
Rust zoxide ❌ CLONE_FAILED N/A ❌ FAIL

Overall: 0/8 ecosystems passed — ❌ FAIL

Error details: The gh CLI proxy ((localhost/redacted)) returned HTTP 403 "access denied: unrecognized endpoint" for all Mossaka/gh-aw-firewall-test-* repositories. This appears to be a firewall/proxy restriction blocking access to repositories outside the github/gh-aw-firewall scope in this sandboxed environment.

Generated by Build Test Suite for issue #2182 · ● 185.7K ·

@github-actions
Copy link
Copy Markdown
Contributor

Smoke Test Results: GitHub Actions Services Connectivity

Check Status Details
Redis PING (host.docker.internal:6379) ❌ Failed redis-cli not installed; raw TCP to 172.17.0.1:6379 timed out
PostgreSQL pg_isready (host.docker.internal:5432) ❌ Failed pg_isready returned "no response" (exit 2); TCP timed out
PostgreSQL SELECT 1 (smoketest db) ❌ Failed Could not connect — port 5432 unreachable

All 3 checks failed. host.docker.internal resolves to 172.17.0.1 but both ports (6379, 5432) are unreachable from the agent environment (connections time out). The service containers may not be running or firewall rules are blocking access.

🔌 Service connectivity validated by Smoke Services

@github-actions
Copy link
Copy Markdown
Contributor

Chroot Version Comparison Results

Runtime Host Version Chroot Version Match?
Python Python 3.12.13 Python 3.12.3
Node.js v24.14.1 v20.20.2
Go go1.22.12 go1.22.12

Overall: ❌ Not all tests passed — Python and Node.js versions differ between host and chroot. The smoke-chroot label was not applied.

Tested by Smoke Chroot

@lpcox lpcox merged commit 9318fdb into main Apr 24, 2026
92 of 109 checks passed
@lpcox lpcox deleted the copilot/awf-api-proxy-fix-key-injection branch April 24, 2026 18:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[awf] API Proxy: Gemini key injection failing — API_KEY_INVALID despite valid key

3 participants