From 90f794bfe2c1dbb5bf3114812678e9e555048d6a Mon Sep 17 00:00:00 2001 From: Wira Basalamah Date: Tue, 21 Apr 2026 13:34:48 +0700 Subject: [PATCH] fix: validate login redirect target by role --- app/auth/login/route.ts | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/app/auth/login/route.ts b/app/auth/login/route.ts index 368c4b8..bf64d42 100644 --- a/app/auth/login/route.ts +++ b/app/auth/login/route.ts @@ -1,6 +1,13 @@ import { NextRequest, NextResponse } from "next/server"; -import { SESSION_COOKIE, UserRole, authenticateUser, getDefaultPathForRole, serializeSession } from "@/lib/auth"; +import { + SESSION_COOKIE, + UserRole, + canAccessPath, + authenticateUser, + getDefaultPathForRole, + serializeSession +} from "@/lib/auth"; import { getRequestAuditContext, writeAuditTrail } from "@/lib/audit"; import { consumeRateLimit, getRateLimitHeaders } from "@/lib/rate-limit"; import { prisma } from "@/lib/prisma"; @@ -15,6 +22,10 @@ function getSafePath(value: string | null) { return null; } + if (value.startsWith("//")) { + return null; + } + return value; } @@ -118,7 +129,11 @@ export async function POST(request: NextRequest) { }); const destination = next ?? getDefaultPathForRole(session.role as UserRole); - const response = NextResponse.redirect(new URL(destination, baseUrl)); + const safeDestination = + destination && canAccessPath(session.role as UserRole, destination) + ? destination + : getDefaultPathForRole(session.role as UserRole); + const response = NextResponse.redirect(new URL(safeDestination, baseUrl)); response.cookies.set(SESSION_COOKIE, await serializeSession(session), { httpOnly: true, sameSite: "lax",