From 72b56222a879c768e56044e748d8718faecd81f9 Mon Sep 17 00:00:00 2001 From: Stanley Peng Date: Thu, 18 Dec 2025 13:41:31 -0800 Subject: [PATCH 1/2] add url validation for android referres --- spec/src/utils/helpers.js | 74 +++++++++++++++++++++++++++++++++++++++ src/utils/helpers.js | 32 ++++++++++++++--- 2 files changed, 101 insertions(+), 5 deletions(-) diff --git a/spec/src/utils/helpers.js b/spec/src/utils/helpers.js index a038299b..749dac6c 100644 --- a/spec/src/utils/helpers.js +++ b/spec/src/utils/helpers.js @@ -15,6 +15,7 @@ const { isNil, getWindowLocation, getCanonicalUrl, + getDocumentReferrer, dispatchEvent, createCustomEvent, hasOrderIdRecord, @@ -215,6 +216,18 @@ describe('ConstructorIO - Utils - Helpers', () => { }); describe('getCanonicalUrl', () => { + it('Should return android app referrers in a valid url structure', () => { + const cleanup = jsdom(); + + const canonicalUrl = 'android-app://com.google.android.googlequicksearchbox/'; + const canonicalEle = document.querySelector('[rel=canonical]'); + canonicalEle.setAttribute('href', canonicalUrl); + + expect(getCanonicalUrl()).to.equal('https://com.google.android.googlequicksearchbox/'); + + cleanup(); + }); + it('Should return the canonical URL from the DOM link element', () => { const cleanup = jsdom(); @@ -264,6 +277,67 @@ describe('ConstructorIO - Utils - Helpers', () => { }); }); + describe('getDocumentReferrer', () => { + it('Should return android app referrers in a valid url structure', () => { + const cleanup = jsdom(); + + const referrerUrl = 'android-app://com.google.android.googlequicksearchbox/'; + Object.defineProperty(document, 'referrer', { + value: referrerUrl, + configurable: true, + }); + + expect(getDocumentReferrer()).to.equal('https://com.google.android.googlequicksearchbox/'); + + cleanup(); + }); + + it('Should return the referrer URL from the document', () => { + const cleanup = jsdom(); + + const referrerUrl = 'https://constructor.io/products/item'; + Object.defineProperty(document, 'referrer', { + value: referrerUrl, + configurable: true, + }); + + expect(getDocumentReferrer()).to.equal(referrerUrl); + + cleanup(); + }); + + it('Should return null for a relative url', () => { + const cleanup = jsdom(); + + const relativeUrl = '/products/item'; + Object.defineProperty(document, 'referrer', { + value: relativeUrl, + configurable: true, + }); + + const result = getDocumentReferrer(); + expect(result).to.be.null; + + cleanup(); + }); + + it('Should return null when referrer is empty', () => { + const cleanup = jsdom(); + + Object.defineProperty(document, 'referrer', { + value: '', + configurable: true, + }); + + expect(getDocumentReferrer()).to.be.null; + cleanup(); + }); + + it('Should return null when not in a DOM context', () => { + expect(getDocumentReferrer()).to.be.null; + }); + }); + describe('dispatchEvent', () => { it('Should dispatch an event if in a DOM context', () => { const cleanup = jsdom(); diff --git a/src/utils/helpers.js b/src/utils/helpers.js index 057745d3..4f688a6b 100644 --- a/src/utils/helpers.js +++ b/src/utils/helpers.js @@ -44,6 +44,23 @@ const utils = { return cleanedParams; }, + cleanAndValidateUrl: (url, baseUrl = undefined) => { + let validatedUrl = null; + + try { + // Handle android app referrers + if (url?.startsWith('android-app')) { + url = url?.replace('android-app', 'https') + } + + validatedUrl = (new URL(url, baseUrl)).toString(); + } catch (e) { + // do nothing + } + + return validatedUrl; + }, + throwHttpErrorFromResponse: (error, response) => response.json().then((json) => { error.message = json.message; error.status = response.status; @@ -92,11 +109,17 @@ const utils = { }, getDocumentReferrer: () => { - if (utils.canUseDOM()) { - return document?.referrer; + let documentReferrer = null; + + try { + if (utils.canUseDOM()) { + documentReferrer = utils.cleanAndValidateUrl(document.referrer); + } + } catch (e) { + // do nothing } - return null; + return documentReferrer; }, getCanonicalUrl: () => { @@ -108,8 +131,7 @@ const utils = { const href = linkEle?.getAttribute('href'); if (href) { - const url = new URL(href, document.location.href); - canonicalURL = url.toString(); + canonicalURL = utils.cleanAndValidateUrl(href, document.location.href); } } } catch (e) { From 979fe650e475beff6dbdffba6d0b7ec0d1a38805 Mon Sep 17 00:00:00 2001 From: Stanley Peng Date: Thu, 18 Dec 2025 14:20:36 -0800 Subject: [PATCH 2/2] lint spell check --- cspell.json | 3 ++- src/utils/helpers.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cspell.json b/cspell.json index 1a988a7b..b91c2462 100644 --- a/cspell.json +++ b/cspell.json @@ -48,6 +48,7 @@ "atcs", "testdata", "Bytespider", - "Timespans" + "Timespans", + "googlequicksearchbox" ] } diff --git a/src/utils/helpers.js b/src/utils/helpers.js index 4f688a6b..c1a4953d 100644 --- a/src/utils/helpers.js +++ b/src/utils/helpers.js @@ -50,7 +50,7 @@ const utils = { try { // Handle android app referrers if (url?.startsWith('android-app')) { - url = url?.replace('android-app', 'https') + url = url?.replace('android-app', 'https'); } validatedUrl = (new URL(url, baseUrl)).toString();