Stop Nefarious Redirects

Block unauthorized redirects and prevent history manipulation

当前为 2024-05-15 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Stop Nefarious Redirects
// @namespace    http://tampermonkey.net/
// @version      3.88
// @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);
    }

    // Keyboard shortcut listener to display the blacklist
    document.addEventListener('keydown', function(e) {
        if (e.altKey && e.key === '2') {
            e.preventDefault();
            displayBlacklist();
        }
    });

    // 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.');
})();