diff --git a/.jules/sentinel.md b/.jules/sentinel.md index a5eb1dd..4851448 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -2,3 +2,7 @@ **Vulnerability:** The application was passing unvalidated HTML variables, specifically `citation.formattedHtml`, to React's `dangerouslySetInnerHTML` prop in multiple components (`src/components/wiki/sortable-citation.tsx`, `src/app/cite/page.tsx`, `src/app/share/[code]/page.tsx`). **Learning:** This is a classic pattern for Cross-Site Scripting (XSS). If a citation's contents originated from an untrusted source or were maliciously formatted, an attacker could execute arbitrary scripts in a user's session when the citation is rendered. **Prevention:** Always sanitize any untrusted or dynamic HTML before rendering it in React. In a Next.js (SSR) application, use a library like `isomorphic-dompurify` to safely strip malicious scripts from the HTML payload on both the client and server side without hydration errors. +## 2026-04-26 - [SSRF Protection in Next.js Fetch API] +**Vulnerability:** Found a Server-Side Request Forgery (SSRF) vulnerability in `src/app/api/lookup/url/route.ts`. The endpoint accepts user-provided URLs and calls `fetch()` directly on them without validating the underlying resolved IP address, exposing the app to internal network scanning and cloud metadata access. +**Learning:** When mitigating SSRF via Node.js native `fetch`, directly rewriting the URL to use a resolved IP address to prevent DNS rebinding breaks Server Name Indication (SNI) and TLS validation for HTTPS requests. +**Prevention:** Full DNS rebinding protection while maintaining SNI requires custom HTTP dispatchers/agents like `undici`. Without such libraries, a standard and robust first-line defense is to resolve the URL hostname to an IP and filter out private IPv4/IPv6 networks prior to executing the `fetch()` request, recognizing it leaves a Time-Of-Check to Time-Of-Use (TOCTOU) gap. diff --git a/src/app/api/lookup/url/route.ts b/src/app/api/lookup/url/route.ts index e01d434..bde2f72 100644 --- a/src/app/api/lookup/url/route.ts +++ b/src/app/api/lookup/url/route.ts @@ -9,6 +9,7 @@ */ import { NextRequest, NextResponse } from 'next/server'; +import { lookup } from 'dns/promises'; import type { SourceType } from '@/types'; interface MetadataResult { @@ -77,6 +78,53 @@ export async function POST(request: NextRequest): Promise