fix: remove hardcoded secrets, restrict CORS, add auth to debug endpoints#423
fix: remove hardcoded secrets, restrict CORS, add auth to debug endpoints#423devin-ai-integration[bot] wants to merge 1 commit intomainfrom
Conversation
…ints Security fixes: - Remove hardcoded Stripe live secret key from lib/stripe.ts and 5 test scripts - Remove hardcoded Mistral/Codestral API keys from 6 files - Remove hardcoded Tavily API keys from 7 files - Remove hardcoded Pusher secret and credentials from lib/pusher.ts - Remove hardcoded OneSignal app ID from lib/onesignal.ts and app/layout.tsx - Replace wildcard CORS (Access-Control-Allow-Origin: *) with configurable origin - Add authentication checks to /api/debug-tools, /api/mcp-test, /api/database/fix-storage - Replace new Function() code injection with safer arithmetic evaluation - Move admin email to environment variable (ADMIN_EMAIL) - Delete 5 test scripts containing hardcoded Stripe live API key All secrets now require proper environment variables to be set. Co-Authored-By: Anye Happiness Ade <anye.happiness@swisslinkedu.com>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
There was a problem hiding this comment.
3 issues found across 22 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="app/api/database/fix-storage/route.ts">
<violation number="1" location="app/api/database/fix-storage/route.ts:16">
P1: This endpoint is labeled admin-level and uses createAdminClient, but the new guard only checks that a user is logged in. Any authenticated user can run the storage repair across all databases. Add an admin access check (e.g., checkAdminAccess or equivalent) before running admin operations.</violation>
</file>
<file name="lib/onesignal.ts">
<violation number="1" location="lib/onesignal.ts:6">
P2: Guard against a missing OneSignal app id before sending the notification. With the new empty-string fallback, requests will send an empty `app_id` and fail at runtime if the env var is not set.</violation>
</file>
<file name="app/layout.tsx">
<violation number="1" location="app/layout.tsx:108">
P2: Guard the OneSignal.init call when NEXT_PUBLIC_ONESIGNAL_APP_ID is missing. Passing an empty appId still initializes the SDK and can trigger runtime errors or fail to register notifications.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| try { | ||
| // Require authentication | ||
| const authSupabase = await createClient(); | ||
| const { data: { user }, error: authError } = await authSupabase.auth.getUser(); |
There was a problem hiding this comment.
P1: This endpoint is labeled admin-level and uses createAdminClient, but the new guard only checks that a user is logged in. Any authenticated user can run the storage repair across all databases. Add an admin access check (e.g., checkAdminAccess or equivalent) before running admin operations.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/api/database/fix-storage/route.ts, line 16:
<comment>This endpoint is labeled admin-level and uses createAdminClient, but the new guard only checks that a user is logged in. Any authenticated user can run the storage repair across all databases. Add an admin access check (e.g., checkAdminAccess or equivalent) before running admin operations.</comment>
<file context>
@@ -1,14 +1,26 @@
try {
+ // Require authentication
+ const authSupabase = await createClient();
+ const { data: { user }, error: authError } = await authSupabase.auth.getUser();
+ if (authError || !user) {
+ return NextResponse.json(
</file context>
|
|
||
| const ONESIGNAL_API_URL = 'https://onesignal.com/api/v1/notifications'; | ||
| const ONESIGNAL_APP_ID = process.env.NEXT_PUBLIC_ONESIGNAL_APP_ID || '074baec0-7042-4faf-a337-674711dd90ad'; | ||
| const ONESIGNAL_APP_ID = process.env.NEXT_PUBLIC_ONESIGNAL_APP_ID || ''; |
There was a problem hiding this comment.
P2: Guard against a missing OneSignal app id before sending the notification. With the new empty-string fallback, requests will send an empty app_id and fail at runtime if the env var is not set.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At lib/onesignal.ts, line 6:
<comment>Guard against a missing OneSignal app id before sending the notification. With the new empty-string fallback, requests will send an empty `app_id` and fail at runtime if the env var is not set.</comment>
<file context>
@@ -3,7 +3,7 @@
const ONESIGNAL_API_URL = 'https://onesignal.com/api/v1/notifications';
-const ONESIGNAL_APP_ID = process.env.NEXT_PUBLIC_ONESIGNAL_APP_ID || '074baec0-7042-4faf-a337-674711dd90ad';
+const ONESIGNAL_APP_ID = process.env.NEXT_PUBLIC_ONESIGNAL_APP_ID || '';
const ONESIGNAL_REST_API_KEY = process.env.ONESIGNAL_REST_API_KEY;
</file context>
| OneSignalDeferred.push(async function(OneSignal) { | ||
| await OneSignal.init({ | ||
| appId: "${process.env.NEXT_PUBLIC_ONESIGNAL_APP_ID || '074baec0-7042-4faf-a337-674711dd90ad'}", | ||
| appId: "${process.env.NEXT_PUBLIC_ONESIGNAL_APP_ID || ''}", |
There was a problem hiding this comment.
P2: Guard the OneSignal.init call when NEXT_PUBLIC_ONESIGNAL_APP_ID is missing. Passing an empty appId still initializes the SDK and can trigger runtime errors or fail to register notifications.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/layout.tsx, line 108:
<comment>Guard the OneSignal.init call when NEXT_PUBLIC_ONESIGNAL_APP_ID is missing. Passing an empty appId still initializes the SDK and can trigger runtime errors or fail to register notifications.</comment>
<file context>
@@ -105,7 +105,7 @@ export default function RootLayout({
OneSignalDeferred.push(async function(OneSignal) {
await OneSignal.init({
- appId: "${process.env.NEXT_PUBLIC_ONESIGNAL_APP_ID || '074baec0-7042-4faf-a337-674711dd90ad'}",
+ appId: "${process.env.NEXT_PUBLIC_ONESIGNAL_APP_ID || ''}",
});
});
</file context>
Summary
Security hardening PR that removes all hardcoded API keys and secrets from source code, restricts overly permissive CORS, and adds authentication to unprotected endpoints.
Secrets removed (22 files changed, 5 scripts deleted):
sk_live_...) fromlib/stripe.ts+ 5 deleted test scriptslib/ai-providers.ts,debug-tools,mcp-test,chat/completions,execute-scheduled-tasks)chat,chat-v2,repo-agent,agent-cloud,execute-scheduled-tasks,web-scraper,specs/route.ts,v1/chat/completions)lib/pusher.tslib/onesignal.tsandapp/layout.tsxmiddleware.tsAll secrets replaced with environment variable lookups falling back to empty strings.
CORS restricted:
Access-Control-Allow-Origin: *→process.env.ALLOWED_ORIGIN || 'https://pipilot.dev'across all middleware paths.Auth added to 3 unprotected endpoints:
/api/debug-tools,/api/mcp-test,/api/database/fix-storage— all now require a valid Supabase session.new Function()replaced in/api/mcp-testcalculator tool with additional input validation.Review & Testing Checklist for Human
TAVILY_API_KEYS(comma-separated),TAVILY_API_KEY(single key, used inv1/chat/completionsandagent-cloud),ADMIN_EMAIL,ALLOWED_ORIGIN. Existing vars that no longer have fallbacks:STRIPE_SECRET_KEY,MISTRAL_API_KEY,CODESTRAL_API_KEY,PUSHER_APP_ID,PUSHER_KEY,PUSHER_SECRET,NEXT_PUBLIC_PUSHER_KEY,NEXT_PUBLIC_ONESIGNAL_APP_ID. If any are missing, the corresponding feature will silently fail or return empty auth headers.ALLOWED_ORIGINaccepts only one origin string. If you need multiple origins (localhost, staging, preview deploys), this will need further work. Verify that local development and Vercel preview deployments still function.ADMIN_EMAILdefaults to''— if env var is not set, the conditionuser.email !== ADMIN_EMAILis always true, so no user gets admin access. ConfirmADMIN_EMAILis set in all environments.mcp-testcalculator still usesFunction()(line 120):Function('"use strict"; return (' + safeExpr + ')')()is functionally identical tonew Function(). The input sanitization is improved but this is still eval-like code execution. Consider whether this endpoint should exist in production at all.Recommended test plan: Deploy to a preview environment with all env vars configured. Verify: (1) chat/AI features still work (Mistral, Codestral, Tavily search), (2) Stripe checkout flow works, (3) Pusher real-time notifications work, (4) admin routes are accessible to the admin email, (5) CORS headers are correct for your frontend domain, (6)
/api/debug-toolsand/api/mcp-testreturn 401 when unauthenticated.Notes
TAVILY_API_KEY(singular, used inv1/chat/completionsandagent-cloud) andTAVILY_API_KEYS(plural/comma-separated, used everywhere else). You'll need to set both.lib/web-scraper.tsstill contains hardcoded anyapi.io scraper keys (lines 52-56) — these were not in scope for this PR but are worth noting.Link to Devin session: https://app.devin.ai/sessions/b48d5abc10c14a50bc2e24643b76e3f6
Requested by: @Hansade2005
Summary by cubic
Removed hardcoded API keys and secrets, restricted CORS to a single allowed origin, and added Supabase auth to debug/admin endpoints. This closes security gaps and moves all credentials to environment variables.
Bug Fixes
process.env.ALLOWED_ORIGINacross middleware and API routes./api/debug-tools,/api/mcp-test, and/api/database/fix-storage.lib/stripe.ts,lib/pusher.ts,lib/onesignal.ts, andlib/ai-providers.ts; moved admin email toADMIN_EMAIL./api/mcp-testwith safer arithmetic parsing.Migration
STRIPE_SECRET_KEY,NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY,MISTRAL_API_KEY,CODESTRAL_API_KEY,TAVILY_API_KEYorTAVILY_API_KEYS,PUSHER_*andNEXT_PUBLIC_PUSHER_KEY,NEXT_PUBLIC_ONESIGNAL_APP_IDandONESIGNAL_REST_API_KEY,ADMIN_EMAIL,ALLOWED_ORIGIN.ALLOWED_ORIGINsupports one origin; add multi-origin handling later if needed.Written for commit ffdb101. Summary will update on new commits.