Skip to content

Instantly share code, notes, and snippets.

@sahava
Last active December 29, 2023 09:11
Show Gist options
  • Save sahava/f3718f981bb01768c0eba714ee94e2d2 to your computer and use it in GitHub Desktop.
Save sahava/f3718f981bb01768c0eba714ee94e2d2 to your computer and use it in GitHub Desktop.
This script reproduces Google Analytics' allowLinker plugin, resulting in a function that returns true if the linker parameter in the URL is valid and false otherwise. Uses the "old" linker plugin format. You can also pass a string as an argument to check if that string is a valid linker parameter.
var checkLinker = function(str) {
// First browser fingerprint method.
// Uses the clientId / gid string, user agent, time, and browser plugin descriptions.
var joiner = function (cidGid, offset) {
var a = new Date,
b = window.navigator,
c = b.plugins || [];
var d = [cidGid, b.userAgent, a.getTimezoneOffset(), a.getYear(), a.getDate(), a.getHours(), a.getMinutes() + offset];
for (var e = 0; e < c.length; ++e) {
d.push(c[e].description);
}
return jumble(d.join('.'));
};
// Second browser fingerprint method.
// Uses the clientId / gid string, time, user agent, browser language.
var joiner2 = function (cidGid, offset) {
var a = new Date,
b = window.navigator,
c = a.getHours() + Math.floor((a.getMinutes() + offset) / 60);
return jumble([cidGid, b.userAgent, b.language || "", a.getTimezoneOffset(), a.getYear(), a.getDate() + Math.floor(c / 24), (24 + c) % 24, (60 + a.getMinutes() + offset) % 60].join("."));
};
// One-way hash of the fingerprint, included in the linker parameter.
var jumble = function (arr) {
var b = 1, c;
if (arr) {
for (b = 0, c = arr.length - 1; 0 <= c; c--) {
var d = arr.charCodeAt(c);
b = (b << 6 & 268435455) + d + (d << 14);
d = b & 266338304;
b = 0 != d ? b ^ d >> 21 : b
}
}
return b.toString();
};
var linkerType, linker;
// Check Linker validity and isolate the Linker parameter string.
if (typeof str === 'string' && str.length) {
if (!/_ga=/.test(str)) {
return 'Invalid linker format in string argument!';
}
linker = str.split('&').filter(function(p) { return p.split('=')[0] === '_ga'; }).shift();
} else {
linkerType = /[?&]_ga=/.test(window.location.search) ? 'search' : /[#&]_ga=/.test(window.location.hash) ? 'hash' : undefined;
linker = linkerType && window.location[linkerType].substring(1).split('&').filter(function(p) { return p.split('=')[0] === '_ga'; }).shift();
}
if (typeof linker === 'undefined' || !linker.length) {
return 'Invalid linker format in URL!';
}
// Get the finger print and Client ID / Google ID strings from the parameter.
var a = linker.indexOf('.'),
b, c, d, fingerprint, cidGid;
if (a > -1) {
b = linker.substring(0, a);
c = linker.substring(a + 1);
d = c.indexOf(".");
fingerprint = c.substring(0, d);
cidGid = c.substring(d + 1);
}
// Jumble the Client ID / Google ID string and compare it against the fingerprint.
// Check current minute, one minute back, and two minutes back.
if (typeof cidGid !== 'undefined') {
cidGid = cidGid.split('-').join('');
return fingerprint === joiner(cidGid, 0) ||
fingerprint === joiner(cidGid, -1) ||
fingerprint === joiner(cidGid, -2) ||
fingerprint === joiner2(cidGid, 0) ||
fingerprint === joiner2(cidGid, -1) ||
fingerprint === joiner2(cidGid, -2);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment