Stop Nefarious Redirects

Block unauthorized redirects and prevent history manipulation

目前為 2024-05-25 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

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

    console.log('Script initialization started.');

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

    // List of allowed popups domains
    const allowedPopups = [
            '500px.com', 'accuweather.com', 'adobe.com', 'adulttime.com', 'alibaba.com', 'amazon.com', 'amazonaws.com',
            'amd.com', 'americanexpress.com', 'anaconda.com', 'angular.io', 'ap.org', 'apache.org', 'apnews.com',
            'apple.com', 'arstechnica.com', 'artstation.com', 'asana.com', 'asus.com', 'atlassian.com', 'autodesk.com',
            'axios.com', 'battle.net', 'bbc.co.uk', 'bbc.com', 'behance.net', 'bestbuy.com', 'bing.com', 'bitbucket.org',
            'blogger.com', 'bloomberg.com', 'bmw.com', 'boeing.com', 'booking.co.uk', 'booking.com', 'bootstrapcdn.com',
            'breitbart.com', 'buzzfeed.com', 'canva.com', 'capitalone.com', 'cbsnews.com', 'character.ai', 'chase.com',
            'chaturbate.com', 'cisco.com', 'citi.com', 'cnbc.com', 'cnet.com', 'cnn.com', 'codecademy.com', 'constantcontact.com',
            'coursera.org', 'craigslist.org', 'dailymail.co.uk', 'dell.com', 'deviantart.com', 'discord.com', 'disney.com',
            'django.com', 'docker.com', 'docusign.com', 'dribbble.com', 'dropbox.com', 'duckduckgo.com', 'duolingo.com',
            'duosecurity.com', 'ebay.com', 'economist.com', 'edx.org', 'elsevier.com', 'engadget.com', 'epicgames.com',
            'eporner.com', 'espn.com', 'etsy.com', 'eurogamer.net', 'expedia.com', 'facebook.com', 'fandom.com', 'fedex.com',
            'figma.com', 'finance.yahoo.com', 'flickr.com', 'flipkart.com', 'forbes.com', 'foxnews.com', 'framer.com',
            'freecodecamp.org', 'gamespot.com', 'gartner.com', 'gettyimages.com', 'git-scm.com', 'github.com', 'gizmodo.com',
            'go.com', 'godaddy.com', 'gog.com', 'goldmansachs.com', 'google.com', 'healthline.com', 'hilton.com', 'homedepot.com',
            'hp.com', 'hubspot.com', 'huffpost.com', 'hulu.com', 'humblebundle.com', 'ibm.com', 'ieee.org', 'ifixit.com',
            'ign.com', 'ikea.com', 'imdb.com', 'imgur.com', 'indeed.com', 'instagram.com', 'instructure.com', 'intel.com',
            'intuit.com', 'invisionapp.com', 'itch.io', 'java.com', 'jetbrains.com', 'joomla.org', 'jquery.com', 'khanacademy.org',
            'kotaku.com', 'kotlinlang.org', 'laravel.com', 'lenovo.com', 'lg.com', 'lifehacker.com', 'linkedin.com', 'live.com',
            'lowes.com', 'lynda.com', 'macys.com', 'mailchimp.com', 'marriott.com', 'mashable.com', 'masterclass.com',
            'mcdonalds.com', 'medium.com', 'mercedes-benz.com', 'microsoft.com', 'microsoftonline.commit.edu', 'mongodb.com',
            'moodle.org', 'mozilla.org', 'msn.com', 'msnbc.com', 'nasa.gov', 'nationalgeographic.com', 'nbc.com', 'nbcnews.com',
            'netflix.com', 'new.reddit.com', 'nextdoor.com', 'nih.gov', 'npr.org', 'nvidia.comnypost.com', 'nytimes.com',
            'office.com', 'okta.com', 'onlyfans.com', 'openai.com', 'oracle.com', 'oreilly.com', 'origin.com', 'outlook.com',
            'overstock.com', 'patreon.com', 'paypal.com', 'pcgamer.com', 'pexels.com', 'php.net', 'pinterest.com', 'pixabay.com',
            'pluralsight.com', 'polygon.com', 'pornhub.com', 'python.org', 'quizlet.com', 'quora.com', 'reactjs.org', 'realtor.com',
            'reddit.com', 'redhat.com', 'roblox.com', 'rubyonrails.org', 'salesforce.com', 'samsung.co.kr', 'samsung.com',
            'sap.com', 'sciencedirect.com', 'scopus.com', 'sears.com', 'sharepoint.com', 'shutterstock.com', 'siemens.com',
            'sketch.com', 'skillshare.com', 'skype.com', 'slack.com', 'sony.com', 'soundcloud.com', 'spotify.com', 'spring.io',
            'stackoverflow.com', 'steamcommunity.com', 'steampowered.com', 'surveymonkey.com', 'symantec.com', 'target.com',
            'techcrunch.com', 'temu.com', 'tesla.com', 'texasinstruments.com', 'theguardian.com', 'thenextweb.com', 'theverge.com',
            'tiktok.com', 'time.com', 'toyota.com', 'trello.com', 'trip.com', 'tripadvisor.com', 'tumblr.com', 'twitch.tv',
            'twitter.com', 'uber.com', 'ucla.edu', 'ucsf.edu', 'udemy.com', 'unity.com', 'unsplash.com', 'ups.com', 'usatoday.com',
            'usnews.com', 'usps.com', 'verizon.com', 'Vice.com', 'vice.com', 'vimeo.com', 'vk.com', 'vmware.com', 'volkswagen.com',
            'vox.com', 'walmart.com', 'washingtonpost.com', 'weather.com', 'weather.gov', 'webmd.com', 'whatsapp.com', 'wikimedia.org',
            'wikipedia.org', 'wired.com', 'wordpress.com', 'wsj.com', 'wunderground.com', 'x.com', 'xerox.com', 'xfinity.com',
            'xhamster.com', 'xilinx.com', 'xnxx.com', 'xvideos.com', 'yahoo.com', 'yelp.com', 'youtube.com', 'zapier.com', 'zendesk.com',
            'zeplin.iozillow.com', 'zoom.us'
    ];

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

    // Save the original assign and open methods
    const originalAssign = window.location.assign.bind(window.location);
    const originalOpen = window.open;

    console.log('Original window.location.assign and window.open saved.');

    // Override the assign method to monitor and control redirects
    window.location.assign = function(url) {
        console.log('Redirect attempt detected:', url);
        if (!allowedPopups.some(domain => url.includes(domain)) && !handleNavigation(url)) {
            console.log('Redirect to undesired domain blocked:', url);
            return; // Block the redirect
        }
        console.log('Redirect allowed to:', url);
        return originalAssign(url);
    };

    console.log('window.location.assign overridden with custom logic.');

    // Override window.open to control popup behavior
    window.open = function(url, name, features) {
        console.log('Popup attempt detected:', url);
        if (allowedPopups.some(domain => url.includes(domain))) {
            console.log('Popup allowed for:', url);
            return originalOpen(url, name, features); // Allow popup
        }
        console.log('Blocked a popup from:', url);
        return null; // Block the popup
    };

    console.log('window.open overridden with custom logic.');

    // Proxy to intercept and handle location changes
    let lastKnownGoodUrl = window.location.href;
    let navigationInProgress = false;

    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;
        }
    });

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