├── .gitignore ├── LICENSE.txt ├── README.md └── linksanitizer.user.js /.gitignore: -------------------------------------------------------------------------------- 1 | *.png 2 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## About 2 | 3 | Link Sanitizer is a simple and efficient userscript for [Violentmonkey](https://violentmonkey.github.io) and [Greasemonkey](https://www.greasespot.net) browser addons. It will "sanitize" links on all webpages using a simple set of common rules. This includes removing trackers, link shims, and other bloated hyperlink redirects. In addition to this script, it is a good idea to use [Privacy Badger](https://www.eff.org/pb), who specializes on some nasty [Facebook](https://www.eff.org/deeplinks/2018/05/privacy-badger-rolls-out-new-ways-fight-facebook-tracking) and [Google](https://www.eff.org/deeplinks/2018/10/privacy-badger-now-fights-more-sneaky-google-tracking) deeplink trackers. 4 | 5 | ## Installation 6 | 7 | 1. Install [Violentmonkey](https://violentmonkey.github.io) or [Greasemonkey](https://www.greasespot.net) userscript manager addon in your browser. 8 | 2. Open [Link Sanitizer file](https://raw.githubusercontent.com/cloux/LinkSanitizer/master/linksanitizer.user.js). Your userscript manager should recognize and install it. 9 | 10 | ## Known Issues 11 | 12 | ### Userscripts and Content Security Policies 13 | 14 | Userscripts won't work on websites implementing restrictive Content Security Policies (e.g. _addons.mozilla.org_, _github.com_, or _twitter.com_). Currently the only way to make ViolentMonkey or GreaseMonkey work on all sites is to disable the CSP protection, which is [generally **not recommended**](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP). 15 | 16 | The corresponding setting in Firefox about:config is: 17 | 18 | security.csp.enable = false 19 | 20 | On Chrome, the CSP can be overridden by extensions [Content Security Policy Override](https://chrome.google.com/webstore/detail/content-security-policy-o/lhieoncdgamiiogcllfmboilhgoknmpi), [UnXSS](https://chrome.google.com/webstore/detail/unxss/cbjmpjkhiafmdnjnigdbelcnbihgpmge) or [Caspr Enforcer](https://chrome.google.com/webstore/detail/caspr-enforcer/fekcdjkhlbjngkimekikebfegbijjafd). 21 | 22 | The development progress on this issue for Mozilla is [here](https://bugzilla.mozilla.org/show_bug.cgi?id=1267027), the discussion on Greasemonkey code is [here](https://github.com/greasemonkey/greasemonkey/issues/2046). 23 | 24 | ### Firefox addons on "superior" domains 25 | 26 | Mozilla decided that Firefox simply disables addons for domains which are regarded as superior to user and his freedom. These domains are unsurprisingly owned by Mozilla. To get rid of this nasty "superior domain list" and enable addons everywhere, set in about:config: 27 | 28 | extensions.webextensions.restrictedDomains = "" 29 | 30 | ### Firefox and AddonManagerAPI on addons.mozilla.org 31 | 32 | Addons are also disabled for the _addons.mozilla.org_ domain, which can access the hidden AddonManagerWebAPI exposed by Firefox. This Firefox behavior is [hardcoded](https://dxr.mozilla.org/mozilla-central/source/toolkit/mozapps/extensions/AddonManagerWebAPI.cpp). To make userscripts work on addons.mozilla.org, first turn off Firefox and then add into the _~/.mozilla/firefox/PROFILE.default/prefs.js_ file: 33 | 34 | user_pref("privacy.resistFingerprinting.block_mozAddonManager", true); 35 | 36 | NOTE: Your path to the _prefs.js_ might differ. See [mozillazine article](http://kb.mozillazine.org/Prefs.js_file) or [dev docs](https://developer.mozilla.org/en-US/docs/Mozilla/Preferences/A_brief_guide_to_Mozilla_preferences) on editing user preferences. 37 | 38 | NOTE: This setting is hidden and not available in about:config, it has to be added into the _prefs.js_ manually. 39 | 40 | NOTE: You will be able to install plugins as usual, but the _addons.mozilla.org_ website will not be able to see which addons are already installed or otherwise manipulate them. 41 | 42 | ### Compatibility 43 | 44 | Tested with [Violentmonkey](https://violentmonkey.github.io) and [Greasemonkey](https://www.greasespot.net) on Firefox 60,61,63 and Chromium 70. Tampermonkey was not tested due to its cumbersome legal status, see [https://tampermonkey.net/eula.php](https://tampermonkey.net/eula.php). 45 | 46 | ## Author 47 | 48 | This userscript is maintained by _cloux@rote.ch_ 49 | 50 | ## License 51 | 52 | This work is free. You can redistribute it and/or modify it under the terms of the Do What The Fuck You Want To Public License, Version 2, as published here: http://www.wtfpl.net/about. 53 | 54 | --- 55 | -------------------------------------------------------------------------------- /linksanitizer.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name Link Sanitizer 3 | // @description Clean up unnecessary hyperlink redirections and link shims 4 | // @version 1.1.9 5 | // @author cloux 6 | // @license WTFPL 2.0; http://www.wtfpl.net/about/ 7 | // @namespace https://github.com/cloux 8 | // @homepage https://github.com/cloux/LinkSanitizer 9 | // @supportURL https://github.com/cloux/LinkSanitizer 10 | // @updateURL https://raw.githubusercontent.com/cloux/LinkSanitizer/master/linksanitizer.user.js 11 | // @icon http://icons.iconarchive.com/icons/designbolts/seo/128/Natural-Link-icon.png 12 | // @include * 13 | // @run-at document-start 14 | // ==/UserScript== 15 | 16 | (function() { 17 | // Limit contentType to "text/plain" or "text/html" 18 | if ((document.contentType != undefined) && (document.contentType != "text/plain") && (document.contentType != "text/html")) { 19 | console.log("Hyperlink Sanitizer - Not loading for content type " + document.contentType); 20 | return; 21 | } 22 | // Sanitize single link 23 | function sanitize(weblink) { 24 | // skip non-http links 25 | if (! /^http/.test(weblink)) { 26 | return weblink; 27 | } 28 | // whitelisted services 29 | if (/google\.[a-z]*\/(ServiceLogin|Logout|AccountChooser)/.test(weblink) || // google login service 30 | /^https:\/\/translate\.google\./.test(weblink) || // Google translator 31 | /^http.*(login|registration)[./?].*http/.test(weblink) || // aliexpress, heise.de 32 | /\/oauth\?/.test(weblink) || // OAuth on aws.amazon.com 33 | /\/signin[/?]/.test(weblink) || // amazon.com, google, gmail 34 | /^https?:\/\/downloads\.sourceforge\.net\//.test(weblink) || // downloads.sourceforge.net 35 | /^https?:\/\/(www\.)?facebook\.com\/sharer/.test(weblink) || // share on FB 36 | /^https?:\/\/(www\.)?linkedin\.com\/share/.test(weblink) || // share on linkedin 37 | /^https?:\/\/(www\.)?twitter\.com\/(intent\/tweet|share)/.test(weblink) || // tweet link 38 | /^https?:\/\/(www\.)?pinterest\.com\/pin\/create\//.test(weblink) || // pinterest post 39 | /^https?:\/\/(www\.)?getpocket\.com\/save/.test(weblink) || // save link to pocket 40 | /^https?:\/\/[a-z.]*archive\.org\//.test(weblink) || // archive.org 41 | /^https?:\/\/github\.com\//.test(weblink) || // Github 42 | /^https:\/\/id\.atlassian\.com\//.test(weblink)) { // Atlassian Login 43 | return weblink; 44 | } 45 | console.log("Hyperlink: " + weblink); 46 | var strnew = weblink.replace(/^..*(https?(%3A|:)[^\\()&]*).*/, '$1'); 47 | strnew = strnew.replace(/%23/g, '#'); 48 | strnew = strnew.replace(/%26/g, '&'); 49 | strnew = strnew.replace(/%2F/g, '/'); 50 | strnew = strnew.replace(/%3A/g, ':'); 51 | strnew = strnew.replace(/%3D/g, '='); 52 | strnew = strnew.replace(/%3F/g, '?'); 53 | // NOTE: %25 must be translated last 54 | strnew = strnew.replace(/%25/g, '%'); 55 | console.log("SANITIZED: " + strnew); 56 | return strnew; 57 | } 58 | 59 | // MutationObserver callback 60 | function callback(mutationsList) { 61 | // Query for elements 62 | for (var mutation of mutationsList) { 63 | switch(mutation.type) { 64 | case "attributes": 65 | // Sanitize single mutated element 66 | if (/..https?(%3A|:)/.test(mutation.target.href)) { 67 | // Avoid infinite callback loops and set target href only if it would actually change 68 | var sanitizedLink = sanitize(mutation.target.href); 69 | if (mutation.target.href != sanitizedLink) { 70 | mutation.target.href = sanitizedLink; 71 | } 72 | } 73 | break; 74 | case "childList": 75 | // Sanitize all new elements 76 | for (var node of mutation.addedNodes) { 77 | if ((typeof node.href !== 'undefined') && (/..https?(%3A|:)/.test(node.href))) { 78 | node.href = sanitize(node.href); 79 | } 80 | } 81 | break; 82 | } 83 | } 84 | } 85 | 86 | // Create an observer instance linked to the callback function 87 | const MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver; 88 | var observer = new MutationObserver(callback); 89 | // Start observing added elements and changes of href attributes 90 | observer.observe(window.document.documentElement, { attributeFilter: [ "href" ], childList: true, subtree: true }); 91 | 92 | })(); 93 | --------------------------------------------------------------------------------