Skip to content

Fix authentication bypass and require SUPABASE_JWT_SECRET#384

Open
vipul674 wants to merge 4 commits into
FireFistisDead:masterfrom
vipul674:fix/bug-342
Open

Fix authentication bypass and require SUPABASE_JWT_SECRET#384
vipul674 wants to merge 4 commits into
FireFistisDead:masterfrom
vipul674:fix/bug-342

Conversation

@vipul674
Copy link
Copy Markdown

@vipul674 vipul674 commented May 30, 2026

Summary

  • Fixed authentication bypass vulnerability in requireSupabaseAuth middleware by rejecting requests when SUPABASE_JWT_SECRET is missing (HTTP 500).
  • Added startup check that throws an error if SUPABASE_JWT_SECRET is not set, preventing server from running with insecure configuration.
  • Updated test suite to include SUPABASE_JWT_SECRET environment variable, ensuring tests pass.

Related issue

Closes #342

Testing

  • I ran the relevant checks locally
  • I verified the app still starts
  • I tested the affected flow end-to-end

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas

Screenshots / recordings

N/A (backend security fix)

Notes

  • This change makes SUPABASE_JWT_SECRET a required environment variable for the Express server. Deployments must set this variable to a strong secret.
  • Existing deployments using Supabase authentication should ensure they have this secret configured.

Security

  • No sensitive data included

Description

Fixed authentication bypass vulnerability where missing SUPABASE_JWT_SECRET allowed any Bearer token to be accepted. Now the middleware returns HTTP 500 when the secret is missing, and the server refuses to start without it.

Testing & Verification

  • Local test suite passed (41/41 tests).
  • Server startup fails with clear error when SUPABASE_JWT_SECRET is missing.
  • Authentication middleware correctly rejects requests with missing secret.

GSSoC 2026 Compliance & Transparency

Summary by CodeRabbit

  • Bug Fixes

    • Startup now validates the authentication secret and fails fast with a clear "server misconfiguration" error if it's missing; authentication middleware continues to reject invalid tokens with proper 401 responses.
  • Tests

    • Added regression and integration tests to ensure startup failure when auth is misconfigured and to verify middleware rejects malformed/invalid tokens and accepts valid ones.

Copilot AI review requested due to automatic review settings May 30, 2026 10:17
@vercel
Copy link
Copy Markdown

vercel Bot commented May 30, 2026

@vipul674 is attempting to deploy a commit to the firefistisdead's projects Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions github-actions Bot added the backend Express or API gateway work label May 30, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 30, 2026

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: 4399b4b1-95c3-4da5-b4bd-aaebff6b6487

📥 Commits

Reviewing files that changed from the base of the PR and between 6f791f1 and 79cad18.

📒 Files selected for processing (1)
  • server.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • server.js

📝 Walkthrough

Walkthrough

Server startup now requires SUPABASE_JWT_SECRET and fails fast if missing. The requireSupabaseAuth middleware always attempts JWT verification, returning 500 for missing configuration and 401 for invalid tokens. Tests save/restore the env var, add a startup regression, and exercise middleware behaviors.

Changes

JWT Secret Enforcement and Verification

Layer / File(s) Summary
Startup JWT Secret Declaration & Fail-fast
server.js
Adds a top-level SUPABASE_JWT_SECRET constant and a main-module startup check that throws if the env var is missing.
JWT Verification Middleware
server.js
Unconditionally imports jsonwebtoken and updates requireSupabaseAuth to return 500 when SUPABASE_JWT_SECRET is absent; otherwise it verifies Bearer JWTs and returns 401 on verification failure.
Test env preservation hooks
server.test.js
Global hooks capture original SUPABASE_JWT_SECRET, set a default test secret when unset, and restore or delete the original value after tests.
Startup regression & auth middleware tests
server.test.js
Adds a spawnSync regression test asserting server startup fails when secret is empty, and an integration suite testing missing header, malformed JWTs, wrong-signature tokens, correctly signed tokens, and runtime-missing-secret misconfiguration.

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Suggested labels:
    level:critical, quality:clean

  • Suggested reviewers:

  • FireFistisDead

  • Poem

"I hopped through code with tiny feet,
Found secrets hiding incomplete.
Now tokens check and startups cry—be sure!
Guards stand tall, tests tidy every door.
A cheerful rabbit hums: secure and pure."

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main changes: fixing an authentication bypass vulnerability and requiring the SUPABASE_JWT_SECRET environment variable.
Description check ✅ Passed The description covers all template sections including summary, related issue, testing checklist, code review checklist, and security notes. All required information is present and complete.
Linked Issues check ✅ Passed The PR successfully addresses all objectives from issue #342: removes hardcoded JWT secret, enforces JWT verification in requireSupabaseAuth, and prevents server startup without SUPABASE_JWT_SECRET configured.
Out of Scope Changes check ✅ Passed All changes are directly related to fixing the authentication bypass and JWT secret management as specified in issue #342. No extraneous modifications detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ 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.

@github-actions github-actions Bot added bug Something isn't working docs Documentation only enhancement New feature or request feature A new feature or improvement fix A targeted fix or cleanup frontend Frontend-related work level:advanced type:security type:testing labels May 30, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Enforces that SUPABASE_JWT_SECRET is configured at startup and required for verifying tokens in the requireSupabaseAuth middleware, closing a gap where requests could pass auth without cryptographic verification.

Changes:

  • Fail-fast at server boot if SUPABASE_JWT_SECRET is not set.
  • Always verify JWTs in requireSupabaseAuth; return 500 if the secret is missing at request time.
  • Update tests to set/restore SUPABASE_JWT_SECRET so the module loads under test.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
server.js Adds startup check for SUPABASE_JWT_SECRET and requires JWT verification in requireSupabaseAuth.
server.test.js Sets and restores SUPABASE_JWT_SECRET env var so tests can load the server module.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread server.js
Comment on lines +729 to +732
if (!secret) {
// Server misconfiguration: SUPABASE_JWT_SECRET must be set.
return res.status(500).json({ error: "Server misconfiguration: missing SUPABASE_JWT_SECRET" });
}
Comment thread server.js Outdated
return res.status(500).json({ error: "Server misconfiguration: missing SUPABASE_JWT_SECRET" });
}

const jwt = require("jsonwebtoken");
Comment thread server.js Outdated
Comment on lines +21 to +24
const SUPABASE_JWT_SECRET = process.env.SUPABASE_JWT_SECRET;
if (!SUPABASE_JWT_SECRET) {
throw new Error("SUPABASE_JWT_SECRET missing in .env – required for /process-from-url authentication");
}
@github-actions github-actions Bot added invalid This doesn't seem right level:intermediate rag-service FastAPI / model service work labels May 30, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
server.test.js (1)

10-15: ⚡ Quick win

Add regression tests for the auth contract, not just env plumbing.

These hooks keep the suite green, but they still don't assert the security behavior this PR is fixing. Please add one test for startup failing without SUPABASE_JWT_SECRET, and one /process-from-url test that rejects a validly signed non-user token (for example role: "anon") as well as a malformed token. Supabase documents separate anon, authenticated, and service_role JWT roles, so that distinction is the regression surface here. (supabase.com)

Also applies to: 31-35

🤖 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 `@server.test.js` around lines 10 - 15, Add regression tests to server.test.js:
add an "it" that ensures server startup fails (or returns error) when
process.env.SUPABASE_JWT_SECRET is unset by clearing SUPABASE_JWT_SECRET (using
originalSupabaseJwtSecret) and attempting the startup path used in tests
(require/startServer or the same initialization used by existing tests),
asserting it throws or exits; and add a "/process-from-url" test that POSTs to
the /process-from-url handler using a JWT signed with SUPABASE_JWT_SECRET but
with payload role: "anon" (and another request with a malformed token) and
assert the endpoint rejects both (HTTP 401/403 or error response) to confirm
non-user and malformed tokens are rejected. Use the same test helpers and
teardown in the file and restore process.env.SUPABASE_JWT_SECRET after each
test.
server.js (1)

