Skip to content

security: VITE_GEMINI_API_KEY is a client-side env variable β€” the Gemini API key is exposed in the browser bundle and can be extracted by any user visiting the siteΒ #111

Description

@divyanshim27

🚨 Security Problem Statement

apps/web/.env documents:
VITE_GEMINI_API_KEY=your-gemini-api-key

The VITE_ prefix in Vite intentionally embeds variables into the client-side JavaScript bundle at build time. This means any user who opens DevTools β†’ Sources on bookcatq.netlify.app can find and extract the Gemini API key in under 60 seconds.

With the key, anyone can:

  • Make unlimited Gemini API calls billed to the BookCAT project's Google account
  • Burn through the free tier quota, breaking mood search, daily quiz, and hot takes for all users
  • Use the key for unrelated purposes

Affected Features

All AI features in BookCAT use this key:

  • Mood-based book search (/discover)
  • Daily quiz generation
  • Hot takes generation
  • Book facts

Proposed Fix

Move all Gemini API calls to Supabase Edge Functions (already deployed for quiz, hot takes, book facts). The frontend calls the Edge Function, which calls Gemini server-side using a secret that never reaches the browser.

1. Remove VITE_GEMINI_API_KEY from apps/web/.env

2. Update Supabase Edge Function secrets:

supabase secrets set GEMINI_API_KEY=your_key_here

3. For mood search (currently calling Gemini from the browser):

Create a new Edge Function apps/web/../supabase/functions/mood-book-search/index.ts:

import { serve } from "https://deno.land/std@0.168.0/http/server.ts";

const GEMINI_API_KEY = Deno.env.get("GEMINI_API_KEY")!;

serve(async (req) => {
  const { mood } = await req.json();
  const response = await fetch(
    `https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent?key=${GEMINI_API_KEY}`,
    {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        contents: [{ parts: [{ text: `Recommend 5 books for someone who feels ${mood}...` }] }],
      }),
    }
  );
  const data = await response.json();
  return new Response(JSON.stringify(data), {
    headers: { "Content-Type": "application/json" },
  });
});

4. Update the frontend Discover page to call supabase.functions.invoke('mood-book-search', { body: { mood } }) instead of calling Gemini directly.

Files to Modify

File Change
apps/web/.env and .env.example Remove VITE_GEMINI_API_KEY
apps/web/src/pages/Discover.jsx Call Edge Function instead of Gemini directly
supabase/functions/mood-book-search/ New Edge Function

πŸ“Š Impact

This is a P0 security issue on the live deployment at bookcatq.netlify.app. The Gemini API key is publicly accessible right now to any visitor. Rotating and moving it to Edge Function secrets costs nothing and permanently closes the exposure.

Suggested labels: security, critical, backend, gssoc:level2

I would like to work on this. Could you please assign it to me?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions