Skip to content

[CRITICAL] Fix OpenRouter API key fallback to prevent silent configuration#856

Merged
durdana3105 merged 1 commit into
durdana3105:mainfrom
Puneet04-tech:api/fix
Jun 9, 2026
Merged

[CRITICAL] Fix OpenRouter API key fallback to prevent silent configuration#856
durdana3105 merged 1 commit into
durdana3105:mainfrom
Puneet04-tech:api/fix

Conversation

@Puneet04-tech

@Puneet04-tech Puneet04-tech commented Jun 7, 2026

Copy link
Copy Markdown

Security Fix Implementation Plan

OpenRouter API Key Fallback Vulnerability

Executive Summary

Vulnerability: Critical configuration security issue with silent API key fallback
Severity: Critical
Status: ✅ Implemented
Branch: api/fix
Files Modified: 3 files (68 insertions, 11 deletions)


1. Vulnerability Description

1.1 Issue

The OpenAI client initialization in backend/routers/chatRoutes.js used a fallback to "dummy-key" when the OPENROUTER_API_KEY environment variable was not configured. This silent fallback allowed the application to start with invalid configuration, potentially enabling:

  • Unauthorized API calls to OpenRouter
  • API abuse and quota exhaustion
  • Unexpected behavior in production
  • Silent configuration errors

1.2 Attack Vector

  1. OPENROUTER_API_KEY environment variable is missing or misconfigured in production
  2. Application falls back to "dummy-key" instead of failing explicitly
  3. This could enable:
    • Unauthorized API calls (if dummy-key has any validity)
    • API abuse and quota exhaustion
    • Silent configuration errors that go undetected
    • Potential security bypasses

1.3 Impact

  • Silent Configuration Failures - Production runs with invalid configuration without explicit error
  • Service Disruption - Chat functionality may fail or behave unpredictably
  • API Abuse Risk - If dummy-key has any validity, it could be abused
  • Security Bypass - Potential for authentication bypass in AI API calls
  • Operational Risk - No validation that API key is valid at application startup

2. Solution Approach

2.1 Strategy

Implement defense-in-depth approach with multiple validation layers:

  1. Environment Variable Validation - Require API key in config schemas
  2. Module-Level Validation - Explicit check at module initialization
  3. Runtime Error Handling - Comprehensive error handling for API failures
  4. Response Validation - Validate API response structure
  5. Security Documentation - Clear comments explaining the vulnerability

2.2 Design Principles

  • Fail Fast - Explicit failure instead of silent fallback
  • Defense in Depth - Multiple validation layers
  • Clear Error Messages - Helpful error messages for debugging
  • Backward Compatible - No breaking changes for properly configured systems
  • Observable - Proper error logging and status codes

3. Detailed Changes

3.1 File: backend/utils/env.js

Change 1: Make OPENROUTER_API_KEY Required

Before:

const envSchema = z.object({
  PORT: z.string().default("5000"),
  SUPABASE_URL: z.string().url(),
  SUPABASE_SERVICE_ROLE_KEY: z.string().min(1),
  SUPABASE_JWT_SECRET: z.string().min(1),
  OPENROUTER_API_KEY: z.string().optional(),  // ❌ Optional
  FRONTEND_URL: z.string().url().default("http://localhost:5173"),
  SITE_URL: z.string().url().optional()
});

After:

const envSchema = z.object({
  PORT: z.string().default("5000"),
  SUPABASE_URL: z.string().url(),
  SUPABASE_SERVICE_ROLE_KEY: z.string().min(1),
  SUPABASE_JWT_SECRET: z.string().min(1),
  OPENROUTER_API_KEY: z.string().min(1),  // ✅ Required
  FRONTEND_URL: z.string().url().default("http://localhost:5173"),
  SITE_URL: z.string().url().optional()
});

Changes:

  • Changed from z.string().optional() to z.string().min(1)
  • Server will fail to start if OPENROUTER_API_KEY is not set
  • Validation happens at server startup via validateEnv()

3.2 File: backend/config.js

Change 2: Make OPENROUTER_API_KEY Required

Before:

const envSchema = z.object({
  PORT: z.string().default("5000"),
  MONGO_URI: z.string().optional(),
  MONGODB_URI: z.string().optional(),
  SUPABASE_URL: z.string().url().optional(),
  SUPABASE_SERVICE_ROLE_KEY: z.string().optional(),
  SUPABASE_ANON_KEY: z.string().optional(),
  OPENROUTER_API_KEY: z.string().optional(),  // ❌ Optional
  PASSWORD_RESET_BASE_URL: z.string().url().optional(),
  FRONTEND_URL: z.string().url().optional(),
  CLIENT_URL: z.string().url().optional(),
  EMAIL_USER: z.string().optional(),
  EMAIL_PASS: z.string().optional(),
  SITE_URL: z.string().url().optional(),
});

After:

const envSchema = z.object({
  PORT: z.string().default("5000"),
  MONGO_URI: z.string().optional(),
  MONGODB_URI: z.string().optional(),
  SUPABASE_URL: z.string().url().optional(),
  SUPABASE_SERVICE_ROLE_KEY: z.string().optional(),
  SUPABASE_ANON_KEY: z.string().optional(),
  OPENROUTER_API_KEY: z.string().min(1),  // ✅ Required
  PASSWORD_RESET_BASE_URL: z.string().url().optional(),
  FRONTEND_URL: z.string().url().optional(),
  CLIENT_URL: z.string().url().optional(),
  EMAIL_USER: z.string().optional(),
  EMAIL_PASS: z.string().optional(),
  SITE_URL: z.string().url().optional(),
});

Changes:

  • Changed from z.string().optional() to z.string().min(1)
  • Ensures consistency across both config files
  • Provides dual validation layers

3.3 File: backend/routers/chatRoutes.js

Change 3: Remove Dummy-Key Fallback

Before:

const openrouter = new OpenAI({
  apiKey: process.env.OPENROUTER_API_KEY || "dummy-key",  // ❌ Fallback
  baseURL: "https://openrouter.ai/api/v1",
  defaultHeaders: {
    "HTTP-Referer": process.env.SITE_URL || "http://localhost:8080",
    "X-Title": "Peer Learning AI",
  },
});

After:

/**
 * SECURITY: Validate OPENROUTER_API_KEY at module initialization
 * This prevents the application from starting with invalid configuration.
 * Previously used a fallback to "dummy-key" which could enable silent
 * configuration failures and potential security issues.
 */
const OPENROUTER_API_KEY = process.env.OPENROUTER_API_KEY;

if (!OPENROUTER_API_KEY) {
  console.error("[security] FATAL: OPENROUTER_API_KEY is not configured. Chat functionality will not work.");
  console.error("[security] Please set the OPENROUTER_API_KEY environment variable and restart the server.");
  throw new Error("OPENROUTER_API_KEY is required for chat functionality");
}

/**
 * Initialize OpenAI client with validated API key
 * No fallback values - fails explicitly if configuration is invalid
 */
const openrouter = new OpenAI({
  apiKey: OPENROUTER_API_KEY,  // ✅ No fallback
  baseURL: "https://openrouter.ai/api/v1",
  defaultHeaders: {
    "HTTP-Referer": process.env.SITE_URL || "http://localhost:8080",
    "X-Title": "Peer Learning AI",
  },
});

Changes:

  • Removed || "dummy-key" fallback
  • Added explicit validation before client initialization
  • Added clear error messages for debugging
  • Added security documentation
  • Throws error if API key is missing

Change 4: Add HttpError Import

Before:

import express from "express";
import OpenAI from "openai";
import dotenv from "dotenv";
import { requireAuth } from "../middlewares/requireAuth.js";
import { rateLimiter } from "../middlewares/rateLimiter.js";
import { asyncHandler } from "../utils/asyncHandler.js";
import { validate } from "../middlewares/validate.js";
import { chatSchemas } from "../validation/schemas.js";

