Skip to content

πŸ›‘οΈ Sentinel: [CRITICAL] Fix SSRF in bulk lookup API#210

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

πŸ›‘οΈ Sentinel: [CRITICAL] Fix SSRF in bulk lookup API#210
aicoder2009 wants to merge 1 commit into
mainfrom
sentinel/fix-ssrf-bulk-lookup-8878284910696671916

Conversation

@aicoder2009

@aicoder2009 aicoder2009 commented Jul 4, 2026

Copy link
Copy Markdown
Owner

🚨 Severity: CRITICAL
πŸ’‘ Vulnerability: Server-Side Request Forgery (SSRF) risk in src/app/api/lookup/bulk/route.ts. The code was using loopback fetch() requests built with request.nextUrl.origin, which depends on the user-controlled Host header.
🎯 Impact: An attacker could manipulate the Host header to route backend internal lookups to an external attacker-controlled domain or arbitrary internal services, potentially leading to unauthorized data exposure, internal service mapping, or other SSRF exploitation.
πŸ”§ Fix: Refactored src/app/api/lookup/bulk/route.ts to directly import the internal Next.js App Router POST handlers (urlPOST, doiPOST, isbnPOST). Construct a minimal synthetic NextRequest with only safe required headers, rather than forwarding or performing network lookups. Test assertions updated to mock the new handler architecture appropriately.
βœ… Verification: Verified by passing all unit tests, confirming pnpm test:run succeeds without breaking. Confirmed npx eslint passes on the changed files. Added findings cleanly into .jules/sentinel.md.


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

Summary by CodeRabbit

  • Bug Fixes
    • Fixed an issue in bulk lookup handling that could rely on request-host-derived network calls.
    • Bulk lookups now use internal routing directly, improving reliability and reducing exposure to malformed host headers.
    • Updated automated checks to reflect the new lookup flow and verify sub-requests are handled correctly.

Removed `fetch` calls dynamically constructed with `request.nextUrl.origin` inside `src/app/api/lookup/bulk/route.ts` which relied on the client-controlled `Host` header. It now securely directly invokes the local API handlers via synthetic NextRequest objects.

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 July 4, 2026 06:44
@vercel

vercel Bot commented Jul 4, 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 Jul 4, 2026 6:45am

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 Jul 4, 2026

Copy link
Copy Markdown

Review Change Stack

πŸ“ Walkthrough

Walkthrough

This PR fixes an SSRF vulnerability in the bulk lookup API by replacing outbound fetch calls to loopback origins with direct invocation of imported URL/DOI/ISBN route handlers via synthetic NextRequest objects. Tests are updated to mock handlers instead of global.fetch, and a security log entry documents the change.

Changes

Fix SSRF in Bulk Lookup API

Layer / File(s) Summary
Direct handler invocation in bulk route
src/app/api/lookup/bulk/route.ts
Imports URL/DOI/ISBN POST handlers, removes origin/baseUrl-based fetch logic, selects handler and typed request body per item, and invokes the handler directly via a synthetic NextRequest instead of an outbound fetch.
Test mocks for direct handler calls
src/app/api/lookup/bulk/route.test.ts
Mocks urlPOST, doiPOST, and isbnPOST and updates routing and batch-behavior tests to stub handler resolutions and assert invocation, replacing prior global.fetch stubbing and URL assertions.
Security changelog entry
.jules/sentinel.md
Adds an entry describing the SSRF risk from host-header-derived loopback fetches and the remediation of invoking internal route handlers directly.

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

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant BulkRoute
  participant UrlPOST
  participant DoiPOST
  participant IsbnPOST

  Client->>BulkRoute: POST /api/lookup/bulk (items)
  loop each item
    BulkRoute->>BulkRoute: detect type, build reqBody, select handler
    alt url
      BulkRoute->>UrlPOST: handler(syntheticReq)
      UrlPOST-->>BulkRoute: NextResponse
    else doi
      BulkRoute->>DoiPOST: handler(syntheticReq)
      DoiPOST-->>BulkRoute: NextResponse
    else isbn
      BulkRoute->>IsbnPOST: handler(syntheticReq)
      IsbnPOST-->>BulkRoute: NextResponse
    end
  end
  BulkRoute-->>Client: aggregated results and summary
Loading

Related Issues: None referenced.

Related PRs: None referenced.

Suggested labels: security, bug

Suggested reviewers: None specified.

Poem

A loopback fetch once roamed too free,
Through headers derived, an SSRF spree.
Now handlers called direct and true,
No hostname trickery slips on through.
The rabbit nods β€” this burrow's safe anew. πŸ‡

πŸš₯ 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 accurately summarizes the main change: fixing an SSRF issue in the bulk lookup API.
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.
✨ 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-ssrf-bulk-lookup-8878284910696671916

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.

@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: 1

🧹 Nitpick comments (1)
src/app/api/lookup/bulk/route.test.ts (1)

65-96: πŸ“ Maintainability & Code Quality | πŸ”΅ Trivial | ⚑ Quick win

Consider asserting the request body forwarded to each handler.

Tests only check toHaveBeenCalled(); they don't verify what was passed to the handler (e.g., that urlPOST actually received { url: ... } rather than some other field). Since the mocked functions ignore their arguments, a bug that sends the wrong JSON field to the correct handler wouldn't be caught here.

