Skip to content

Fix : Backend security issue for payment - #276

Merged
hrx01-dev merged 1 commit into
mainfrom
fix/backend-securityissue
Jul 11, 2026
Merged

Fix : Backend security issue for payment#276
hrx01-dev merged 1 commit into
mainfrom
fix/backend-securityissue

Conversation

@hrx01-dev

@hrx01-dev hrx01-dev commented Jul 11, 2026

Copy link
Copy Markdown
Owner

description

  • updated api/razorpay.ts to immediately fail and return an error if you're running locally without a FIREBASE_SERVICE_ACCOUNT. This prevents the server from hanging indefinitely.
  • Frontend Timeout: I added a 15-second timeout to the API fetch call in src/dashboard/pages/PaymentManagement.tsx. If the backend ever stalls for more than 15 seconds again, the request will automatically abort and show an error toast so the UI isn't locked up.

Summary by CodeRabbit

  • Bug Fixes
    • Improved Firebase configuration handling for more reliable backend initialization.
    • Added safeguards to prevent payment requests from hanging indefinitely.
    • Payment initiation now shows an error when the request exceeds 15 seconds.

@vercel

vercel Bot commented Jul 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
servio Ready Ready Preview, Comment Jul 11, 2026 1:02pm

@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Firebase Admin initialization now receives an explicit project ID across estimate, quote, and Razorpay APIs, with a local-environment guard for Razorpay. Payment order creation now aborts requests exceeding 15 seconds.

Changes

Payment infrastructure updates

Layer / File(s) Summary
Explicit Firebase Admin initialization
api/estimate.ts, api/quote.ts, api/razorpay.ts
Firebase Admin uses VITE_FIREBASE_PROJECT_ID or FIREBASE_PROJECT_ID; Razorpay skips initialization in unsupported non-production environments.
Payment order request timeout
src/dashboard/pages/PaymentManagement.tsx
The createOrder request uses an AbortController, aborts after 15 seconds, and clears its timeout after completion.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

Suggested labels: Highest priority, UX

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is relevant to the PR’s main backend payment-related fix, though it is a bit broad.
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.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/backend-securityissue

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions

Copy link
Copy Markdown
Contributor

Visit the preview URL for this PR (updated for commit aab5aa5):

https://servio-0--pr276-fix-backend-security-cok0r05q.web.app

(expires Sat, 18 Jul 2026 13:03:48 GMT)

🔥 via Firebase Hosting GitHub Action 🌎

Sign: 15915abb5951eb298a844eda460b24f444d93a69

@hrx01-dev hrx01-dev left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all ci green

@hrx01-dev
hrx01-dev merged commit cf3c3cc into main Jul 11, 2026
9 checks passed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
api/razorpay.ts (1)

22-28: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract initAdmin to a shared module.

The initAdmin function is duplicated across three API files with diverging implementations — different error log messages and only razorpay.ts having the local environment guard. Extract to a shared module to ensure consistent behavior and simplify future maintenance.

  • api/razorpay.ts#L22-L28: Move initAdmin (including the local guard) to a shared Firebase Admin init module and import from there.
  • api/estimate.ts#L28-L30: Replace the local initAdmin copy with an import from the shared module.
  • api/quote.ts#L30-L32: Replace the local initAdmin copy with an import from the shared module.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/razorpay.ts` around lines 22 - 28, The initAdmin implementation is
