From 0fb5c7320bd42b7408b23f1e16774d246ba06e86 Mon Sep 17 00:00:00 2001 From: Danny White <3104761+dnywh@users.noreply.github.com> Date: Sun, 19 Apr 2026 09:26:32 +1000 Subject: [PATCH 1/8] fix referrer encoding --- src/app/actions.ts | 22 +++++++++++++++++++++- src/proxy.ts | 18 +++++++----------- src/utils/attributionUtils.ts | 29 ++++++++++++++++++++++++++--- 3 files changed, 54 insertions(+), 15 deletions(-) diff --git a/src/app/actions.ts b/src/app/actions.ts index c80ea86f..0c510092 100644 --- a/src/app/actions.ts +++ b/src/app/actions.ts @@ -13,6 +13,24 @@ import { headers } from "next/headers"; import { redirect } from "next/navigation"; import { getTranslations } from "next-intl/server"; +const normaliseReferrer = (referrer: string | undefined) => { + if (!referrer) return undefined; + + let current = referrer; + + for (let i = 0; i < 3; i += 1) { + try { + const decoded = decodeURIComponent(current); + if (decoded === current) break; + current = decoded; + } catch { + break; + } + } + + return current; +}; + export const signUpAction = async (formData: FormData, request?: Request) => { const t = await getTranslations("Errors"); const email = formData.get("email")?.toString(); @@ -28,7 +46,9 @@ export const signUpAction = async (formData: FormData, request?: Request) => { const origin = headersList.get("origin"); // Get attribution data - const referrer = formData.get("initial_referrer")?.toString(); + const referrer = normaliseReferrer( + formData.get("initial_referrer")?.toString() + ); const utmSource = formData.get("utm_source")?.toString(); const utmMedium = formData.get("utm_medium")?.toString(); const utmCampaign = formData.get("utm_campaign")?.toString(); diff --git a/src/proxy.ts b/src/proxy.ts index b86b1641..87c6afdb 100644 --- a/src/proxy.ts +++ b/src/proxy.ts @@ -36,17 +36,13 @@ export async function proxy(request: NextRequest) { externalReferrer && request.method === "GET" ) { - response.cookies.set( - INITIAL_REFERRER_COOKIE, - encodeURIComponent(externalReferrer), - { - httpOnly: false, - maxAge: INITIAL_REFERRER_MAX_AGE, - path: "/", - sameSite: "lax", - secure: request.nextUrl.protocol === "https:", - } - ); + response.cookies.set(INITIAL_REFERRER_COOKIE, externalReferrer, { + httpOnly: false, + maxAge: INITIAL_REFERRER_MAX_AGE, + path: "/", + sameSite: "lax", + secure: request.nextUrl.protocol === "https:", + }); } return response; diff --git a/src/utils/attributionUtils.ts b/src/utils/attributionUtils.ts index ded500b7..197bd1b7 100644 --- a/src/utils/attributionUtils.ts +++ b/src/utils/attributionUtils.ts @@ -47,12 +47,28 @@ const getCookie = (name: string): string | null => { if (!value) return null; try { - return decodeURIComponent(value); + return normaliseReferrer(value); } catch { return value; } }; +const normaliseReferrer = (referrer: string): string => { + let current = referrer; + + for (let i = 0; i < 3; i += 1) { + try { + const decoded = decodeURIComponent(current); + if (decoded === current) break; + current = decoded; + } catch { + break; + } + } + + return current; +}; + const getExternalDocumentReferrer = (): string | null => { const referrer = document.referrer; @@ -72,7 +88,7 @@ const getExternalDocumentReferrer = (): string | null => { }; const storeInitialReferrer = (referrer: string) => { - localStorage.setItem(INITIAL_REFERRER_KEY, referrer); + localStorage.setItem(INITIAL_REFERRER_KEY, normaliseReferrer(referrer)); }; export function captureAttributionParams() { @@ -125,10 +141,17 @@ export function getStoredAttributionParams(): StoredAttributionParams { const stored = localStorage.getItem(UTM_STORAGE_KEY); const storedInitialReferrer = localStorage.getItem(INITIAL_REFERRER_KEY); const cookieReferrer = getCookie(INITIAL_REFERRER_COOKIE); - const initialReferrer = storedInitialReferrer ?? cookieReferrer; + const initialReferrer = storedInitialReferrer + ? normaliseReferrer(storedInitialReferrer) + : cookieReferrer; if (!storedInitialReferrer && initialReferrer) { storeInitialReferrer(initialReferrer); + } else if ( + storedInitialReferrer && + initialReferrer !== storedInitialReferrer + ) { + storeInitialReferrer(initialReferrer); } return { From 04100dcc703d249c3c97dd9fe7681c283b9cdcdf Mon Sep 17 00:00:00 2001 From: Danny White <3104761+dnywh@users.noreply.github.com> Date: Sun, 19 Apr 2026 09:32:59 +1000 Subject: [PATCH 2/8] fix referrer type guard --- src/utils/attributionUtils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/attributionUtils.ts b/src/utils/attributionUtils.ts index 197bd1b7..ccafdaee 100644 --- a/src/utils/attributionUtils.ts +++ b/src/utils/attributionUtils.ts @@ -149,6 +149,7 @@ export function getStoredAttributionParams(): StoredAttributionParams { storeInitialReferrer(initialReferrer); } else if ( storedInitialReferrer && + initialReferrer && initialReferrer !== storedInitialReferrer ) { storeInitialReferrer(initialReferrer); From 0dbd3138134547af40056b979b22b5e2a3e3d943 Mon Sep 17 00:00:00 2001 From: Danny White <3104761+dnywh@users.noreply.github.com> Date: Sun, 19 Apr 2026 09:48:07 +1000 Subject: [PATCH 3/8] address referrer review --- src/app/actions.ts | 21 ++------------------- src/utils/attributionUtils.ts | 28 +++++----------------------- src/utils/referrer.ts | 22 ++++++++++++++++++++++ 3 files changed, 29 insertions(+), 42 deletions(-) create mode 100644 src/utils/referrer.ts diff --git a/src/app/actions.ts b/src/app/actions.ts index 0c510092..fe196680 100644 --- a/src/app/actions.ts +++ b/src/app/actions.ts @@ -2,6 +2,7 @@ import { validateName } from "@/lib/formValidation"; import { createClient } from "@/utils/supabase/server"; +import { normalizeReferrer } from "@/utils/referrer"; import { getBaseUrl } from "@/utils/url"; import { encodedRedirect, @@ -13,24 +14,6 @@ import { headers } from "next/headers"; import { redirect } from "next/navigation"; import { getTranslations } from "next-intl/server"; -const normaliseReferrer = (referrer: string | undefined) => { - if (!referrer) return undefined; - - let current = referrer; - - for (let i = 0; i < 3; i += 1) { - try { - const decoded = decodeURIComponent(current); - if (decoded === current) break; - current = decoded; - } catch { - break; - } - } - - return current; -}; - export const signUpAction = async (formData: FormData, request?: Request) => { const t = await getTranslations("Errors"); const email = formData.get("email")?.toString(); @@ -46,7 +29,7 @@ export const signUpAction = async (formData: FormData, request?: Request) => { const origin = headersList.get("origin"); // Get attribution data - const referrer = normaliseReferrer( + const referrer = normalizeReferrer( formData.get("initial_referrer")?.toString() ); const utmSource = formData.get("utm_source")?.toString(); diff --git a/src/utils/attributionUtils.ts b/src/utils/attributionUtils.ts index ccafdaee..504f6158 100644 --- a/src/utils/attributionUtils.ts +++ b/src/utils/attributionUtils.ts @@ -1,5 +1,7 @@ "use client"; +import { normalizeReferrer } from "@/utils/referrer"; + const UTM_STORAGE_KEY = "attribution_params"; const INITIAL_REFERRER_KEY = "initial_referrer"; const INITIAL_REFERRER_COOKIE = "initial_referrer"; @@ -46,27 +48,7 @@ const getCookie = (name: string): string | null => { if (!value) return null; - try { - return normaliseReferrer(value); - } catch { - return value; - } -}; - -const normaliseReferrer = (referrer: string): string => { - let current = referrer; - - for (let i = 0; i < 3; i += 1) { - try { - const decoded = decodeURIComponent(current); - if (decoded === current) break; - current = decoded; - } catch { - break; - } - } - - return current; + return normalizeReferrer(value) ?? null; }; const getExternalDocumentReferrer = (): string | null => { @@ -88,7 +70,7 @@ const getExternalDocumentReferrer = (): string | null => { }; const storeInitialReferrer = (referrer: string) => { - localStorage.setItem(INITIAL_REFERRER_KEY, normaliseReferrer(referrer)); + localStorage.setItem(INITIAL_REFERRER_KEY, normalizeReferrer(referrer)); }; export function captureAttributionParams() { @@ -142,7 +124,7 @@ export function getStoredAttributionParams(): StoredAttributionParams { const storedInitialReferrer = localStorage.getItem(INITIAL_REFERRER_KEY); const cookieReferrer = getCookie(INITIAL_REFERRER_COOKIE); const initialReferrer = storedInitialReferrer - ? normaliseReferrer(storedInitialReferrer) + ? normalizeReferrer(storedInitialReferrer) : cookieReferrer; if (!storedInitialReferrer && initialReferrer) { diff --git a/src/utils/referrer.ts b/src/utils/referrer.ts new file mode 100644 index 00000000..f1dc627a --- /dev/null +++ b/src/utils/referrer.ts @@ -0,0 +1,22 @@ +export function normalizeReferrer(referrer: string): string; +export function normalizeReferrer(referrer: undefined): undefined; +export function normalizeReferrer( + referrer: string | undefined +): string | undefined; +export function normalizeReferrer(referrer: string | undefined) { + if (!referrer) return undefined; + + let current = referrer; + + for (let i = 0; i < 3; i += 1) { + try { + const decoded = decodeURIComponent(current); + if (decoded === current) break; + current = decoded; + } catch { + break; + } + } + + return current; +} From 9dc4f4195a648ab18db4dce10e24f471bcb46857 Mon Sep 17 00:00:00 2001 From: Danny White <3104761+dnywh@users.noreply.github.com> Date: Sun, 19 Apr 2026 11:06:10 +1000 Subject: [PATCH 4/8] refine referrer normalisation --- src/app/actions.ts | 4 ++-- src/utils/attributionUtils.ts | 8 ++++---- src/utils/referrer.ts | 14 ++++++++------ 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/app/actions.ts b/src/app/actions.ts index fe196680..bdbc6515 100644 --- a/src/app/actions.ts +++ b/src/app/actions.ts @@ -1,8 +1,8 @@ "use server"; import { validateName } from "@/lib/formValidation"; +import { normaliseReferrer } from "@/utils/referrer"; import { createClient } from "@/utils/supabase/server"; -import { normalizeReferrer } from "@/utils/referrer"; import { getBaseUrl } from "@/utils/url"; import { encodedRedirect, @@ -29,7 +29,7 @@ export const signUpAction = async (formData: FormData, request?: Request) => { const origin = headersList.get("origin"); // Get attribution data - const referrer = normalizeReferrer( + const referrer = normaliseReferrer( formData.get("initial_referrer")?.toString() ); const utmSource = formData.get("utm_source")?.toString(); diff --git a/src/utils/attributionUtils.ts b/src/utils/attributionUtils.ts index 504f6158..f17f9bff 100644 --- a/src/utils/attributionUtils.ts +++ b/src/utils/attributionUtils.ts @@ -1,6 +1,6 @@ "use client"; -import { normalizeReferrer } from "@/utils/referrer"; +import { normaliseReferrer } from "@/utils/referrer"; const UTM_STORAGE_KEY = "attribution_params"; const INITIAL_REFERRER_KEY = "initial_referrer"; @@ -48,7 +48,7 @@ const getCookie = (name: string): string | null => { if (!value) return null; - return normalizeReferrer(value) ?? null; + return normaliseReferrer(value) ?? null; }; const getExternalDocumentReferrer = (): string | null => { @@ -70,7 +70,7 @@ const getExternalDocumentReferrer = (): string | null => { }; const storeInitialReferrer = (referrer: string) => { - localStorage.setItem(INITIAL_REFERRER_KEY, normalizeReferrer(referrer)); + localStorage.setItem(INITIAL_REFERRER_KEY, normaliseReferrer(referrer)); }; export function captureAttributionParams() { @@ -124,7 +124,7 @@ export function getStoredAttributionParams(): StoredAttributionParams { const storedInitialReferrer = localStorage.getItem(INITIAL_REFERRER_KEY); const cookieReferrer = getCookie(INITIAL_REFERRER_COOKIE); const initialReferrer = storedInitialReferrer - ? normalizeReferrer(storedInitialReferrer) + ? normaliseReferrer(storedInitialReferrer) : cookieReferrer; if (!storedInitialReferrer && initialReferrer) { diff --git a/src/utils/referrer.ts b/src/utils/referrer.ts index f1dc627a..4c24a354 100644 --- a/src/utils/referrer.ts +++ b/src/utils/referrer.ts @@ -1,14 +1,16 @@ -export function normalizeReferrer(referrer: string): string; -export function normalizeReferrer(referrer: undefined): undefined; -export function normalizeReferrer( +const encodedReferrerPrefix = /^https?%(?:25)*3a%(?:25)*2f%(?:25)*2f/i; + +export function normaliseReferrer(referrer: string): string; +export function normaliseReferrer(referrer: undefined): undefined; +export function normaliseReferrer( referrer: string | undefined ): string | undefined; -export function normalizeReferrer(referrer: string | undefined) { - if (!referrer) return undefined; +export function normaliseReferrer(referrer: string | undefined) { + if (referrer === undefined) return undefined; let current = referrer; - for (let i = 0; i < 3; i += 1) { + for (let i = 0; i < 3 && encodedReferrerPrefix.test(current); i += 1) { try { const decoded = decodeURIComponent(current); if (decoded === current) break; From f4a454d91537d2c99b65c4ce1611180be17bdd16 Mon Sep 17 00:00:00 2001 From: Danny White <3104761+dnywh@users.noreply.github.com> Date: Sun, 19 Apr 2026 11:06:36 +1000 Subject: [PATCH 5/8] remove duplicate rules --- .cursor/rules/file-format-preference.mdc | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 .cursor/rules/file-format-preference.mdc diff --git a/.cursor/rules/file-format-preference.mdc b/.cursor/rules/file-format-preference.mdc deleted file mode 100644 index d226cdf2..00000000 --- a/.cursor/rules/file-format-preference.mdc +++ /dev/null @@ -1,6 +0,0 @@ ---- -globs: .tsx,.jsx -alwaysApply: false ---- - -Prefer TypeScript over JavaScript when creating components and so forth. From 6753e2a53d8c519725dd6aec940b5c39a638d8c8 Mon Sep 17 00:00:00 2001 From: Danny White <3104761+dnywh@users.noreply.github.com> Date: Sun, 19 Apr 2026 11:17:09 +1000 Subject: [PATCH 6/8] harden referrer normalisation --- src/app/actions.ts | 4 ++-- src/utils/attributionUtils.ts | 11 ++++++----- src/utils/referrer.ts | 25 ++++++++++++++++++++++++- 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/src/app/actions.ts b/src/app/actions.ts index bdbc6515..7a521b73 100644 --- a/src/app/actions.ts +++ b/src/app/actions.ts @@ -1,7 +1,7 @@ "use server"; import { validateName } from "@/lib/formValidation"; -import { normaliseReferrer } from "@/utils/referrer"; +import { getSafeHttpReferrer } from "@/utils/referrer"; import { createClient } from "@/utils/supabase/server"; import { getBaseUrl } from "@/utils/url"; import { @@ -29,7 +29,7 @@ export const signUpAction = async (formData: FormData, request?: Request) => { const origin = headersList.get("origin"); // Get attribution data - const referrer = normaliseReferrer( + const referrer = getSafeHttpReferrer( formData.get("initial_referrer")?.toString() ); const utmSource = formData.get("utm_source")?.toString(); diff --git a/src/utils/attributionUtils.ts b/src/utils/attributionUtils.ts index f17f9bff..896f0bef 100644 --- a/src/utils/attributionUtils.ts +++ b/src/utils/attributionUtils.ts @@ -123,14 +123,15 @@ export function getStoredAttributionParams(): StoredAttributionParams { const stored = localStorage.getItem(UTM_STORAGE_KEY); const storedInitialReferrer = localStorage.getItem(INITIAL_REFERRER_KEY); const cookieReferrer = getCookie(INITIAL_REFERRER_COOKIE); - const initialReferrer = storedInitialReferrer - ? normaliseReferrer(storedInitialReferrer) - : cookieReferrer; + const initialReferrer = + storedInitialReferrer !== null + ? normaliseReferrer(storedInitialReferrer) + : cookieReferrer; - if (!storedInitialReferrer && initialReferrer) { + if (storedInitialReferrer === null && initialReferrer) { storeInitialReferrer(initialReferrer); } else if ( - storedInitialReferrer && + storedInitialReferrer !== null && initialReferrer && initialReferrer !== storedInitialReferrer ) { diff --git a/src/utils/referrer.ts b/src/utils/referrer.ts index 4c24a354..8aa3d8cf 100644 --- a/src/utils/referrer.ts +++ b/src/utils/referrer.ts @@ -1,4 +1,6 @@ const encodedReferrerPrefix = /^https?%(?:25)*3a%(?:25)*2f%(?:25)*2f/i; +const controlCharacters = /[\u0000-\u001f\u007f]/g; +const MAX_REFERRER_LENGTH = 512; export function normaliseReferrer(referrer: string): string; export function normaliseReferrer(referrer: undefined): undefined; @@ -20,5 +22,26 @@ export function normaliseReferrer(referrer: string | undefined) { } } - return current; + return current.replace(controlCharacters, "").slice(0, MAX_REFERRER_LENGTH); +} + +export function getSafeHttpReferrer(referrer: string | undefined) { + const normalisedReferrer = normaliseReferrer(referrer)?.trim(); + + if (!normalisedReferrer) return undefined; + + try { + const referrerUrl = new URL(normalisedReferrer); + + if (!["http:", "https:"].includes(referrerUrl.protocol)) { + return undefined; + } + + referrerUrl.search = ""; + referrerUrl.hash = ""; + + return referrerUrl.toString().slice(0, MAX_REFERRER_LENGTH); + } catch { + return undefined; + } } From 85c0a2eda0d13f032624ebf66f3b3d37d7590ac0 Mon Sep 17 00:00:00 2001 From: Danny White <3104761+dnywh@users.noreply.github.com> Date: Sun, 19 Apr 2026 11:42:52 +1000 Subject: [PATCH 7/8] address referrer review follow-up --- src/app/auth/callback/route.ts | 4 ++-- src/app/auth/confirm/route.ts | 4 ++-- src/app/auth/session/route.ts | 4 ++-- src/components/AuthHashCompletion.tsx | 8 ++++---- src/utils/attributionUtils.ts | 6 +++++- src/utils/authRedirects.ts | 6 +++--- src/utils/referrer.ts | 6 +++--- src/utils/storage.ts | 6 +++--- 8 files changed, 24 insertions(+), 20 deletions(-) diff --git a/src/app/auth/callback/route.ts b/src/app/auth/callback/route.ts index dc7d1276..274f1d87 100644 --- a/src/app/auth/callback/route.ts +++ b/src/app/auth/callback/route.ts @@ -1,5 +1,5 @@ import { createClient } from "@/utils/supabase/server"; -import { appendSuccessParam, normalizeNextPath } from "@/utils/authRedirects"; +import { appendSuccessParam, normaliseNextPath } from "@/utils/authRedirects"; import { NextResponse } from "next/server"; const isAuthDebugEnabled = process.env.NEXT_PUBLIC_AUTH_DEBUG === "true"; @@ -23,7 +23,7 @@ export async function GET(request: Request) { const requestedNextPath = requestUrl.searchParams.get("next") ?? requestUrl.searchParams.get("redirect_to"); - const nextPath = normalizeNextPath(requestedNextPath, "/profile"); + const nextPath = normaliseNextPath(requestedNextPath, "/profile"); if (code) { const supabase = await createClient(); diff --git a/src/app/auth/confirm/route.ts b/src/app/auth/confirm/route.ts index 9966a3e2..6f517fff 100644 --- a/src/app/auth/confirm/route.ts +++ b/src/app/auth/confirm/route.ts @@ -3,7 +3,7 @@ import { appendSuccessParam, getDefaultNextPathByType, isSupportedEmailAuthType, - normalizeNextPath, + normaliseNextPath, } from "@/utils/authRedirects"; import { NextResponse } from "next/server"; @@ -35,7 +35,7 @@ export async function GET(request: Request) { requestUrl.searchParams.get("next") ?? requestUrl.searchParams.get("redirect_to"); const defaultNextPath = getDefaultNextPathByType(authType); - const nextPath = normalizeNextPath(requestedNextPath, defaultNextPath); + const nextPath = normaliseNextPath(requestedNextPath, defaultNextPath); if (!tokenHash || !isSupportedEmailAuthType(authType)) { debugAuth("invalid-confirm-query", { diff --git a/src/app/auth/session/route.ts b/src/app/auth/session/route.ts index fa3f5200..fa682398 100644 --- a/src/app/auth/session/route.ts +++ b/src/app/auth/session/route.ts @@ -3,7 +3,7 @@ import { appendSuccessParam, getDefaultNextPathByType, isSupportedEmailAuthType, - normalizeNextPath, + normaliseNextPath, } from "@/utils/authRedirects"; import { NextResponse } from "next/server"; @@ -54,7 +54,7 @@ export async function POST(request: Request) { } const defaultNextPath = getDefaultNextPathByType(type); - const nextPath = normalizeNextPath( + const nextPath = normaliseNextPath( typeof body?.next === "string" ? body.next : null, defaultNextPath ); diff --git a/src/components/AuthHashCompletion.tsx b/src/components/AuthHashCompletion.tsx index 6ff09d45..6a9f2835 100644 --- a/src/components/AuthHashCompletion.tsx +++ b/src/components/AuthHashCompletion.tsx @@ -4,7 +4,7 @@ import { useEffect } from "react"; import { getDefaultNextPathByType, isSupportedEmailAuthType, - normalizeNextPath, + normaliseNextPath, } from "@/utils/authRedirects"; const INVALID_LINK_MESSAGE = @@ -37,7 +37,7 @@ export default function AuthHashCompletion() { queryParams.get("next") ?? queryParams.get("redirect_to"); const requestedType = queryParams.get("type") ?? hashType; const defaultNextPath = getDefaultNextPathByType(requestedType); - const nextPath = normalizeNextPath(preferredNextPath, defaultNextPath); + const nextPath = normaliseNextPath(preferredNextPath, defaultNextPath); const authCode = queryParams.get("code"); const hasAuthHashPayload = @@ -126,7 +126,7 @@ export default function AuthHashCompletion() { return; } - const typedNextPath = normalizeNextPath( + const typedNextPath = normaliseNextPath( preferredNextPath, getDefaultNextPathByType(type) ); @@ -177,7 +177,7 @@ export default function AuthHashCompletion() { return; } - const resolvedNextPath = normalizeNextPath(data.next, typedNextPath); + const resolvedNextPath = normaliseNextPath(data.next, typedNextPath); debugAuth("session-finalized", { type, nextPath: resolvedNextPath, diff --git a/src/utils/attributionUtils.ts b/src/utils/attributionUtils.ts index 896f0bef..3fa04bfa 100644 --- a/src/utils/attributionUtils.ts +++ b/src/utils/attributionUtils.ts @@ -48,7 +48,11 @@ const getCookie = (name: string): string | null => { if (!value) return null; - return normaliseReferrer(value) ?? null; + const cleanedValue = normaliseReferrer(value); + + if (!cleanedValue.trim()) return null; + + return cleanedValue; }; const getExternalDocumentReferrer = (): string | null => { diff --git a/src/utils/authRedirects.ts b/src/utils/authRedirects.ts index aac2022b..021203d9 100644 --- a/src/utils/authRedirects.ts +++ b/src/utils/authRedirects.ts @@ -24,7 +24,7 @@ export const getDefaultNextPathByType = (type: string | null | undefined) => { return "/profile"; }; -export const normalizeNextPath = ( +export const normaliseNextPath = ( candidatePath: string | null | undefined, fallbackPath: string ) => { @@ -49,8 +49,8 @@ export const normalizeNextPath = ( }; export const appendSuccessParam = (path: string, successValue: string) => { - const normalizedPath = normalizeNextPath(path, "/profile"); - const url = new URL(normalizedPath, "https://www.peels.app"); + const normalisedPath = normaliseNextPath(path, "/profile"); + const url = new URL(normalisedPath, "https://www.peels.app"); url.searchParams.set("success", successValue); return `${url.pathname}${url.search}${url.hash}`; }; diff --git a/src/utils/referrer.ts b/src/utils/referrer.ts index 8aa3d8cf..a64802bd 100644 --- a/src/utils/referrer.ts +++ b/src/utils/referrer.ts @@ -26,12 +26,12 @@ export function normaliseReferrer(referrer: string | undefined) { } export function getSafeHttpReferrer(referrer: string | undefined) { - const normalisedReferrer = normaliseReferrer(referrer)?.trim(); + const cleanedReferrer = normaliseReferrer(referrer)?.trim(); - if (!normalisedReferrer) return undefined; + if (!cleanedReferrer) return undefined; try { - const referrerUrl = new URL(normalisedReferrer); + const referrerUrl = new URL(cleanedReferrer); if (!["http:", "https:"].includes(referrerUrl.protocol)) { return undefined; diff --git a/src/utils/storage.ts b/src/utils/storage.ts index a500a07d..874e4dde 100644 --- a/src/utils/storage.ts +++ b/src/utils/storage.ts @@ -1,7 +1,7 @@ const PRODUCTION_SUPABASE_HOST = "mfnaqdyunuafbwukbbyr.supabase.co"; const STORAGE_PUBLIC_PATH = "/storage/v1/object/public"; -function normalizeAssetPath(assetPath: string) { +function normaliseAssetPath(assetPath: string) { return assetPath.replace(/^\/+/, ""); } @@ -22,7 +22,7 @@ export function getStoragePublicUrl(bucket: string, assetPath: string) { if (!supabaseUrl) return null; - return `${supabaseUrl.replace(/\/$/, "")}${STORAGE_PUBLIC_PATH}/${bucket}/${normalizeAssetPath(assetPath)}`; + return `${supabaseUrl.replace(/\/$/, "")}${STORAGE_PUBLIC_PATH}/${bucket}/${normaliseAssetPath(assetPath)}`; } export function usesHostedStaticAssets() { @@ -43,7 +43,7 @@ export function getStaticAssetUrl( export function getStaticFontUrl(assetPath: string) { return getStoragePublicUrl( "static", - `fonts/${normalizeAssetPath(assetPath)}` + `fonts/${normaliseAssetPath(assetPath)}` ); } From 8b6f64f6d88ded341c495a125242a6c17d12275d Mon Sep 17 00:00:00 2001 From: Danny White <3104761+dnywh@users.noreply.github.com> Date: Sun, 19 Apr 2026 11:53:36 +1000 Subject: [PATCH 8/8] trim referrer cookie values --- src/utils/attributionUtils.ts | 5 +++-- src/utils/referrer.ts | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/utils/attributionUtils.ts b/src/utils/attributionUtils.ts index 3fa04bfa..af1fbc90 100644 --- a/src/utils/attributionUtils.ts +++ b/src/utils/attributionUtils.ts @@ -49,10 +49,11 @@ const getCookie = (name: string): string | null => { if (!value) return null; const cleanedValue = normaliseReferrer(value); + const trimmedValue = cleanedValue.trim(); - if (!cleanedValue.trim()) return null; + if (!trimmedValue) return null; - return cleanedValue; + return trimmedValue; }; const getExternalDocumentReferrer = (): string | null => { diff --git a/src/utils/referrer.ts b/src/utils/referrer.ts index a64802bd..20b10fd6 100644 --- a/src/utils/referrer.ts +++ b/src/utils/referrer.ts @@ -10,7 +10,7 @@ export function normaliseReferrer( export function normaliseReferrer(referrer: string | undefined) { if (referrer === undefined) return undefined; - let current = referrer; + let current = referrer.trim(); for (let i = 0; i < 3 && encodedReferrerPrefix.test(current); i += 1) { try {