Stop Nefarious Redirects

Block unauthorized redirects and prevent history manipulation

Ekde 2024/05/15. Vidu La ĝisdata versio.

// ==UserScript==
// @name         Stop Nefarious Redirects
// @namespace    http://tampermonkey.net/
// @version      3.89
// @description  Block unauthorized redirects and prevent history manipulation
// @match        http://*/*
// @match        https://*/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_xmlhttpRequest
// @license      MIT
// @run-at       document-start
// ==/UserScript==

(function() {
    'use strict';

    // Manual blacklist
    const manualBlacklist = new Set([
        'getrunkhomuto.info'
    ]);

    // Function to get the current automated blacklist
    function getAutomatedBlacklist() {
        return new Set(GM_getValue('blacklist', []));
    }

    // Function to add a URL to the automated blacklist
    function addToAutomatedBlacklist(url) {
        let encodedUrl = encodeURIComponent(url);
        let blacklist = getAutomatedBlacklist();
        if (!blacklist.has(encodedUrl)) {
            blacklist.add(encodedUrl);
            GM_setValue('blacklist', Array.from(blacklist));
            console.log('Added to automated blacklist:', url);
        }
    }

    // Function to display the blacklist
    function displayBlacklist() {
        let automatedBlacklist = getAutomatedBlacklist();
        let fullBlacklist = new Set([...manualBlacklist, ...automatedBlacklist]);
        console.log('Current Blacklist:\n' + Array.from(fullBlacklist).map(decodeURIComponent).join('\n'));
        alert('Current Blacklist:\n' + Array.from(fullBlacklist).map(decodeURIComponent).join('\n'));
    }

    // Function to handle navigation events
    function handleNavigation(url) {
        try {
            if (!isUrlAllowed(url)) {
                console.error('Blocked navigation to:', url);
                addToAutomatedBlacklist(url); // Add the unauthorized URL to the automated blacklist
                if (lastKnownGoodUrl) {
                    window.location.replace(lastKnownGoodUrl);
                }
                return false;
            } else {
                console.log('Navigation allowed to:', url);
                lastKnownGoodUrl = url;
                return true;
            }
        } catch (error) {
            console.error('Error in handleNavigation:', error);
        }
    }

    let lastKnownGoodUrl = window.location.href;
    let navigationInProgress = false;

    // Proxy to intercept and handle location changes
    const locationProxy = new Proxy(window.location, {
        set(target, prop, value) {
            if ((prop === 'href' || prop === 'assign' || prop === 'replace') && !navigationInProgress) {
                if (!handleNavigation(value)) {
                    return false;
                }
            }
            return Reflect.set(target, prop, value);
        },
        get(target, prop) {
            if (prop === 'assign' || prop === 'replace') {
                return function(url) {
                    if (!navigationInProgress && handleNavigation(url)) {
                        navigationInProgress = true;
                        setTimeout(() => {
                            navigationInProgress = false;
                        }, 0);
                        return target[prop].call(target, url);
                    }
                };
            }
            return Reflect.get(target, prop);
        }
    });

    // Replace window.location with the proxy
    Object.defineProperty(window, 'location', {
        configurable: true,
        enumerable: true,
        get() {
            return locationProxy;
        }
    });

    // Monitor window.open() calls
    const originalOpen = window.open;
    window.open = function(url) {
        if (handleNavigation(url)) {
            return originalOpen.apply(this, arguments);
        }
    };

    // Monitor document.createElement('a') calls
    const originalCreateElement = document.createElement;
    document.createElement = function(tagName) {
        const element = originalCreateElement.call(document, tagName);
        if (tagName.toLowerCase() === 'a') {
            const originalSetAttribute = element.setAttribute;
            element.setAttribute = function(name, value) {
                if (name === 'href') {
                    if (!handleNavigation(value)) {
                        return;
                    }
                }
                return originalSetAttribute.apply(this, arguments);
            };
        }
        return element;
    };

    // Enhanced navigation control for back/forward buttons
    window.addEventListener('popstate', function(event) {
        if (!navigationInProgress && !isUrlAllowed(window.location.href)) {
            console.error('Blocked navigation to:', window.location.href);
            navigationInProgress = true;
            setTimeout(() => {
                navigationInProgress = false;
            }, 0);
            history.pushState(null, "", lastKnownGoodUrl); // Push the last known good URL
            window.location.replace(lastKnownGoodUrl); // Force redirect to last known good URL
            event.preventDefault();
        }
    });

    // Function to handle history manipulation
    function handleHistoryManipulation(originalMethod, data, title, url) {
        if (!isUrlAllowed(url)) {
            console.error('Blocked history manipulation to:', url);
            return;
        }
        return originalMethod.call(history, data, title, url);
    }

    // Wrap history.pushState and history.replaceState
    const originalPushState = history.pushState;
    const originalReplaceState = history.replaceState;

    history.pushState = function(data, title, url) {
        return handleHistoryManipulation(originalPushState, data, title, url);
    };

    history.replaceState = function(data, title, url) {
        return handleHistoryManipulation(originalReplaceState, data, title, url);
    };

    // Ensure we have a state to go back to if needed
    if (history.length === 1) {
        // Directly landed on this page, fake history
        history.replaceState(null, "", "/");
        history.pushState(null, "", window.location.href);
    }

    // Function to check if a URL is allowed based on the blacklist
    function isUrlAllowed(url) {
        let encodedUrl = encodeURIComponent(url);
        let automatedBlacklist = getAutomatedBlacklist();
        let isBlocked = Array.from(manualBlacklist).some(blockedUrl => encodedUrl.includes(blockedUrl)) ||
                        Array.from(automatedBlacklist).some(blockedUrl => encodedUrl.includes(blockedUrl));
        if (isBlocked) {
            console.log(`Blocked URL: ${url}`);
        }
        return !isBlocked;
    }

    console.log('Redirect control script with blacklist initialized.');
})();