Skip to content

🛡️ Sentinel: [CRITICAL] Fix SSRF vulnerability in bulk lookup#161

Open
aicoder2009 wants to merge 1 commit into
mainfrom
sentinel/fix-bulk-lookup-ssrf-15995001700895303358
Open

🛡️ Sentinel: [CRITICAL] Fix SSRF vulnerability in bulk lookup#161
aicoder2009 wants to merge 1 commit into
mainfrom
sentinel/fix-bulk-lookup-ssrf-15995001700895303358

Conversation

@aicoder2009

@aicoder2009 aicoder2009 commented Jun 12, 2026

Copy link
Copy Markdown
Owner

🚨 Severity: CRITICAL
💡 Vulnerability: The /api/lookup/bulk endpoint used request.nextUrl.origin to determine the base URL for internal API calls via fetch(). Because this origin is derived from the Host header, it could be spoofed by an attacker, leading to Server-Side Request Forgery (SSRF) and allowing the server to proxy requests to arbitrary external or internal network locations.
🎯 Impact: Attackers could abuse the bulk lookup proxy to scan internal networks, access private resources, or bypass external IP-based protections.
🔧 Fix: Replaced network-based internal fetch loopbacks with direct function invocations. The handlers for /api/lookup/url, /api/lookup/doi, and /api/lookup/isbn are now imported and called directly with a synthetically constructed NextRequest.
✅ Verification: pnpm lint and pnpm test:run pass. The tests for src/app/api/lookup/bulk/route.test.ts were updated to mock the internal handlers and confirm the direct invocation logic.


PR created automatically by Jules for task 15995001700895303358 started by @aicoder2009

Summary by CodeRabbit

Bug Fixes

  • Fixed a Server-Side Request Forgery (SSRF) vulnerability in the bulk lookup API endpoint. The implementation has been updated to eliminate this vulnerability. All existing functionality remains intact, and the changes are fully backward compatible with no action required from end users.

Removed internal HTTP `fetch` loopbacks in `/api/lookup/bulk` which derived the origin from the user-controllable `request.nextUrl.origin` (`Host` header). This resolves a Server-Side Request Forgery (SSRF) vulnerability.

The logic has been updated to directly import and invoke the internal Next.js Route Handlers (`urlHandler`, `doiHandler`, `isbnHandler`) using a synthetically constructed `NextRequest` object. Test cases have been appropriately mocked and verified.

Co-authored-by: aicoder2009 <127642633+aicoder2009@users.noreply.github.com>
@google-labs-jules

Copy link
Copy Markdown
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

Copilot AI review requested due to automatic review settings June 12, 2026 06:36
@vercel

vercel Bot commented Jun 12, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
opencitation Ready Ready Preview, Comment Jun 12, 2026 6:37am

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR fixes an SSRF vulnerability in the bulk lookup API route by replacing loopback HTTP fetch (which uses user-controlled Host headers) with direct invocation of internal sub-route handlers via synthetic NextRequest objects. Tests are updated to mock and verify handler calls instead of fetch calls.

Changes

SSRF Mitigation via Direct Handler Invocation

Layer / File(s) Summary
Security vulnerability documentation
.jules/sentinel.md
New sentinel entry documents the SSRF vulnerability caused by loopback fetch using Host-header-derived request.nextUrl.origin and records the fix: direct handler invocation with synthetic NextRequest.
Handler import and per-item routing logic
src/app/api/lookup/bulk/route.ts
Imports POST handlers for URL/DOI/ISBN lookup routes; refactors per-item processing to select the correct handler based on regex-detected input type instead of building an endpoint string.
Direct handler invocation with synthetic NextRequest
src/app/api/lookup/bulk/route.ts
Replaces fetch call with construction of a synthetic NextRequest (including JSON body and Content-Type header) and direct handler invocation; captures and processes the handler response.
Test infrastructure and assertion updates
src/app/api/lookup/bulk/route.test.ts
Replaces global.fetch mocking with vi.mock for sub-route handler modules; updates routing and outcome tests to assert handler calls and verify constructed request URLs contain expected sub-route paths.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A fetch loop once whispered through the wire,
But headers deceived—no Host to admire!
Now handlers called directly, clean and bright,
Synthetic requests patch the loopback blight.
Security dances where trust takes flight! 🌙

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.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 directly addresses the primary change: fixing a critical SSRF vulnerability in the bulk lookup API. It is specific, clear, and accurately summarizes the main security fix.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch sentinel/fix-bulk-lookup-ssrf-15995001700895303358

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed due to a network error.


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

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/app/api/lookup/bulk/route.ts (1)

16-20: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Harden the bulk API contract for non-string items.

src/app/api/lookup/bulk/route.ts validates that items is an array, but not that every element is a string. A payload like { items: [123] } reaches item.trim() and turns a client error into a 500. Please reject non-string elements up front in src/app/api/lookup/bulk/route.ts, and add a regression test in src/app/api/lookup/bulk/route.test.ts to lock that behavior in.

🤖 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/app/api/lookup/bulk/route.ts` around lines 16 - 20, Validate that every
element of the parsed items array is a string before using item.trim(): after
const { items } = body... and the existing falsy/empty checks, add a guard that
checks Array.isArray(items) && items.every(x => typeof x === "string") and
return NextResponse.json({ error: "Items must be an array of strings" }, {
status: 400 }) if it fails; update the handler where item.trim() is called to
assume elements are strings. Then add a regression test in
src/app/api/lookup/bulk/route.test.ts that posts { items: [123] } (and another
mixed-type array) and asserts a 400 response and the expected error message to
lock this behavior.
🤖 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.

Outside diff comments:
In `@src/app/api/lookup/bulk/route.ts`:
- Around line 16-20: Validate that every element of the parsed items array is a
string before using item.trim(): after const { items } = body... and the
existing falsy/empty checks, add a guard that checks Array.isArray(items) &&
items.every(x => typeof x === "string") and return NextResponse.json({ error:
"Items must be an array of strings" }, { status: 400 }) if it fails; update the
handler where item.trim() is called to assume elements are strings. Then add a
regression test in src/app/api/lookup/bulk/route.test.ts that posts { items:
[123] } (and another mixed-type array) and asserts a 400 response and the
expected error message to lock this behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 5504a29f-daff-459f-9395-bedf38ea84e1

📥 Commits

Reviewing files that changed from the base of the PR and between b69285b and 04d5f00.

📒 Files selected for processing (3)
  • .jules/sentinel.md
  • src/app/api/lookup/bulk/route.test.ts
  • src/app/api/lookup/bulk/route.ts

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