After:

import express from "express";
import OpenAI from "openai";
import dotenv from "dotenv";
import { requireAuth } from "../middlewares/requireAuth.js";
import { rateLimiter } from "../middlewares/rateLimiter.js";
import { asyncHandler } from "../utils/asyncHandler.js";
import { validate } from "../middlewares/validate.js";
import { chatSchemas } from "../validation/schemas.js";
import { HttpError } from "../utils/httpError.js";  // ✅ Added

Changes:

  • Added HttpError import for proper error handling
  • Enables consistent error responses across the application

Change 5: Add Comprehensive Error Handling

Before:

router.post("/chat", requireAuth, rateLimiter, validate(chatSchemas.chatCompletion), asyncHandler(async (req, res) => {
  const { model = "openai/gpt-3.5-turbo", max_tokens, temperature = 0.7 } = req.body;

  const safeMaxTokens = Math.min(
    typeof max_tokens === "number" ? max_tokens : MAX_TOKENS_CAP,
    MAX_TOKENS_CAP
  );

  const chatMessages = [{ role: "system", content: SYSTEM_PROMPT }, ...req.body.messages];

  const response = await openrouter.chat.completions.create({
    model,
    messages: chatMessages,
    max_tokens: safeMaxTokens,
    temperature,
  });

  res.json({ reply: response.choices[0].message.content });
}));

After:

/**
 * POST /api/chat
 *
 * Chat endpoint for AI-powered conversations.
 *
 * SECURITY:
 * - Requires authentication (requireAuth middleware)
 * - Rate limited to prevent API abuse
 * - Input validation using Zod schemas
 * - Token limits enforced to prevent cost escalation
 *
 * Error handling:
 * - 503 if AI service is unavailable
 * - 400 for invalid input
 * - 401 for authentication failures
 */
router.post("/chat", requireAuth, rateLimiter, validate(chatSchemas.chatCompletion), asyncHandler(async (req, res) => {
  const { model = "openai/gpt-3.5-turbo", max_tokens, temperature = 0.7 } = req.body;

  // Validate and cap token usage to prevent cost escalation
  const safeMaxTokens = Math.min(
    typeof max_tokens === "number" ? max_tokens : MAX_TOKENS_CAP,
    MAX_TOKENS_CAP
  );

  const chatMessages = [{ role: "system", content: SYSTEM_PROMPT }, ...req.body.messages];

  try {
    const response = await openrouter.chat.completions.create({
      model,
      messages: chatMessages,
      max_tokens: safeMaxTokens,
      temperature,
    });

    // Validate response structure before accessing
    if (!response?.choices?.[0]?.message?.content) {
      throw new HttpError(502, "AI service returned an invalid response format");
    }

    res.json({ reply: response.choices[0].message.content });
  } catch (error) {
    // Handle OpenAI API errors specifically
    if (error?.status === 401) {
      throw new HttpError(503, "AI service authentication failed. Please check API configuration.");
    }
    if (error?.status === 429) {
      throw new HttpError(429, "AI service rate limit exceeded. Please try again later.");
    }
    if (error?.status === 500 || error?.status === 502 || error?.status === 503) {
      throw new HttpError(503, "AI service is temporarily unavailable. Please try again later.");
    }
    // Re-throw other errors to be handled by the error handler middleware
    throw error;
  }
}));

Changes:

  • Added comprehensive JSDoc documentation
  • Wrapped API call in try-catch block
  • Added response structure validation
  • Added specific error handling for:
    • 401: Authentication failures
    • 429: Rate limit exceeded
    • 500/502/503: Service unavailability
  • Added clear error messages for each scenario

4. Security Improvements

4.1 Configuration Validation

  • Mechanism: Zod schema validation at server startup
  • Benefit: Server fails explicitly if API key is missing
  • Performance: Zero runtime overhead (validation at startup)

4.2 Module-Level Validation

  • Mechanism: Explicit check before OpenAI client initialization
  • Benefit: Defense-in-depth with dual validation layers
  • Performance: Negligible (one-time check at module load)

4.3 Error Handling

  • Mechanism: Specific error handling for different API error codes
  • Benefit: Clear error messages for debugging and monitoring
  • Status Codes:
    • 401: Authentication failure
    • 429: Rate limit exceeded
    • 502: Invalid response format
    • 503: Service unavailable

4.4 Response Validation

  • Mechanism: Validate response structure before accessing nested properties
  • Benefit: Prevents crashes on malformed API responses
  • Performance: Minimal (simple null check)

5. Testing Recommendations

5.1 Configuration Tests

// Test missing API key at startup
- unset OPENROUTER_API_KEY
- start server
- Expected: Server fails with clear error message

// Test invalid API key
- set OPENROUTER_API_KEY="invalid"
- start server
- Expected: Server starts but API calls fail with 401

5.2 Integration Tests

// Test valid API key
POST /api/chat
Body: { "messages": [{"role": "user", "content": "Hello"}] }
Expected: 200 OK with AI response

// Test rate limiting
Send 21 requests rapidly
Expected: 429 Too Many Requests

// Test authentication
POST /api/chat (without auth)
Expected: 401 Unauthorized

5.3 Error Handling Tests

// Test 401 error handling
Mock OpenAI API to return 401
Expected: 503 with "authentication failed" message

// Test 429 error handling
Mock OpenAI API to return 429
Expected: 429 with "rate limit exceeded" message

// Test malformed response
Mock OpenAI API to return invalid structure
Expected: 502 with "invalid response format" message

6. Deployment Considerations

6.1 Prerequisites

  • OPENROUTER_API_KEY environment variable must be set
  • Valid OpenRouter API key must be obtained
  • Environment configuration must be updated

6.2 Rollout Strategy

  1. Update environment configuration in staging
  2. Deploy to staging environment
  3. Verify chat functionality works correctly
  4. Monitor error logs for validation failures
  5. Deploy to production during low-traffic period
  6. Monitor for increased error rates

6.3 Monitoring

  • Monitor server startup for configuration errors
  • Monitor 503 error rates on /api/chat
  • Monitor 401 authentication failures
  • Monitor 429 rate limit violations
  • Set up alerts for abnormal error patterns

7. Rollback Plan

7.1 Rollback Procedure

If issues arise after deployment:

git checkout main
git revert <commit-hash>
git push origin main

7.2 Rollback Triggers

  • High rate of legitimate 503 errors
  • Chat functionality completely broken
  • Server fails to start in production
  • Significant increase in configuration errors

8. Future Enhancements

8.1 Additional Security Measures

  • API Key Rotation - Implement automatic key rotation
  • Key Scoping - Use scoped keys with limited permissions
  • Request Signing - Add request signing for additional security
  • Audit Logging - Log all AI API calls for security audit

8.2 Performance Optimizations

  • Response Caching - Cache common responses to reduce API calls
  • Request Batching - Batch multiple requests to reduce overhead
  • Connection Pooling - Reuse HTTP connections for better performance

8.3 Operational Improvements

  • Health Checks - Add health check endpoint for AI service
  • Circuit Breaker - Implement circuit breaker pattern for API failures
  • Fallback Models - Add fallback to alternative AI providers

9. Code Review Checklist

  • Removed dummy-key fallback from OpenAI client
  • Added API key validation in env.js
  • Added API key validation in config.js
  • Added module-level validation in chatRoutes.js
  • Added comprehensive error handling
  • Added response structure validation
  • Added security documentation
  • Added HttpError import
  • Added clear error messages
  • No breaking changes for properly configured systems

10. Summary

