Problem
The login flow trusts next verbatim:
src/app/login/login-form.tsx:18,68 — router.push(next) with the raw query param. ?next=https://evil.com navigates the user off-site after sign-in.
src/app/auth/callback/route.ts:16,44 — server side prepends SITE_URL, which contains the obvious cases, but still accepts odd inputs like control characters or /\evil.com.
Suggested fix
Shared helper (e.g. in src/lib/):
function safeNext(raw: string | null): string {
if (!raw) return "/";
if (!raw.startsWith("/") || raw.startsWith("//") || raw.includes("\\")) return "/";
return raw;
}
Use it in both the client form and the auth callback route. Add unit tests.
Acceptance criteria
Problem
The login flow trusts
nextverbatim:src/app/login/login-form.tsx:18,68—router.push(next)with the raw query param.?next=https://evil.comnavigates the user off-site after sign-in.src/app/auth/callback/route.ts:16,44— server side prependsSITE_URL, which contains the obvious cases, but still accepts odd inputs like control characters or/\evil.com.Suggested fix
Shared helper (e.g. in
src/lib/):Use it in both the client form and the auth callback route. Add unit tests.
Acceptance criteria