Skip to content

Commit 1585102

Browse files
hh
1 parent 3cac1d0 commit 1585102

2 files changed

Lines changed: 19 additions & 13 deletions

File tree

middleware.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,8 @@ const handleUnauthorized = ( request: NextRequest ) => {
9595
const redirectUrl = new URL( LOGIN_ROUTE, request.url );
9696
const returnPath = collectReturnPath( request );
9797
if ( returnPath ) {
98-
redirectUrl.searchParams.set( "from", returnPath );
98+
const encodedReturnPath = encodeURIComponent( returnPath ).replace( /%2F/gi, "/" );
99+
redirectUrl.search = `?from=${ encodedReturnPath }`;
99100
}
100101

101102
const response = NextResponse.redirect( redirectUrl );

pages/login.js

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { useState } from "react";
33
import Link from "next/link";
44
import { dispatchAuthStateChange } from "@/utils/authState";
55

6-
const DEFAULT_REDIRECT_PATH = "yugioh/my-collection";
6+
const DEFAULT_REDIRECT_PATH = "/yugioh/my-collection";
77
const LOCALHOST_FALLBACK_ORIGIN = "http://localhost:3000";
88

99
const getBaseOrigin = () => {
@@ -22,37 +22,42 @@ const getBaseOrigin = () => {
2222
return LOCALHOST_FALLBACK_ORIGIN;
2323
};
2424

25-
const resolveRedirectPath = ( queryParam ) => {
26-
const baseOrigin = getBaseOrigin();
25+
const BLOCKED_PATHS = new Set( [ "/login", "/register", "/api", "/api/auth/login", "/api/auth/register" ] );
2726

27+
const resolveRedirectPath = ( queryParam ) => {
2828
if ( typeof queryParam !== "string" ) {
2929
return DEFAULT_REDIRECT_PATH;
3030
}
3131

32-
const sanitizedParam = queryParam.trim();
32+
const trimmed = queryParam.trim();
3333

34-
if ( !sanitizedParam || sanitizedParam.startsWith( "/" ) ) {
34+
if ( !trimmed ) {
3535
return DEFAULT_REDIRECT_PATH;
3636
}
3737

38-
if ( sanitizedParam.startsWith( "/" ) ) {
39-
return sanitizedParam.startsWith( "/api" )
40-
? DEFAULT_REDIRECT_PATH
41-
: sanitizedParam;
38+
// Reject protocol-relative, absolute URLs to other origins, and javascript: style values early.
39+
if ( /^([a-zA-Z][a-zA-Z\d+\-.]*:)?\/\//.test( trimmed ) ) {
40+
return DEFAULT_REDIRECT_PATH;
4241
}
4342

43+
const relativePath = trimmed.startsWith( "/" ) ? trimmed : `/${ trimmed }`;
44+
4445
try {
45-
const candidate = new URL( sanitizedParam, baseOrigin );
46+
const baseOrigin = getBaseOrigin();
47+
const candidate = new URL( relativePath, baseOrigin );
4648

4749
if ( candidate.origin !== baseOrigin ) {
4850
return DEFAULT_REDIRECT_PATH;
4951
}
5052

51-
if ( candidate.pathname.startsWith( "/api" ) ) {
53+
const { pathname } = candidate;
54+
55+
if ( BLOCKED_PATHS.has( pathname ) || pathname.startsWith( "/api" ) ) {
5256
return DEFAULT_REDIRECT_PATH;
5357
}
5458

55-
return `${ candidate.pathname }${ candidate.search }${ candidate.hash }` || DEFAULT_REDIRECT_PATH;
59+
const safePath = `${ pathname }${ candidate.search }${ candidate.hash }`.trim();
60+
return safePath || DEFAULT_REDIRECT_PATH;
5661
} catch {
5762
return DEFAULT_REDIRECT_PATH;
5863
}

0 commit comments

Comments
 (0)