21-24: ⚡ Quick win

Fail fast on startup, not on import.

This guard runs during require("./server.js"), so anything that imports app now hard-fails before it can configure test/runtime env. Moving the throw into the require.main === module startup path preserves the security guarantee without breaking non-startup consumers.

♻️ Proposed refactor
-const SUPABASE_JWT_SECRET = process.env.SUPABASE_JWT_SECRET;
-if (!SUPABASE_JWT_SECRET) {
-  throw new Error("SUPABASE_JWT_SECRET missing in .env – required for /process-from-url authentication");
-}
+const getSupabaseJwtSecret = () => (process.env.SUPABASE_JWT_SECRET || "").trim();
 if (require.main === module) {
   requireInternalRagToken();
+  if (!getSupabaseJwtSecret()) {
+    throw new Error("SUPABASE_JWT_SECRET missing in .env – required for /process-from-url authentication");
+  }
 
   (async () => {
🤖 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 `@server.js` around lines 21 - 24, The code currently throws when
SUPABASE_JWT_SECRET is missing at module import time (const SUPABASE_JWT_SECRET
... throw new Error(...)); move this guard into the startup execution path so
imports don't fail: remove the top-level throw and instead perform the check
inside the module’s entry/start block (use if (require.main === module) { if
(!SUPABASE_JWT_SECRET) throw new Error(...) } or add the same check at the start
of the startServer / main function) ensuring the error is still raised on actual
process startup while allowing other code to require the module for tests or
configuration.
🤖 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 `@server.js`:
- Around line 729-738: After verifying the JWT in requireSupabaseAuth, validate
Supabase-specific claims on the decoded token (the object assigned to req.user
from jwt.verify) and reject non-user tokens: ensure req.user.role ===
"authenticated" (and optionally req.user.aud === "authenticated") and return a
401 JSON error if these checks fail; keep the jwt.verify(token, secret) call but
add explicit claim checks to prevent anon or service_role tokens from being
accepted.

---

Nitpick comments:
In `@server.js`:
- Around line 21-24: The code currently throws when SUPABASE_JWT_SECRET is
missing at module import time (const SUPABASE_JWT_SECRET ... throw new
Error(...)); move this guard into the startup execution path so imports don't
fail: remove the top-level throw and instead perform the check inside the
module’s entry/start block (use if (require.main === module) { if
(!SUPABASE_JWT_SECRET) throw new Error(...) } or add the same check at the start
of the startServer / main function) ensuring the error is still raised on actual
process startup while allowing other code to require the module for tests or
configuration.

In `@server.test.js`:
- Around line 10-15: Add regression tests to server.test.js: add an "it" that
ensures server startup fails (or returns error) when
process.env.SUPABASE_JWT_SECRET is unset by clearing SUPABASE_JWT_SECRET (using
originalSupabaseJwtSecret) and attempting the startup path used in tests
(require/startServer or the same initialization used by existing tests),
asserting it throws or exits; and add a "/process-from-url" test that POSTs to
the /process-from-url handler using a JWT signed with SUPABASE_JWT_SECRET but
with payload role: "anon" (and another request with a malformed token) and
assert the endpoint rejects both (HTTP 401/403 or error response) to confirm
non-user and malformed tokens are rejected. Use the same test helpers and
teardown in the file and restore process.env.SUPABASE_JWT_SECRET after each
test.
🪄 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: 960e9956-071d-4b57-a2d9-b7067767c112

📥 Commits

Reviewing files that changed from the base of the PR and between 04e78f5 and 5512eef.

📒 Files selected for processing (2)
  • server.js
  • server.test.js

Comment thread server.js
Comment on lines +729 to +738
if (!secret) {
// Server misconfiguration: SUPABASE_JWT_SECRET must be set.
return res.status(500).json({ error: "Server misconfiguration: missing SUPABASE_JWT_SECRET" });
}

const jwt = require("jsonwebtoken");
try {
req.user = jwt.verify(token, secret);
} catch (err) {
return res.status(401).json({ error: "Invalid token" });
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the exact region around the reported lines
sed -n '700,780p' server.js | nl -ba

# Find all usages of req.user to understand what claims are relied on
rg -n "req\.user" server.js

# Find where token is sourced and what middleware this snippet belongs to
rg -n "const token|token\s*=" server.js
rg -n "Authorization|bearer|req\.headers" server.js

# Show surrounding auth middleware definition (helps confirm expectations)
rg -n "verify|jsonwebtoken|SUPABASE_JWT_SECRET" server.js

Repository: FireFistisDead/pdf-qa-bot

Length of output: 112


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Confirm file exists
ls -la server.js

# Show the exact region around the reported lines with line numbers (no `nl` needed)
sed -n '700,780p' server.js | awk '{printf "%6d %s\n", NR+699, $0}'

# Find all usages of req.user to understand what claims are relied on
rg -n "req\.user" server.js

# Find where token is sourced and what middleware this snippet belongs to
rg -n "(token\s*=|const token|Authorization|bearer|req\.headers)" server.js

# Find where jsonwebtoken is used and where SUPABASE_JWT_SECRET is referenced
rg -n "(jsonwebtoken|jwt\.verify|SUPABASE_JWT_SECRET)" server.js

# Show any claim/role/aud checks nearby (if any)
rg -n "(role|aud|sub|authenticated|service_role|anon)" server.js

Repository: FireFistisDead/pdf-qa-bot

Length of output: 4551


🌐 Web query:

Supabase JWT claims role aud anon service_role authenticated

💡 Result:

In Supabase Auth JWTs, the relevant claims are: 1) role - "anon": anonymous user - "authenticated": authenticated user - "service_role": service role (intended for server-side use) [1] 2) aud (audience) - "authenticated": for authenticated user tokens - "anon": for anonymous user tokens [1] So the typical combinations you’ll see are: - Anonymous access: role = "anon" and aud = "anon" [1] - Authenticated user access: role = "authenticated" and aud = "authenticated" [1] - Service role: role = "service_role" (service-role token example shown includes role="service_role"; aud is part of the required JWT claims schema but the key point is the role value) [1] If you’re decoding/inspecting JWTs: Supabase documents these as standard JWT claims used for authorization with Postgres/RLS via the role claim [2].

Citations:


🌐 Web query:

Supabase JWT signing algorithm HS256 RS256 project JWT secret jwt.verify algorithms

💡 Result:

Supabase supports multiple algorithms for signing and verifying JSON Web Tokens (JWTs), but it strongly recommends using asymmetric algorithms over symmetric ones [1][2]. Asymmetric Algorithms (Recommended) Supabase supports asymmetric signing keys, which provide improved security and performance compared to shared secrets [1][3]. - ES256 (NIST P-256 Curve): This is the recommended algorithm. It is faster than RSA and produces shorter signatures, which is beneficial for reducing data transfer sizes and cookie management [1][4]. - RS256 (RSA 2048): A widely supported public-key algorithm, though generally slower than elliptic curves [1][4]. Symmetric Algorithm (Not Recommended for Production) - HS256 (HMAC with SHA-256): This uses a shared secret [1][5]. Supabase explicitly advises against using HS256 for production applications because there is no public key counterpart, making key revocation more difficult and potentially exposing your project to security risks [1][4][6]. JWT Verification - Preferred Method: For asymmetric keys, Supabase recommends using supabase.auth.getClaims() or high-quality JWT verification libraries [2][3]. These libraries verify the token locally using public keys fetched from your project's JWKS endpoint (/.well-known/jwks.json), avoiding the need to make network calls to the Auth server [2][3][7]. - Legacy/HS256 Verification: If you are still using a shared secret (HS256), the recommended approach is to verify the token by sending a request to the Auth server (e.g., via getUser()), rather than verifying it locally with the secret [2][6]. Security Best Practices - Algorithm Pinning: Always pin the expected algorithm in your verification code (e.g., algorithms: ["RS256"]) to prevent algorithm confusion attacks, where an attacker might attempt to force a symmetric signature using an asymmetric public key [8][5]. - Avoid alg: none: Explicitly reject tokens with the none algorithm [5][9]. - Use JWKS: Leverage the public JSON Web Key Set (JWKS) endpoint provided by your project for secure, automated public key discovery when using asymmetric algorithms [2][7].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Search for SUPABASE_JWT_SECRET usage and any documentation/comments about JWT algorithm/role handling
rg -n "SUPABASE_JWT_SECRET|jwt\.verify|jsonwebtoken|role\"|aud\"|service_role|anon\"|authenticated\"" -S .

# Show server.js around token verification (more context)
sed -n '680,760p' server.js

# Locate any config/env/doc mentioning JWT algorithm (HS256/RS256) or Supabase Auth JWT structure
rg -n "HS256|RS256|JWKS|well-known/jwks|getClaims|getUser|supabase\.auth" -S . || true

# List repository top-level files that might contain auth instructions
ls -ლა

Repository: FireFistisDead/pdf-qa-bot

Length of output: 50465


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1,120p' src/middleware/authMiddleware.js
echo "----"
sed -n '1,220p' src/controllers/authController.js
echo "----"
sed -n '1,140p' server.test.js
echo "----"
rg -n "authMiddleware|requireSupabaseAuth|req\.user|jwt\.verify" -S server.js src/middleware src/controllers server.test.js
echo "----"
rg -n "role\"|aud\"|service_role|anon\"|authenticated\"|sub\"" -S server.js src/middleware src/controllers

Repository: FireFistisDead/pdf-qa-bot

Length of output: 8088


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find where /process-from-url is called and what Authorization header is set
rg -n "process-from-url" -S . --glob='*.js' --glob='*.jsx' --glob='*.ts' --glob='*.tsx'

rg -n "SUPABASE_JWT_SECRET|requireSupabaseAuth|Bearer " -S server.js frontend src --glob='*.js' --glob='*.jsx' --glob='*.ts' --glob='*.tsx'

# Locate ragAuthHeaders implementation (to ensure it's not being conflated with Supabase JWT auth)
rg -n "function ragAuthHeaders|ragAuthHeaders" -S server.js src --glob='*.js'

# Check if `req.user` is used anywhere besides assignment
rg -n "req\.user" server.js src --glob='*.js'

Repository: FireFistisDead/pdf-qa-bot

Length of output: 1899


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check for any documentation / config references
rg -n "SUPABASE_JWT_SECRET|process-from-url|requireSupabaseAuth|access_token|role\"|aud\"" -S . --glob='README*' --glob='*.md' --glob='*.js' --glob='*.ts'

# Show the frontend call site with surrounding context (ragService.js)
sed -n '1,120p' frontend/src/services/ragService.js

# Extract any comments around /process-from-url auth expectations
rg -n "process-from-url" -n server.js
sed -n '700,900p' server.js

Repository: FireFistisDead/pdf-qa-bot

Length of output: 12346


Enforce Supabase JWT claim checks in requireSupabaseAuth (reject role=anon / non-user tokens)

requireSupabaseAuth only does jwt.verify(token, secret) and assigns the result to req.user without checking Supabase authorization claims (role / aud). That means an anon (and potentially service_role) JWT can pass this middleware.

After jwt.verify, require user-level claims, e.g. claims.role === "authenticated" (and typically claims.aud === "authenticated"), otherwise return 401. See Supabase JWT fields: https://supabase.com/docs/guides/auth/jwt-fields

🤖 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 `@server.js` around lines 729 - 738, After verifying the JWT in
requireSupabaseAuth, validate Supabase-specific claims on the decoded token (the
object assigned to req.user from jwt.verify) and reject non-user tokens: ensure
req.user.role === "authenticated" (and optionally req.user.aud ===
"authenticated") and return a 401 JSON error if these checks fail; keep the
jwt.verify(token, secret) call but add explicit claim checks to prevent anon or
service_role tokens from being accepted.

@vipul674 vipul674 requested a review from Copilot May 30, 2026 10:58
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

Comment thread server.js
Comment on lines 1061 to +1064
requireInternalRagToken();
if (!SUPABASE_JWT_SECRET) {
throw new Error("SUPABASE_JWT_SECRET missing in .env – required for /process-from-url authentication");
}
Comment thread server.test.js
Comment on lines +782 to +793
test("accepts valid token and proceeds to route handler", async () => {
const token = jwt.sign({ role: "authenticated" }, process.env.SUPABASE_JWT_SECRET);
const res = await fetch(`${baseUrl}/process-from-url`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({ url: "https://example.com/test.pdf" }),
});
assert.notEqual(res.status, 401, "Valid token should not be rejected");
});
Comment thread server.js Outdated
return res.status(500).json({ error: "Server misconfiguration: missing SUPABASE_JWT_SECRET" });
}

