diff --git a/backend/src/llm-gemini-header.test.ts b/backend/src/llm-gemini-header.test.ts new file mode 100644 index 0000000..5a090b2 --- /dev/null +++ b/backend/src/llm-gemini-header.test.ts @@ -0,0 +1,32 @@ +import { test } from "node:test"; +import assert from "node:assert/strict"; +import { summarizeVideo } from "./llm.js"; + +test("summarizeVideo sends Gemini API key via x-goog-api-key header and not URL query parameter", async () => { + let capturedUrl = ""; + let capturedHeaders: Record = {}; + + const originalFetch = globalThis.fetch; + globalThis.fetch = (async (input: RequestInfo | URL, init?: RequestInit) => { + capturedUrl = input.toString(); + if (init?.headers) { + capturedHeaders = init.headers as Record; + } + return { + ok: true, + json: async () => ({ + candidates: [{ content: { parts: [{ text: JSON.stringify({ summary: "test summary" }) }] } }], + }), + } as Response; + }) as typeof fetch; + + try { + process.env.GEMINI_API_KEY = "test-secret-gemini-key"; + await summarizeVideo({ url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ" }); + + assert.ok(!capturedUrl.includes("key="), "Request URL must not contain key= query parameter"); + assert.equal(capturedHeaders["x-goog-api-key"], "test-secret-gemini-key", "x-goog-api-key header must contain the API key"); + } finally { + globalThis.fetch = originalFetch; + } +}); diff --git a/backend/src/llm.ts b/backend/src/llm.ts index 4974871..e8ab96d 100644 --- a/backend/src/llm.ts +++ b/backend/src/llm.ts @@ -754,11 +754,13 @@ export async function summarizeVideo(input: { }); const endpoint = - `https://generativelanguage.googleapis.com/v1beta/models/${config.gemini.model}:generateContent` + - `?key=${encodeURIComponent(config.gemini.apiKey)}`; + `https://generativelanguage.googleapis.com/v1beta/models/${config.gemini.model}:generateContent`; const res = await fetch(endpoint, { method: "POST", - headers: { "Content-Type": "application/json" }, + headers: { + "Content-Type": "application/json", + "x-goog-api-key": config.gemini.apiKey, + }, body: JSON.stringify({ systemInstruction: { parts: [{ text: VIDEO_SYSTEM }] }, contents: [{ role: "user", parts }],