Greasy Fork

Greasy Fork is available in English.

Search Enhanced Protection (Tampermonkey)

Enhance privacy when using search engines: strip tracking params, remove redirect wrappers, set no-referrer, force HTTPS on links, and add noreferrer/noopener. Works on Google, Bing, DuckDuckGo, Yahoo and common search pages. Toggle features from the Tampermonkey menu.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Search Enhanced Protection (Tampermonkey) 
// @namespace    http://tampermonkey.net/
// @version      1.3
// @description  Enhance privacy when using search engines: strip tracking params, remove redirect wrappers, set no-referrer, force HTTPS on links, and add noreferrer/noopener. Works on Google, Bing, DuckDuckGo, Yahoo and common search pages. Toggle features from the Tampermonkey menu.
// @author       Skibidi555
// @match        *://*.google.*/*
// @match        *://*.bing.com/*
// @match        *://*.duckduckgo.com/*
// @match        *://search.yahoo.com/*
// @match        *://*.brave.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @run-at       document-idle
// @license      You can only change the name 
// ==/UserScript==

(function () {
    'use strict';
    const DEBUG = false;

    // ---- Configuration (persistent via GM_setValue with defaults) ----
    const defaults = {
        stripTrackingParams: true,
        fixRedirectLinks: true,
        setNoReferrerMeta: true,
        forceHTTPS: true,
        addNoreferrerRel: true
    };

    // Load/save helpers (use GM_getValue default arg to avoid undefined)
    function getOpt(key) {
        try {
            return GM_getValue(key, defaults[key]);
        } catch (e) {
            // fallback - Tampermonkey should support GM_getValue with default but be defensive
            return (typeof window['__sep_defaults__'] !== 'undefined' && key in window["__sep_defaults__"])
                ? window['__sep_defaults__'][key] : defaults[key];
        }
    }
    function setOpt(key, val) {
        try {
            GM_setValue(key, !!val);
            if (DEBUG) console.info('SEP: setOpt', key, !!val);
        } catch (e) {
            if (DEBUG) console.warn('SEP: GM_setValue failed', e);
        }
    }

    // Register menu commands to toggle features (re-run without reload)
    function registerMenu() {
        try {
            GM_registerMenuCommand('Toggle stripTrackingParams (' + (getOpt('stripTrackingParams') ? 'ON' : 'OFF') + ')', () => {
                setOpt('stripTrackingParams', !getOpt('stripTrackingParams'));
                runProtection(true);
            });
            GM_registerMenuCommand('Toggle fixRedirectLinks (' + (getOpt('fixRedirectLinks') ? 'ON' : 'OFF') + ')', () => {
                setOpt('fixRedirectLinks', !getOpt('fixRedirectLinks'));
                runProtection(true);
            });
            GM_registerMenuCommand('Toggle setNoReferrerMeta (' + (getOpt('setNoReferrerMeta') ? 'ON' : 'OFF') + ')', () => {
                setOpt('setNoReferrerMeta', !getOpt('setNoReferrerMeta'));
                runProtection(true);
            });
            GM_registerMenuCommand('Toggle forceHTTPS (' + (getOpt('forceHTTPS') ? 'ON' : 'OFF') + ')', () => {
                setOpt('forceHTTPS', !getOpt('forceHTTPS'));
                runProtection(true);
            });
            GM_registerMenuCommand('Toggle addNoreferrerRel (' + (getOpt('addNoreferrerRel') ? 'ON' : 'OFF') + ')', () => {
                setOpt('addNoreferrerRel', !getOpt('addNoreferrerRel'));
                runProtection(true);
            });
        } catch (e) {
            if (DEBUG) console.warn('SEP: registerMenu failed', e);
        }
    }

    registerMenu();

    // ---- Utilities ----
    const TRACKING_PARAMS = [
        /^utm_/i,
        /^gclid$/i,
        /^fbclid$/i,
        /^mc_cid$/i,
        /^mc_eid$/i,
        /^igshid$/i,
        /^ref$/i,
        /^ref_src$/i,
        /^msclkid$/i,
        /^trk_?/i,
        /^icid$/i,
        /^_hsenc$/i,
        /^_hsmi$/i
    ];

    function isTrackingParam(key) {
        return TRACKING_PARAMS.some(rx => rx.test(key));
    }

    function stripTrackingParamsFromSearchParams(sp) {
        const toDelete = [];
        for (const key of sp.keys()) {
            if (isTrackingParam(key)) toDelete.push(key);
        }
        for (const k of toDelete) sp.delete(k);
    }

    function cleanUrlString(urlStr) {
        try {
            // resolve relative URLs
            const u = new URL(urlStr, document.baseURI);

            if (getOpt('stripTrackingParams')) {
                stripTrackingParamsFromSearchParams(u.searchParams);
                if (u.hash && u.hash.includes('=')) {
                    try {
                        const h = new URLSearchParams(u.hash.replace(/^#/, ''));
                        let changed = false;
                        for (const key of Array.from(h.keys())) {
                            if (isTrackingParam(key)) { h.delete(key); changed = true; }
                        }
                        if (changed) u.hash = h.toString() ? '#' + h.toString() : '';
                    } catch (e) { /* ignore malformed hash */ }
                }
            }

            if (getOpt('forceHTTPS') && u.protocol === 'http:') {
                if (/\.(com|org|net|io|gov|edu|co|uk|de|fr|es|nl|ca|au|info|me)$/i.test(u.hostname)) {
                    u.protocol = 'https:';
                }
            }
            return u.toString();
        } catch (e) {
            if (DEBUG) console.warn('SEP: cleanUrlString parse fail for', urlStr, e);
            return urlStr;
        }
    }

    function unwrapGoogleRedirect(href) {
        try {
            const u = new URL(href, document.baseURI);
            // common redirect endpoints
            if ((/\/url$/i).test(u.pathname) && u.searchParams.has('q')) {
                return u.searchParams.get('q');
            }
            for (const p of ['url', 'u', 'q']) {
                if (u.searchParams.has(p)) return u.searchParams.get(p);
            }
        } catch (e) {
            if (DEBUG) console.debug('SEP: unwrap failed', href, e);
        }
        return href;
    }

    function addRel(existing, addition) {
        const set = new Set((existing || '').split(/\s+/).filter(Boolean));
        addition.split(/\s+/).forEach(t => set.add(t));
        return Array.from(set).join(' ');
    }

    function cleanAnchor(a) {
        try {
            if (!(a instanceof HTMLAnchorElement)) return;
            const orig = a.getAttribute('href');
            if (!orig) return;

            if (/^\s*(javascript:|#|mailto:|tel:)/i.test(orig)) {
                if (getOpt('addNoreferrerRel')) a.rel = addRel(a.rel, 'noreferrer noopener');
                return;
            }

            let cleaned = orig;

            if (getOpt('fixRedirectLinks')) {
                cleaned = unwrapGoogleRedirect(cleaned);
            }

            cleaned = cleanUrlString(cleaned);

            if (cleaned !== orig) {
                a.setAttribute('data-sep-cleaned-href', '1');
                a.setAttribute('href', cleaned);
            }

            if (getOpt('addNoreferrerRel')) {
                a.rel = addRel(a.rel, 'noreferrer noopener');
            }
        } catch (e) {
            if (DEBUG) console.warn('SEP: cleanAnchor error', e);
        }
    }

    function processAnchors(root = document) {
        try {
            const anchors = (root && root.querySelectorAll) ? root.querySelectorAll('a[href]') : [];
            for (const a of anchors) {
                if (a.getAttribute('data-sep-cleaned-href') === '1') continue;
                cleanAnchor(a);
            }
        } catch (e) {
            if (DEBUG) console.warn('SEP: processAnchors error', e);
        }
    }

    // prevent handling repeated clicks on same link (e.g., search engine handlers)
    let recentlyIntercepted = new WeakSet();

    function installClickInterceptor() {
        document.addEventListener('click', function (ev) {
            try {
                // ignore if user used modifier keys to open context or special clicks
                if (ev.defaultPrevented) return;
                const target = ev.target;
                if (!target || typeof target.closest !== 'function') return;
                const a = target.closest('a[href]');
                if (!a) return;
                if (recentlyIntercepted.has(a)) return;

                const href = a.getAttribute('href');
                if (!href || /^\s*(javascript:|#)/i.test(href)) return;

                if (getOpt('fixRedirectLinks') || getOpt('stripTrackingParams')) {
                    const cleaned = cleanUrlString(unwrapGoogleRedirect(href));
                    if (cleaned && cleaned !== href) {
                        // middle click / ctrl/meta should open in new tab
                        const openInNew = (ev.button === 1) || ev.ctrlKey || ev.metaKey || a.target === '_blank';
                        ev.preventDefault();
                        ev.stopPropagation();
                        recentlyIntercepted.add(a);
                        if (openInNew) {
                            // open with no opener/referrer where possible
                            try { window.open(cleaned, '_blank', 'noopener,noreferrer'); }
                            catch (e) { window.open(cleaned, '_blank'); }
                        } else {
                            location.href = cleaned;
                        }
                        // remove mark after short time to allow normal subsequent clicks
                        setTimeout(() => { recentlyIntercepted.delete(a); }, 1000);
                    }
                }
            } catch (e) {
                if (DEBUG) console.warn('SEP: click interceptor error', e);
            }
        }, true);
    }

    function setNoReferrerMeta() {
        if (!getOpt('setNoReferrerMeta')) return;
        try {
            let meta = document.querySelector('meta[name="referrer"]');
            if (!meta) {
                meta = document.createElement('meta');
                meta.name = 'referrer';
                if (document.head) {
                    document.head.prepend(meta);
                } else if (document.documentElement) {
                    // fallback - append to documentElement if head absent (rare)
                    document.documentElement.insertBefore(meta, document.documentElement.firstChild);
                }
            }
            meta.setAttribute('content', 'no-referrer');
        } catch (e) {
            if (DEBUG) console.warn('SEP: setNoReferrerMeta failed', e);
        }
    }

    let observer = null;
    function observeAndClean() {
        try {
            if (observer) observer.disconnect();
            observer = new MutationObserver((mutations) => {
                for (const m of mutations) {
                    if (m.addedNodes && m.addedNodes.length) {
                        for (const n of m.addedNodes) {
                            if (n.nodeType === 1) {
                                if (n.querySelector && n.querySelector('a[href]')) {
                                    processAnchors(n);
                                } else if (n.matches && n.matches('a[href]')) {
                                    cleanAnchor(n);
                                }
                            }
                        }
                    }
                }
            });
            observer.observe(document.documentElement || document.body, { childList: true, subtree: true });
            processAnchors(document);
        } catch (e) {
            if (DEBUG) console.warn('SEP: observeAndClean failed', e);
        }
    }

    function runProtection(forceReprocess = false) {
        try {
            if (getOpt('setNoReferrerMeta')) setNoReferrerMeta();
            if (getOpt('fixRedirectLinks') || getOpt('stripTrackingParams') || getOpt('addNoreferrerRel') || getOpt('forceHTTPS')) {
                if (forceReprocess) processAnchors(document);
                installClickInterceptor();
                observeAndClean();
            }
            if (DEBUG) console.info('Search Enhanced Protection: active (features: ' +
                (getOpt('stripTrackingParams') ? 'stripTrackingParams ' : '') +
                (getOpt('fixRedirectLinks') ? 'fixRedirectLinks ' : '') +
                (getOpt('setNoReferrerMeta') ? 'setNoReferrerMeta ' : '') +
                (getOpt('forceHTTPS') ? 'forceHTTPS ' : '') +
                (getOpt('addNoreferrerRel') ? 'addNoreferrerRel ' : '') + ')');
        } catch (e) {
            if (DEBUG) console.error('SEP: runProtection failed', e);
        }
    }

    runProtection();

    // SPA navigation hooks
    (function hijackHistory() {
        try {
            const push = history.pushState;
            const replace = history.replaceState;
            history.pushState = function () {
                push.apply(this, arguments);
                setTimeout(() => runProtection(true), 200);
            };
            history.replaceState = function () {
                replace.apply(this, arguments);
                setTimeout(() => runProtection(true), 200);
            };
            window.addEventListener('popstate', () => setTimeout(() => runProtection(true), 200));
        } catch (e) {
            if (DEBUG) console.warn('SEP: hijackHistory failed', e);
        }
    })();

})();