As per path instructions, src/app/api/**/*.test.ts should "Write tests for citation formatting and API route logic."

βœ… Example strengthened assertion
     const response = await POST(makeRequest({ items: ['https://example.com'] }));
     const data = await response.json();
     expect(data.results[0].success).toBe(true);
     expect(data.results[0].data.title).toBe('Example Page');
     expect(urlPOST).toHaveBeenCalled();
+    const [syntheticReq] = (urlPOST as ReturnType<typeof vi.fn>).mock.calls[0];
+    expect(await syntheticReq.json()).toEqual({ url: 'https://example.com' });
πŸ€– 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.test.ts` around lines 65 - 96, The bulk lookup
route tests only verify that the correct handler functions are called, but not
that they receive the right request payload. In `POST` coverage for `urlPOST`,
`doiPOST`, and `isbnPOST`, strengthen the assertions to check the forwarded
body/arguments so a wrong field mapping (for example, sending the wrong key to
the right handler) is caught. Use the existing handler mocks in
`src/app/api/lookup/bulk/route.test.ts` to assert the exact payload passed into
each route-specific POST handler.

Source: Path instructions

πŸ€– 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 @.jules/sentinel.md:
- Line 11: The markdown in this sentinel note has a malformed inline code span
because backslashes do not escape backticks inside single-backtick code spans,
which breaks rendering. Update the affected sentence in .jules/sentinel.md to
use a valid inline code span format (for example, a double-backtick fence or
reworded code formatting) so the literal `fetch(...)`/`request.nextUrl.origin`
snippet renders correctly and complies with markdownlint-cli2 MD038.

---

Nitpick comments:
In `@src/app/api/lookup/bulk/route.test.ts`:
- Around line 65-96: The bulk lookup route tests only verify that the correct
handler functions are called, but not that they receive the right request
payload. In `POST` coverage for `urlPOST`, `doiPOST`, and `isbnPOST`, strengthen
the assertions to check the forwarded body/arguments so a wrong field mapping
(for example, sending the wrong key to the right handler) is caught. Use the
existing handler mocks in `src/app/api/lookup/bulk/route.test.ts` to assert the
exact payload passed into each route-specific POST handler.
πŸͺ„ 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 8d986047-191f-4b57-b016-d3fb1fdfc7d4

πŸ“₯ Commits

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

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

Comment thread .jules/sentinel.md
**Prevention:** The output of `marked` (or any markdown parser) must always be wrapped with `DOMPurify.sanitize()` (using `isomorphic-dompurify` for SSR) before being passed to `dangerouslySetInnerHTML`.
## 2025-03-08 - Fixed SSRF in bulk lookup API

**Vulnerability:** Server-Side Request Forgery (SSRF) risk in `src/app/api/lookup/bulk/route.ts` where it was using `fetch(\`\${request.nextUrl.origin}/api/lookup/...\`)`. `request.nextUrl.origin` is dynamically derived from the potentially attacker-controllable `Host` header.

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

Fix malformed code span (breaks rendering).

Backslash doesn't escape backticks inside a single-backtick code span per CommonMark, so this span likely terminates early and renders with stray backticks/backslashes.

πŸ“ Proposed fix using a double-backtick fence
-**Vulnerability:** Server-Side Request Forgery (SSRF) risk in `src/app/api/lookup/bulk/route.ts` where it was using `fetch(\`\${request.nextUrl.origin}/api/lookup/...\`)`. `request.nextUrl.origin` is dynamically derived from the potentially attacker-controllable `Host` header.
+**Vulnerability:** Server-Side Request Forgery (SSRF) risk in `src/app/api/lookup/bulk/route.ts` where it was using ``fetch(`${request.nextUrl.origin}/api/lookup/...`)``. `request.nextUrl.origin` is dynamically derived from the potentially attacker-controllable `Host` header.

Flagged by markdownlint-cli2: "Spaces inside code span elements (MD038, no-space-in-code)".

πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
**Vulnerability:** Server-Side Request Forgery (SSRF) risk in `src/app/api/lookup/bulk/route.ts` where it was using `fetch(\`\${request.nextUrl.origin}/api/lookup/...\`)`. `request.nextUrl.origin` is dynamically derived from the potentially attacker-controllable `Host` header.
**Vulnerability:** Server-Side Request Forgery (SSRF) risk in `src/app/api/lookup/bulk/route.ts` where it was using ``fetch(`${request.nextUrl.origin}/api/lookup/...`)``. `request.nextUrl.origin` is dynamically derived from the potentially attacker-controllable `Host` header.
🧰 Tools
πŸͺ› markdownlint-cli2 (0.22.1)

[warning] 11-11: Spaces inside code span elements

(MD038, no-space-in-code)

πŸ€– 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 @.jules/sentinel.md at line 11, The markdown in this sentinel note has a
malformed inline code span because backslashes do not escape backticks inside
single-backtick code spans, which breaks rendering. Update the affected sentence
in .jules/sentinel.md to use a valid inline code span format (for example, a
double-backtick fence or reworded code formatting) so the literal
`fetch(...)`/`request.nextUrl.origin` snippet renders correctly and complies
with markdownlint-cli2 MD038.

Source: Linters/SAST tools

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