duplicated and inconsistent across the API handlers. Extract initAdmin,
including the local environment guard, into a shared Firebase Admin
initialization module, then import and use it from api/razorpay.ts lines 22-28,
api/estimate.ts lines 28-30, and api/quote.ts lines 30-32; remove each local
copy so all handlers share the same behavior and error messaging.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@api/razorpay.ts`:
- Around line 22-28: The Firestore initialization in initAdmin() for
api/estimate.ts and api/quote.ts lacks the local credential guard. Add the same
non-production check used by razorpay.ts for missing FIREBASE_SERVICE_ACCOUNT
and GOOGLE_APPLICATION_CREDENTIALS, returning false before initializeApp;
alternatively, reuse a shared helper if one exists.

In `@src/dashboard/pages/PaymentManagement.tsx`:
- Around line 248-261: Update the payment request error handling around the
create-order fetch to detect AbortError and show a user-friendly timeout message
instead of passing the raw abort text to toast.error. Apply the same
AbortController and 15-second timeout pattern to the verifyPayment fetch,
ensuring each timeout is cleared after completion and its AbortError receives
the same timeout-specific handling.

---

Nitpick comments:
In `@api/razorpay.ts`:
- Around line 22-28: The initAdmin implementation is duplicated and inconsistent
across the API handlers. Extract initAdmin, including the local environment
guard, into a shared Firebase Admin initialization module, then import and use
it from api/razorpay.ts lines 22-28, api/estimate.ts lines 28-30, and
api/quote.ts lines 30-32; remove each local copy so all handlers share the same
behavior and error messaging.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7108c240-6119-4053-86e1-de097c580a33

📥 Commits

Reviewing files that changed from the base of the PR and between ee2833e and aab5aa5.

📒 Files selected for processing (4)
  • api/estimate.ts
  • api/quote.ts
  • api/razorpay.ts
  • src/dashboard/pages/PaymentManagement.tsx

Comment thread api/razorpay.ts
Comment on lines +22 to +28
if (process.env.NODE_ENV !== "production" && !process.env.GOOGLE_APPLICATION_CREDENTIALS) {
console.error("Missing FIREBASE_SERVICE_ACCOUNT in local environment. Cannot connect to Firestore.");
return false;
}
initializeApp({
projectId: process.env.VITE_FIREBASE_PROJECT_ID || process.env.FIREBASE_PROJECT_ID,
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if estimate and quote endpoints are called from the frontend in local development
rg -n "api/estimate\|api/quote" src/ --type=ts -g '!**/node_modules/**' -C2

Repository: hrx01-dev/Servio

Length of output: 154


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== repo files ==\n'
git ls-files | rg '(^|/)(api/(razorpay|estimate|quote)\.ts|src/|pages/|app/|routes/|server/)' || true

printf '\n== locate relevant symbols ==\n'
rg -n "initAdmin|initializeApp|GOOGLE_APPLICATION_CREDENTIALS|FIREBASE_SERVICE_ACCOUNT|estimate|quote|razorpay" api src . -g '!**/node_modules/**' || true

printf '\n== file outlines ==\n'
for f in api/razorpay.ts api/estimate.ts api/quote.ts; do
  if [ -f "$f" ]; then
    echo "--- $f ---"
    wc -l "$f"
    sed -n '1,260p' "$f" | cat -n | sed -n '1,260p'
  fi
done

Repository: hrx01-dev/Servio

Length of output: 50372


🏁 Script executed:

#!/bin/bash
set -euo pipefail

for f in api/estimate.ts api/quote.ts api/razorpay.ts src/dashboard/services/estimationService.ts src/app/lib/submitQuote.ts; do
  if [ -f "$f" ]; then
    echo "===== $f ====="
    nl -ba "$f" | sed -n '1,120p'
    echo
  fi
done

Repository: hrx01-dev/Servio

Length of output: 222


🏁 Script executed:

#!/bin/bash
set -euo pipefail

for f in api/estimate.ts api/quote.ts api/razorpay.ts; do
  echo "===== $f ====="
  sed -n '1,120p' "$f" | cat -n
  echo
done

echo "===== api/estimate.ts (around init/use) ====="
sed -n '240,270p' api/estimate.ts | cat -n
echo

echo "===== api/quote.ts (around init/use) ====="
sed -n '55,85p' api/quote.ts | cat -n
echo

echo "===== api/razorpay.ts (around init/use) ====="
sed -n '110,190p' api/razorpay.ts | cat -n

Repository: hrx01-dev/Servio

Length of output: 23392


🌐 Web query:

Firebase Admin SDK initializeApp projectId getFirestore without GOOGLE_APPLICATION_CREDENTIALS local development hang application default credentials

💡 Result:

The Firebase Admin SDK requires valid credentials to communicate with Google Cloud services. When initializing the SDK in a local development environment without the GOOGLE_APPLICATION_CREDENTIALS environment variable or explicit service account credentials, the SDK attempts to use Application Default Credentials (ADC) [1][2]. If ADC are not configured (e.g., via gcloud auth application-default login), the SDK may attempt to reach the Google metadata server (metadata.google.internal) to fetch credentials [3][4]. If the environment is not a Google-managed infrastructure (like Cloud Functions or Compute Engine), this attempt fails because the metadata server is unreachable, leading to errors or long-running requests that may hang while awaiting a response [3][5]. To resolve this during local development, use one of the following approaches: 1. Service Account Key: Download a service account JSON key from your Firebase Project Settings and set the GOOGLE_APPLICATION_CREDENTIALS environment variable to the path of this file [6][2][7]. 2. gcloud CLI: Run gcloud auth application-default login to set up local ADC credentials [8]. Note that for some services like Firebase Authentication, these end-user credentials may have limitations; in such cases, providing a Project ID is required, and sometimes additional configuration is necessary [8]. 3. Emulators: If you are connecting to local Firebase Emulators, ensure you have set the relevant emulator host environment variables (e.g., FIRESTORE_EMULATOR_HOST) [9][10][11]. Even when using emulators, the Admin SDK typically requires a Project ID to be defined, which can be provided via the projectId option in initializeApp or the GCLOUD_PROJECT environment variable [9][10][12]. If your application hangs specifically when calling getFirestore, ensure you have properly configured the SDK with valid credentials or, if working against the emulator, that your environment variables (like FIRESTORE_EMULATOR_HOST) are correctly set before the Firestore client is instantiated [9][10]. Moving client initialization out of the global scope can also help prevent issues where the SDK attempts to initialize before your environment configuration is fully loaded [13].

Citations:


Add the local Firestore guard to api/estimate.ts and api/quote.ts initAdmin() in both handlers still falls back to ADC/projectId-only init, so local requests without FIREBASE_SERVICE_ACCOUNT or GOOGLE_APPLICATION_CREDENTIALS can still stall when they hit Firestore. Mirror the api/razorpay.ts guard here or extract it into a shared helper.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/razorpay.ts` around lines 22 - 28, The Firestore initialization in
initAdmin() for api/estimate.ts and api/quote.ts lacks the local credential
guard. Add the same non-production check used by razorpay.ts for missing
FIREBASE_SERVICE_ACCOUNT and GOOGLE_APPLICATION_CREDENTIALS, returning false
before initializeApp; alternatively, reuse a shared helper if one exists.