This fix addresses a critical configuration security vulnerability by removing the silent fallback to "dummy-key" and implementing multiple layers of validation. The server now fails explicitly with clear error messages if the OPENROUTER_API_KEY is not configured, preventing silent configuration failures, potential API abuse, and unauthorized access. The implementation includes comprehensive error handling, response validation, and security documentation, making the codebase more robust and maintainable.

Fixes : #835

Summary by CodeRabbit

  • Bug Fixes

    • Enhanced error handling for chat API calls with proper HTTP error mapping.
    • Added validation of API responses.
  • Chores

    • Required OpenRouter API Key in configuration validation.
    • Added URL format validation for Site URL configuration.

…ation failures

- Remove dummy-key fallback in chatRoutes.js OpenAI client initialization
- Make OPENROUTER_API_KEY required in backend/utils/env.js validation
- Make OPENROUTER_API_KEY required in backend/config.js validation
- Add explicit API key validation at module initialization in chatRoutes.js
- Add comprehensive error handling for OpenAI API errors (401, 429, 500, 502, 503)
- Add response structure validation to prevent crashes on malformed responses
- Add security documentation explaining the vulnerability and fix
- Import HttpError for proper error handling

This fix prevents the application from starting with invalid AI API configuration,
eliminating the risk of silent configuration failures, potential API abuse,
and unauthorized access. The server now fails explicitly with clear error messages
if OPENROUTER_API_KEY is not configured.
@vercel

vercel Bot commented Jun 7, 2026

Copy link
Copy Markdown

Someone is attempting to deploy a commit to the durdana3105's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jun 7, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 03f806be-ba31-4cb3-a1d3-67a732c9d71e

📥 Commits

Reviewing files that changed from the base of the PR and between b11a714 and ae841ab.

📒 Files selected for processing (3)
  • backend/config.js
  • backend/routers/chatRoutes.js
  • backend/utils/env.js

📝 Walkthrough

Walkthrough

This PR enforces OPENROUTER_API_KEY as a required environment variable, removes the dummy-key fallback from the chat router initialization, and adds comprehensive error handling to the /api/chat endpoint. Environment validation is tightened across config and utils layers, client initialization fails explicitly on missing credentials, and API calls now map upstream errors to appropriate HTTP responses.

Changes

API Key Requirement and Error Handling

Layer / File(s) Summary
Environment schema hardening
backend/config.js, backend/utils/env.js
OPENROUTER_API_KEY changes from optional to required (non-empty), and SITE_URL becomes a URL-validated optional string. Environment validation now fails at startup if the API key is missing or empty.
Chat router client initialization
backend/routers/chatRoutes.js
The HttpError import is added and startup-time validation ensures OPENROUTER_API_KEY is present; the previous "dummy-key" fallback is removed, and the OpenAI client is instantiated only with the validated key.
Chat endpoint error handling
backend/routers/chatRoutes.js
The /api/chat route wraps the OpenRouter call in try/catch, validates the response payload before reading choices[0].message.content, and maps upstream error statuses (401, 429, 5xx variants) to HttpError instances with specific HTTP codes; unexpected errors are re-thrown to the error middleware.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

  • #835: The changes directly address the same code-level problem by removing the "dummy-key" fallback and making OPENROUTER_API_KEY required across config, utils, and router layers.

Suggested labels

type:bug, quality:clean

Poem

🐰 Keys are validated, no more dummies pretend,
Errors caught early, from start to the end,
URLs verified, API calls safe and sound,
A hardened backend, where security is found!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: fixing a critical OpenRouter API key fallback issue by preventing silent configuration failures.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

@Puneet04-tech

Copy link
Copy Markdown
Author

@durdana3105 I have made pr , review it and merge

@durdana3105 durdana3105 merged commit 5e84739 into durdana3105:main Jun 9, 2026
1 of 3 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request Jun 9, 2026
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.

[CRITICAL] OpenRouter API Key Fallback to "dummy-key" in Production Enables Silent Configuration Failures

2 participants