const jwt = require("jsonwebtoken");
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
server.test.js (1)

723-794: ⚡ Quick win

Strengthen auth regression coverage in the new middleware suite.

The positive-path test is too permissive (not 401), and the middleware’s explicit 500 misconfiguration branch is still untested. Tightening both will make this security fix less brittle.

Suggested test hardening
   test("accepts valid token and proceeds to route handler", async () => {
     const token = jwt.sign({ role: "authenticated" }, process.env.SUPABASE_JWT_SECRET);
     const res = await fetch(`${baseUrl}/process-from-url`, {
       method: "POST",
       headers: {
         "Content-Type": "application/json",
         Authorization: `Bearer ${token}`,
       },
       body: JSON.stringify({ url: "https://example.com/test.pdf" }),
     });
-    assert.notEqual(res.status, 401, "Valid token should not be rejected");
+    assert.equal(res.status, 403);
+    const data = await res.json();
+    assert.equal(data.error, "URL host is not allowed.");
   });
+
+  test("returns 500 when SUPABASE_JWT_SECRET is missing at request time", async () => {
+    const original = process.env.SUPABASE_JWT_SECRET;
+    delete process.env.SUPABASE_JWT_SECRET;
+    try {
+      const res = await fetch(`${baseUrl}/process-from-url`, {
+        method: "POST",
+        headers: {
+          "Content-Type": "application/json",
+          Authorization: "Bearer placeholder",
+        },
+        body: JSON.stringify({ url: "https://example.com/test.pdf" }),
+      });
+      assert.equal(res.status, 500);
+      const data = await res.json();
+      assert.match(data.error, /missing SUPABASE_JWT_SECRET/i);
+    } finally {
+      if (original === undefined) {
+        delete process.env.SUPABASE_JWT_SECRET;
+      } else {
+        process.env.SUPABASE_JWT_SECRET = original;
+      }
+    }
+  });
🤖 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 `@server.test.js` around lines 723 - 794, The positive-path test in the
requireSupabaseAuth suite is too weak (assert.notEqual(res.status, 401)); update
the "accepts valid token" case to assert the exact successful status (e.g., 200)
and verify expected response content from the /process-from-url route so the
middleware truly allows valid tokens. Also add a new test that simulates the
middleware misconfiguration by temporarily deleting or clearing
process.env.SUPABASE_JWT_SECRET, calling POST /process-from-url, and asserting
the middleware returns the explicit 500 misconfiguration response (status 500
and the middleware's misconfiguration error body) to cover the 500 branch in
requireSupabaseAuth.
🤖 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.

Nitpick comments:
In `@server.test.js`:
- Around line 723-794: The positive-path test in the requireSupabaseAuth suite
is too weak (assert.notEqual(res.status, 401)); update the "accepts valid token"
case to assert the exact successful status (e.g., 200) and verify expected
response content from the /process-from-url route so the middleware truly allows
valid tokens. Also add a new test that simulates the middleware misconfiguration
by temporarily deleting or clearing process.env.SUPABASE_JWT_SECRET, calling
POST /process-from-url, and asserting the middleware returns the explicit 500
misconfiguration response (status 500 and the middleware's misconfiguration
error body) to cover the 500 branch in requireSupabaseAuth.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 26e571b3-3035-4995-bffa-5f86ccecb665

📥 Commits

Reviewing files that changed from the base of the PR and between 5512eef and 0f4a55d.

📒 Files selected for processing (2)
  • server.js
  • server.test.js

@vipul674 vipul674 requested a review from Copilot May 30, 2026 11:17
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

server.test.js:1

  • This assertion only checks that the response is not a 401 with a specific error string. A regression where the middleware rejects valid tokens with a different status (e.g. 500 or 403) or a different message would still pass. Consider asserting positively against the expected downstream handler behavior (e.g. status code or response shape) for a valid token to provide stronger coverage.
const { test, describe, before, after } = require("node:test");

Comment thread server.js Outdated
const RAG_SERVICE_URL = process.env.RAG_SERVICE_URL || "http://localhost:5000";
const getInternalRagToken = () => (process.env.INTERNAL_RAG_TOKEN || "").trim();
const PORT = process.env.PORT || 4000;
const SUPABASE_JWT_SECRET = process.env.SUPABASE_JWT_SECRET;
Comment thread server.test.js
Comment on lines +798 to +802
test("returns 500 when SUPABASE_JWT_SECRET is missing at request time", async () => {
const express = require("express");
const testApp = express();
testApp.use(express.json());
const testMiddleware = (req, res, next) => {
…tured const

- Hoist `require('jsonwebtoken')` to top of file so it loads once
  rather than on every authenticated request (Copilot feedback).
- Capture SUPABASE_JWT_SECRET with .trim() to reject whitespace-only
  values and read from the captured constant in requireSupabaseAuth
  instead of process.env on every request, matching the INTERNAL_RAG_TOKEN
  pattern and avoiding drift if the env var is mutated after module load
  (Copilot feedback).
- No behavior change: all 47 tests pass.
@vipul674
Copy link
Copy Markdown
Author

vipul674 commented Jun 1, 2026

@FireFistisDead Quick update — pushed commit 79cad18 addressing the remaining Copilot review feedback:

  • Hoisted require("jsonwebtoken") to the top of server.js (no per-request module lookup)
  • Capture SUPABASE_JWT_SECRET with .trim() at module load to reject whitespace-only values, and use the captured const in requireSupabaseAuth instead of re-reading process.env per request
  • 47/47 tests pass, no behavior change

The only remaining CI signal is the Vercel preview deploy gate which needs your team to authorize Vercel for the org (one-time setup). Happy to help debug if you want — otherwise the PR is mergeable from a code/CI perspective.

Let me know if you'd like me to also add a quality:clean / quality:exceptional label. 🙏

@vipul674
Copy link
Copy Markdown
Author

vipul674 commented Jun 2, 2026

@FireFistisDead Please review and let me know for any changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend Express or API gateway work bug Something isn't working docs Documentation only enhancement New feature or request feature A new feature or improvement fix A targeted fix or cleanup frontend Frontend-related work invalid This doesn't seem right level:advanced level:intermediate rag-service FastAPI / model service work type:security type:testing

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Security]: Hardcoded JWT secret and authentication bypass vulnerabilities

2 participants