Comment on lines +248 to +261
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 15000);

const orderRes = await fetch(`${baseUrl}/api/razorpay?action=createOrder`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
amount: amountToPay,
clientEmail: auth.currentUser.email,
}),
signal: controller.signal,
});
clearTimeout(timeoutId);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Handle AbortError with a user-friendly timeout message.

When the 15-second timeout fires, fetch throws an AbortError with the raw message "The operation was aborted." — the catch block at line 308 passes this directly to toast.error, which is not user-friendly. Check for AbortError and show a timeout-specific message.

Consider also applying the same timeout pattern to the verifyPayment fetch (lines 276-287), which currently has no abort or timeout handling and could hang indefinitely.

🔧 Proposed fix for AbortError handling
     } catch (error: unknown) {
       const err = error as Error;
-      toast.error(err.message || "Failed to initiate payment");
+      if (err.name === "AbortError") {
+        toast.error("Request timed out. Please try again.");
+      } else {
+        toast.error(err.message || "Failed to initiate payment");
+      }
     } finally {
       setIsProcessing(false);
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/dashboard/pages/PaymentManagement.tsx` around lines 248 - 261, Update the
payment request error handling around the create-order fetch to detect
AbortError and show a user-friendly timeout message instead of passing the raw
abort text to toast.error. Apply the same AbortController and 15-second timeout
pattern to the verifyPayment fetch, ensuring each timeout is cleared after
completion and its AbortError receives the same timeout-specific handling.

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.

1 participant