fix: validate rd redirect on login with same rules as logout (#8655)

* fix: validate rd redirect on login with same rules as logout

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: sanitize rd at source in login callback to prevent leaking to goto

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: validate rd redirect in Login component for fresh login flow

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ruben Fiszel
2026-04-01 19:44:22 +00:00
committed by GitHub
parent 175af8032f
commit bcce627387
3 changed files with 16 additions and 4 deletions

View File

@@ -21,6 +21,7 @@
import Skeleton from './common/skeleton/Skeleton.svelte'
import Button from './common/button/Button.svelte'
import { sameTopDomainOrigin } from '$lib/cookies'
import { isValidLogoutRedirect } from '$lib/logoutRedirect'
interface Props {
rd?: string | undefined
@@ -134,7 +135,11 @@
async function redirectUser() {
if (rd?.startsWith('http')) {
window.location.href = rd
if (isValidLogoutRedirect(rd)) {
window.location.href = rd
return
}
goto('/')
return
}
if ($workspaceStore) {

View File

@@ -20,6 +20,7 @@
import Login from '$lib/components/Login.svelte'
import { onMount } from 'svelte'
import { refreshSuperadmin } from '$lib/refreshUser'
import { isValidLogoutRedirect } from '$lib/logoutRedirect'
const email = page.url.searchParams.get('email') ?? ''
const password = page.url.searchParams.get('password') ?? ''
@@ -55,7 +56,11 @@
async function redirectUser() {
if (rd?.startsWith('http')) {
window.location.href = rd
if (isValidLogoutRedirect(rd)) {
window.location.href = rd
return
}
goto('/')
return
}

View File

@@ -8,6 +8,7 @@
import { userStore, usersWorkspaceStore, workspaceStore } from '$lib/stores'
import { getUserExt } from '$lib/user'
import { logoutWithRedirect } from '$lib/logoutKit'
import { isValidLogoutRedirect } from '$lib/logoutRedirect'
import { parseQueryParams } from '$lib/utils'
import { page } from '$app/state'
import { isCloudHosted } from '$lib/cloud'
@@ -22,10 +23,11 @@
onMount(async () => {
// const closeCookie = getAndDeleteCookie('close')
// console.log('closeCookie', closeCookie)
const rd = localStorage.getItem('rd')
if (rd) {
const rawRd = localStorage.getItem('rd')
if (rawRd) {
localStorage.removeItem('rd')
}
const rd = rawRd?.startsWith('http') && !isValidLogoutRedirect(rawRd) ? null : rawRd
const cookieCloseUponLogin = getCookie('close') == 'true'
const closeUponLogin = cookieCloseUponLogin ?? localStorage.getItem('closeUponLogin') == 'true'
if (error) {