diff --git a/assets/js/background.js b/assets/js/background.js index a624f05..53b95b1 100644 --- a/assets/js/background.js +++ b/assets/js/background.js @@ -112,7 +112,8 @@ function registerRedirectHandlers() { for (let targetParam in REDIRECT_DATA_BY_TARGET_PARAM) { const { patterns: urls, - types + types, + paramDelimiters = ['&'], } = REDIRECT_DATA_BY_TARGET_PARAM[targetParam]; // Don't do anything stupid. @@ -132,7 +133,7 @@ function registerRedirectHandlers() { return {}; } - const targetUrl = extractRedirectTarget(details.url, targetParam); + const targetUrl = extractRedirectTarget(details.url, targetParam, paramDelimiters); if (!targetUrl) { return {}; } diff --git a/assets/js/common.js b/assets/js/common.js index 9dc8c9f..fff0b67 100644 --- a/assets/js/common.js +++ b/assets/js/common.js @@ -19,13 +19,19 @@ function getOptionsFromStorage(cb, options) { } -function findQueryParam(targetParam, url) { +function findQueryParam(targetParam, url, paramDelimiter = '&') { url = url || window.location.href; - if (!(targetParam && url)) { + if (!(targetParam && url && paramDelimiter)) { return false; } + // For now we'll support query stryings that take one of 2 forms: + // 1) key1=value1&key2=value2&key3=value3 + // 2) key1(value1)key2(value2)key3(value3) + // So...base the key/value delimiter based on the param delimiter + const kvDelimiter = paramDelimiter === '&' ? '=' : '('; + // Find the first occurrance of '?' character. I've seen URLs that have embedded // URLs that are not properly encoded, e.g.: // https://www.google.com/url?hl=en&q=http://t.dd.delta.org/r/?id%3Dxxxxx,yyyyyy,zzzzz&source=gmail&ust=1516647918588000&usg=AFQjCNEV1C1cwHSrU8r1kyYmaPe4IAsb-Q @@ -42,7 +48,7 @@ function findQueryParam(targetParam, url) { if (queryString) { // Get the key/value pairs from the query string - const keyVals = queryString.split('&'); + const keyVals = queryString.split(paramDelimiter); // Figure out how many pairs we have const kvsLength = keyVals.length; // For each iteration fo the loop @@ -50,13 +56,15 @@ function findQueryParam(targetParam, url) { for(let i=0; i < kvsLength; i++) { // Get this key/value pair and split it up into its pieces - kv = keyVals[i].split('='); + kv = keyVals[i].split(kvDelimiter); // We are looking for "url=blahblahblah", so see if this is the one if (kv[0] === targetParam) { return kv[1]; } } } + + return false; } diff --git a/assets/js/redirects.js b/assets/js/redirects.js index 0433b50..b0f46ad 100644 --- a/assets/js/redirects.js +++ b/assets/js/redirects.js @@ -8,7 +8,8 @@ const SCHEMA = ''; const SUBDOMAIN = ''; const PATH = ''; const QS_VALUE = ''; -const QS_KVS = ''; +const QS_KVS_1 = ''; +const QS_KVS_2 = ''; const KNOWN_REDIRECTS = [ @@ -93,6 +94,7 @@ const KNOWN_REDIRECTS = [ { name: 'Tradedoubler', targetParam: 'url', + paramDelimiters: ['&', ')'], patterns: [ `${SCHEMA}${SUBDOMAIN}.tradedoubler.com/click?` ], @@ -126,7 +128,13 @@ const KNOWN_REDIRECTS = [ name: 'Slack', targetParam: 'url', patterns: [ - `${SCHEMA}slack-redir.net/link?` + `${SCHEMA}slack-redir.net/link?`, + ], + types: ['main_frame'] + name: 'Digidip', + targetParam: 'url', + patterns: [ + `${SCHEMA}${SUBDOMAIN}.digidip.net/visit?`, ], types: ['main_frame'] } @@ -139,30 +147,32 @@ const KNOWN_REDIRECTS = [ // using a closure-like approach to prevent having to scan the URL again // to figure out which pattern it matched, and then finally extract the // target for that pattern. Should result in things being much faster in -// then end. +// the end. // Use 'var' here so that it's not scoped incorrectly. var REDIRECT_DATA_BY_TARGET_PARAM = {}; KNOWN_REDIRECTS.forEach(KNOWN_REDIRECT => { // Pluck out the param and the patterns - const targetParam = KNOWN_REDIRECT.targetParam; - const orginalPatterns = KNOWN_REDIRECT.patterns; - const types = KNOWN_REDIRECT.types; + const { + targetParam, + paramDelimiters = ['&'], + patterns: orginalPatterns, + types, + } = KNOWN_REDIRECT; // Make sure everything looks good - if (!(targetParam && orginalPatterns && orginalPatterns.length && types && types.length)) { + if (!(targetParam && orginalPatterns && orginalPatterns.length && types && types.length && paramDelimiters && paramDelimiters.length)) { return; } // Prep the Object if necessary - if (!(REDIRECT_DATA_BY_TARGET_PARAM[targetParam])) { - REDIRECT_DATA_BY_TARGET_PARAM[targetParam] = { - patterns: [], - regexes: [], - types: [] - }; - } + REDIRECT_DATA_BY_TARGET_PARAM[targetParam] = REDIRECT_DATA_BY_TARGET_PARAM[targetParam] || { + paramDelimiters: [], + patterns: [], + regexes: [], + types: [], + }; // Go through every 'type' for this redirect types.forEach(type => { @@ -172,24 +182,38 @@ KNOWN_REDIRECTS.forEach(KNOWN_REDIRECT => { } }); + paramDelimiters.forEach(paramDelimiter => { + if (!REDIRECT_DATA_BY_TARGET_PARAM[targetParam].paramDelimiters.includes(paramDelimiter)) { + REDIRECT_DATA_BY_TARGET_PARAM[targetParam].paramDelimiters.push(paramDelimiter); + } + }); + const newPatterns = []; const newClipboardRegexes = []; // Go through each of these patterns and create any combinations we need to orginalPatterns.forEach(originalPattern => { - - // Create the key/value placeholder for the target param - const targetParamKv = `${targetParam}=${QS_VALUE}`; - - // We need to generate a few variations on this original pattern for URL matching - // 1) support the URL param as the first param - newPatterns.push(replacePlaceholders(`${originalPattern}${targetParamKv}`)); - // 2) support the URL param as a non-first param - newPatterns.push(replacePlaceholders(`${originalPattern}${QS_KVS}${targetParamKv}`)); - - // The regex only needs 1 variation which includes optional query string key/values - const regexPattern = replacePlaceholdersRegex(`${originalPattern}${QS_KVS}${targetParamKv}`); - newClipboardRegexes.push(new RegExp(regexPattern)); + paramDelimiters.forEach(paramDelimiter => { + // For now we'll support query stryings that take one of 2 forms: + // 1) key1=value1&key2=value2&key3=value3 + // 2) key1(value1)key2(value2)key3(value3) + // So...base the key/value delimiter based on the param delimiter + const kvDelimiter = paramDelimiter === '&' ? '=' : '('; + const kvPlaceholder = paramDelimiter === '&' ? QS_KVS_1 : QS_KVS_2; + + // Create the key/value placeholder for the target param + const targetParamKv = `${targetParam}${kvDelimiter}${QS_VALUE}`; + + // We need to generate a few variations on this original pattern for URL matching + // 1) support the URL param as the first param + newPatterns.push(replacePlaceholders(`${originalPattern}${targetParamKv}`)); + // 2) support the URL param as a non-first param + newPatterns.push(replacePlaceholders(`${originalPattern}${kvPlaceholder}${targetParamKv}`)); + + // The regex only needs 1 variation which includes optional query string key/values + const regexPattern = replacePlaceholdersRegex(`${originalPattern}${kvPlaceholder}${targetParamKv}`); + newClipboardRegexes.push(new RegExp(regexPattern)); + }); }); // Add these patterns to the array of patterns for this target param @@ -199,7 +223,6 @@ KNOWN_REDIRECTS.forEach(KNOWN_REDIRECT => { REDIRECT_DATA_BY_TARGET_PARAM[targetParam].regexes.push(...newClipboardRegexes); }); - // Escape all of the literals function escapeRegExp(str) { return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); @@ -211,7 +234,8 @@ function replacePlaceholders(pattern) { .replace(SCHEMA, '*://') .replace(SUBDOMAIN, '*') .replace(PATH, '/*') - .replace(QS_KVS, '*&') + .replace(QS_KVS_1, '*&') + .replace(QS_KVS_2, '*)') .replace(QS_VALUE, '*'); } @@ -225,12 +249,14 @@ function replacePlaceholdersRegex(pattern) { // This one required text on either side of the '=' sign, when I've seen // some places build junk that would not match. Not sure if this is a good idea // to "fix" or not. - // pattern = pattern.replace(QS_KVS, '([\\w]+\\=[\\w]+\\&)*'); + // pattern = pattern.replace(QS_KVS_1, '([\\w]+\\=[\\w]+\\&)*'); // This would be the "fix" for the above. It allows blanks on either side of the // '=' sign. - // pattern = pattern.replace(QS_KVS, '([\\w*+\\=[\\w]*\\&)*'); + // pattern = pattern.replace(QS_KVS_1, '([\\w*+\\=[\\w]*\\&)*'); // OK, this one handles even more scenarios that are acceptable - .replace(QS_KVS, '([\\w*+\\=?[\\w]*\\&)*') + .replace(QS_KVS_1, '([\\w*+\\=?[\\w]*\\&)*') + // foo(bar)baz(bop)... + .replace(QS_KVS_2, '(\\w*\\(\\w*\\))*') .replace(QS_VALUE, '\\w'); } @@ -240,21 +266,23 @@ function replacePlaceholdersCreateExample(pattern) { .replace(SCHEMA, 'https://') .replace(SUBDOMAIN, 'foo') .replace(PATH, '/path/to/whatever') - .replace(QS_KVS, '&') + .replace(QS_KVS_1, '&') .replace(QS_VALUE, 'foo'); } // Extract the redirect target from a URL given the target parameter -function extractRedirectTarget(url, targetParam = 'url') { - // See if we can find a target in the URL. - let target = findQueryParam(targetParam, url); +function extractRedirectTarget(url, targetParam = 'url', paramDelimiters = ['&']) { + for (let paramDelimiter of paramDelimiters) { + // See if we can find a target in the URL. + let target = findQueryParam(targetParam, url, paramDelimiter); - if (typeof target === 'string' && target.startsWith('http')) { - return decodeURIComponent(target); - } + if (typeof target === 'string' && target.startsWith('http')) { + return decodeURIComponent(target); + } + }; - return false; + return false } @@ -267,7 +295,8 @@ function followRedirect(url) { for (let targetParam in REDIRECT_DATA_BY_TARGET_PARAM) { // Get the regexes for this target param const { - regexes = [] + regexes = [], + paramDelimiters = ['&'], } = REDIRECT_DATA_BY_TARGET_PARAM[targetParam]; // Go through each regex for this target param @@ -275,7 +304,7 @@ function followRedirect(url) { regex = regexes[i]; // If the URL matches this redirect pattern, then extract the redirect. if (regex.test(url)) { - url = extractRedirectTarget(url, targetParam) || url; + url = extractRedirectTarget(url, targetParam, paramDelimiters) || url; // All done with this regex stuff. break outerLoop; } diff --git a/assets/manifest.json b/assets/manifest.json index bf4e668..a049bb3 100644 --- a/assets/manifest.json +++ b/assets/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "URL Tracking Stripper & Redirect Skipper", "short_name": "URL Tracking Stripper & Redirect Skipper", - "version": "5.0.1", + "version": "5.1.0", "description": "Increase the speed & privacy of your browsing. Skip/remove tracking parameters & redirects from URLs to keep them shorter & cleaner.", "homepage_url": "https://github.com/newhouse/url-tracking-stripper", "icons": {