diff --git a/src/services/auth.ts b/src/services/auth.ts index 60359c8..050679d 100644 --- a/src/services/auth.ts +++ b/src/services/auth.ts @@ -4,6 +4,7 @@ import { md5 } from 'hash-wasm'; import { rootRouterPath, router } from '@/router'; import { api } from '@/services/api'; import { setToken } from '@/services/request'; +import { isSafeRedirect } from '@/utils/helper'; let _email = ''; export const setUserEmail = (email: string) => { @@ -23,7 +24,9 @@ export async function login(email: string, password: string) { const loginFrom = new URLSearchParams(window.location.search).get( 'loginFrom', ); - router.navigate(loginFrom || rootRouterPath.user); + router.navigate( + isSafeRedirect(loginFrom) ? loginFrom! : rootRouterPath.user, + ); } } catch (err) { const e = err as Error; diff --git a/src/utils/helper.test.ts b/src/utils/helper.test.ts index 8068340..d034fe7 100644 --- a/src/utils/helper.test.ts +++ b/src/utils/helper.test.ts @@ -1,5 +1,38 @@ import { describe, expect, test } from 'bun:test'; -import { isExpVersion } from './helper'; +import { isExpVersion, isSafeRedirect } from './helper'; + +describe('isSafeRedirect', () => { + test('should return true for internal paths', () => { + expect(isSafeRedirect('/dashboard')).toBe(true); + expect(isSafeRedirect('/user/profile')).toBe(true); + expect(isSafeRedirect('/')).toBe(true); + }); + + test('should return false for empty or nullish values', () => { + expect(isSafeRedirect(null)).toBe(false); + expect(isSafeRedirect(undefined)).toBe(false); + expect(isSafeRedirect('')).toBe(false); + }); + + test('should return false for external URLs', () => { + expect(isSafeRedirect('https://google.com')).toBe(false); + expect(isSafeRedirect('http://malicious.com')).toBe(false); + expect(isSafeRedirect('ftp://server.com')).toBe(false); + }); + + test('should return false for protocol-relative URLs', () => { + expect(isSafeRedirect('//google.com')).toBe(false); + }); + + test('should return false for backslash-prefixed paths (potential bypass)', () => { + expect(isSafeRedirect('/\\google.com')).toBe(false); + }); + + test('should return false for paths that do not start with /', () => { + expect(isSafeRedirect('user/profile')).toBe(false); + expect(isSafeRedirect('javascript:alert(1)')).toBe(false); + }); +}); describe('isExpVersion', () => { test('should return false when config is null', () => { diff --git a/src/utils/helper.ts b/src/utils/helper.ts index 51064a9..0fc0a11 100644 --- a/src/utils/helper.ts +++ b/src/utils/helper.ts @@ -66,6 +66,12 @@ export const testUrls = async (urls?: string[]) => { return urls[0]; }; +export const isSafeRedirect = (url: string | null | undefined): boolean => { + if (!url) return false; + // Ensure it starts with / and not // or \ + return url.startsWith('/') && !url.startsWith('//') && !url.startsWith('/\\'); +}; + export const isExpVersion = ( config: VersionConfig | null | undefined, packageVersion: string,