Skip to content

Security: Fix 4 vulnerabilities — API key exposure, unauthenticated endpoints, open proxy - #43

Draft
cursor[bot] wants to merge 3 commits into
mainfrom
cursor/vulnerability-hunter-agent-73ff
Draft

Security: Fix 4 vulnerabilities — API key exposure, unauthenticated endpoints, open proxy#43
cursor[bot] wants to merge 3 commits into
mainfrom
cursor/vulnerability-hunter-agent-73ff

Conversation

@cursor

@cursor cursor Bot commented Jun 23, 2026

Copy link
Copy Markdown

Summary

Automated security scan identified 4 vulnerabilities (3 HIGH, 1 MEDIUM) and this PR implements fixes for all of them.

Changes

1. Root .gitignore — add .env exclusion patterns (MEDIUM fix)

Prevents accidental commit of .env.local and other secret-bearing environment files. Previously only the-unit/api/.gitignore had this protection.

2. Gemini API key moved server-side (HIGH fix)

  • Removed define: { 'process.env.GEMINI_API_KEY': ... } from vite.config.ts — the key is no longer inlined into the client JavaScript bundle
  • Added POST /api/analyze endpoint in server.ts that makes Gemini calls server-side
  • Updated App.tsx analyzeEvent() to call the new server endpoint via fetch()

3. /api/trigger-alert protected with shared secret (HIGH fix)

The endpoint now requires an x-alert-secret header matching the ALERT_SECRET environment variable. Unauthenticated requests are rejected with 401.

4. User allowlist for LLM proxy API (HIGH fix)

  • New allowlist.ts middleware that rejects users not in ALLOWED_EMAILS env var
  • Fail-closed: if ALLOWED_EMAILS is empty, all requests are blocked
  • Wired into all /v1/* provider routes (Anthropic, OpenAI, xAI, Gemini)
  • Updated deploy-gcp.sh to pass ALLOWED_EMAILS (defaults to admin email)

Testing

  • npm run lint (tsc --noEmit) passes for silverback-ai-studio
  • npm run build (Vite production build) succeeds — verified bundle no longer contains inlined API key
  • npm run test:boot passes all 6 existing tests for the-unit/api
  • node sites/build.mjs generates all 11 sites successfully

Deployment Notes

After merging, set these environment variables:

  • silverback-ai-studio: ALERT_SECRET (any strong random string) for the trigger-alert endpoint
  • gumption-api (Cloud Run): ALLOWED_EMAILS=bryan@norcalcarbmobile.com (comma-separated list of approved users)
Open in Web View Automation 

cursoragent and others added 3 commits June 23, 2026 09:54
Prevents accidental commit of .env.local and other secret-bearing
environment files from workers/silverback-ai-studio/ and repo root.
The the-unit/api/.gitignore already had this but root did not.

Co-authored-by: CARBComplianceApp <CARBComplianceApp@users.noreply.github.com>
…ndpoint

- Remove Vite 'define' that inlined GEMINI_API_KEY into client bundle
- Add /api/analyze endpoint on Express server (server-side Gemini calls)
- Update App.tsx analyzeEvent() to call server endpoint via fetch
- Protect /api/trigger-alert with x-alert-secret header check

The API key is no longer embedded in browser-accessible JavaScript.
The trigger-alert endpoint now requires a shared secret to prevent
unauthenticated alert injection.

Co-authored-by: CARBComplianceApp <CARBComplianceApp@users.noreply.github.com>
- New allowlist.ts middleware rejects users not in ALLOWED_EMAILS
- Fail-closed: if ALLOWED_EMAILS is empty, all requests are blocked
- Wire enforceAllowlist into all /v1/* provider routes after auth
- Update deploy script to pass ALLOWED_EMAILS env var (defaults to admin)

Prevents abuse by arbitrary Google accounts authenticating via
Firebase and proxying paid LLM API calls (Anthropic, OpenAI, xAI,
Vertex AI) at the project owner's expense.

Co-authored-by: CARBComplianceApp <CARBComplianceApp@users.noreply.github.com>
Comment thread the-unit/api/src/index.ts
app.use("/v1/openai", requireFirebaseAuth, enforceDailyCap, openaiRouter);
app.use("/v1/xai", requireFirebaseAuth, enforceDailyCap, xaiRouter);
app.use("/v1/gemini", requireFirebaseAuth, enforceDailyCap, geminiRouter);
app.use("/v1/anthropic", requireFirebaseAuth, enforceAllowlist, enforceDailyCap, anthropicRouter);
Comment thread the-unit/api/src/index.ts
app.use("/v1/xai", requireFirebaseAuth, enforceDailyCap, xaiRouter);
app.use("/v1/gemini", requireFirebaseAuth, enforceDailyCap, geminiRouter);
app.use("/v1/anthropic", requireFirebaseAuth, enforceAllowlist, enforceDailyCap, anthropicRouter);
app.use("/v1/openai", requireFirebaseAuth, enforceAllowlist, enforceDailyCap, openaiRouter);
Comment thread the-unit/api/src/index.ts
app.use("/v1/gemini", requireFirebaseAuth, enforceDailyCap, geminiRouter);
app.use("/v1/anthropic", requireFirebaseAuth, enforceAllowlist, enforceDailyCap, anthropicRouter);
app.use("/v1/openai", requireFirebaseAuth, enforceAllowlist, enforceDailyCap, openaiRouter);
app.use("/v1/xai", requireFirebaseAuth, enforceAllowlist, enforceDailyCap, xaiRouter);
Comment thread the-unit/api/src/index.ts
app.use("/v1/anthropic", requireFirebaseAuth, enforceAllowlist, enforceDailyCap, anthropicRouter);
app.use("/v1/openai", requireFirebaseAuth, enforceAllowlist, enforceDailyCap, openaiRouter);
app.use("/v1/xai", requireFirebaseAuth, enforceAllowlist, enforceDailyCap, xaiRouter);
app.use("/v1/gemini", requireFirebaseAuth, enforceAllowlist, enforceDailyCap, geminiRouter);
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.

2 participants