// ==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',
// Add other domains as necessary...
'zoom.us', 'new.reddit.com'
];
// 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.');
})();