Skip to content

Commit

Permalink
Support browsers that automically url encode cookie values, by always…
Browse files Browse the repository at this point in the history
… encoding and decoding cookies.
  • Loading branch information
MustafaLeithy committed Nov 7, 2023
1 parent 1573ee1 commit 2be40bc
Showing 1 changed file with 45 additions and 7 deletions.
52 changes: 45 additions & 7 deletions packages/clarity-js/src/data/metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export function start(): void {
const ua = navigator && "userAgent" in navigator ? navigator.userAgent : Constant.Empty;
const title = document && document.title ? document.title : Constant.Empty;
electron = ua.indexOf(Constant.Electron) > 0 ? BooleanFlag.True : BooleanFlag.False;

// Populate ids for this page
let s = session();
let u = user();
Expand Down Expand Up @@ -75,7 +75,7 @@ export function start(): void {
function userAgentData(): void {
let uaData = navigator["userAgentData"];
if (uaData && uaData.getHighEntropyValues) {
uaData.getHighEntropyValues(["model","platform","platformVersion","uaFullVersion"]).then(ua => {
uaData.getHighEntropyValues(["model", "platform", "platformVersion", "uaFullVersion"]).then(ua => {
dimension.log(Dimension.Platform, ua.platform);
dimension.log(Dimension.PlatformVersion, ua.platformVersion);
ua.brands?.forEach(brand => { dimension.log(Dimension.Brand, brand.name + Constant.Tilde + brand.version); });
Expand All @@ -99,7 +99,7 @@ export function metadata(cb: MetadataCallback, wait: boolean = true): void {
// Immediately invoke the callback if the caller explicitly doesn't want to wait for the upgrade confirmation
cb(data, !config.lean);
} else {
callbacks.push({callback: cb, wait: wait });
callbacks.push({ callback: cb, wait: wait });
}
}

Expand Down Expand Up @@ -162,7 +162,7 @@ function track(u: User, consent: BooleanFlag = null): void {
consent = consent === null ? u.consent : consent;
// Convert time precision into days to reduce number of bytes we have to write in a cookie
// E.g. Math.ceil(1628735962643 / (24*60*60*1000)) => 18852 (days) => ejo in base36 (13 bytes => 3 bytes)
let end = Math.ceil((Date.now() + (Setting.Expire * Time.Day))/Time.Day);
let end = Math.ceil((Date.now() + (Setting.Expire * Time.Day)) / Time.Day);
// If DOB is not set in the user object, use the date set in the config as a DOB
let dob = u.dob === 0 ? (config.dob === null ? 0 : config.dob) : u.dob;

Expand Down Expand Up @@ -205,7 +205,7 @@ function num(string: string, base: number = 10): number {
function user(): User {
let output: User = { id: shortid(), version: 0, expiry: null, consent: BooleanFlag.False, dob: 0 };
let cookie = getCookie(Constant.CookieKey);
if(cookie && cookie.length > 0) {
if (cookie && cookie.length > 0) {
// Splitting and looking up first part for forward compatibility, in case we wish to store additional information in a cookie
let parts = cookie.split(Constant.Pipe);
// For backward compatibility introduced in v0.6.18; following code can be removed with future iterations
Expand Down Expand Up @@ -243,16 +243,54 @@ function getCookie(key: string): string {
for (let i = 0; i < cookies.length; i++) {
let pair: string[] = cookies[i].split(Constant.Equals);
if (pair.length > 1 && pair[0] && pair[0].trim() === key) {
return pair[1];
// Some browsers automatically url encode cookie values if they are not url encoded.
// We therefore encode and decode cookie values ourselves.
return decodeCookieValue(pair[1]);
}
}
}
}
return null;
}

function decodeCookieValue(value: string): string {
// For backwards compatability we need to consider 3 cases:
// * Cookie was previously not encoded by Clarity and browser did not encode it
// * Cookie was previously not encoded by Clarity and browser encoded it once or more
// * Cookie was previously encoded by Clarity and browser did not encode it
let [isEncoded, decodedValue] = valueIsEncoded(value);
if (!isEncoded) {
return value;
}

while (isEncoded) {
[isEncoded, decodedValue] = valueIsEncoded(decodedValue);
}

return decodedValue;
}

function valueIsEncoded(value: string): [boolean, string] {
try {
let decodedValue = decodeURI(value);
return [decodedValue != value, decodedValue];
}
catch {
}

return [false, value];
}

function encodeCookieValue(value: string): string {
return encodeURI(value);
}

function setCookie(key: string, value: string, time: number): void {
if (config.track && ((navigator && navigator.cookieEnabled) || supported(document, Constant.Cookie))) {
if (config.track && ((navigator && navigator.cookieEnabled) || supported(document, Constant.Cookie))) {
// Some browsers automatically url encode cookie values if they are not url encoded.
// We therefore encode and decode cookie values ourselves.
value = encodeCookieValue(value);

let expiry = new Date();
expiry.setDate(expiry.getDate() + time);
let expires = expiry ? Constant.Expires + expiry.toUTCString() : Constant.Empty;
Expand Down

0 comments on commit 2be40bc

Please sign in to comment.