WS-3 PR-B2b: A13-3 + single-cookie + single-host (incl. flatpickr precursor) #6
Binary file not shown.
@@ -1,19 +1,49 @@
|
||||
/**
|
||||
* Resolve the post-login redirect target. If the caller supplied a `?to=`
|
||||
* query that's a same-origin relative path, honour it; otherwise fall back
|
||||
* to the auth-store's resolveLandingRoute().
|
||||
* query that is a same-origin, well-formed relative path, honour it;
|
||||
* otherwise fall back to the auth-store's resolveLandingRoute().
|
||||
*
|
||||
* The `startsWith('/')` + `!startsWith('//')` guard is the **minimum**
|
||||
* A13-3 (open-redirect) precaution. Full domain-validation lands in
|
||||
* WS-3 PR-B2b.
|
||||
* `isSafeRelativePath` rejects every input that is not a strict relative
|
||||
* path: missing/empty, absolute, protocol-relative (`//`), backslash-bearing
|
||||
* (browsers normalise `\` → `/` in some contexts), control-character-bearing,
|
||||
* or anything the URL constructor parses to a different origin than our
|
||||
* synthetic invalid origin. The URL-constructor check is the authoritative
|
||||
* guard — the prefix and character checks are fast pre-filters.
|
||||
*
|
||||
* Closes A13-3 (open-redirect on post-login). The minimum precaution from
|
||||
* WS-3 PR-B2a (`startsWith('/') && !startsWith('//')`) is now superseded.
|
||||
*/
|
||||
|
||||
const SYNTHETIC_ORIGIN = 'https://__crewli_safe_relative_check__.invalid'
|
||||
|
||||
function isSafeRelativePath(to: string): boolean {
|
||||
if (!to || !to.startsWith('/') || to.startsWith('//'))
|
||||
return false
|
||||
|
||||
if (to.includes('\\'))
|
||||
return false
|
||||
|
||||
// eslint-disable-next-line no-control-regex
|
||||
if (/[\x00-\x1F\x7F]/.test(to))
|
||||
return false
|
||||
|
||||
try {
|
||||
const url = new URL(to, SYNTHETIC_ORIGIN)
|
||||
if (url.origin !== SYNTHETIC_ORIGIN)
|
||||
return false
|
||||
}
|
||||
catch {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
export function resolvePostLoginTarget(
|
||||
rawTo: string | null | undefined,
|
||||
fallback: () => string,
|
||||
): string {
|
||||
const to = rawTo ?? ''
|
||||
if (to.startsWith('/') && !to.startsWith('//'))
|
||||
return to
|
||||
|
||||
return fallback()
|
||||
return isSafeRelativePath(to) ? to : fallback()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user