Greasy Fork

Greasy Fork is available in English.

SpyScan

发现网站上的跟踪脚本、指纹识别和监控技术。

当前为 2025-11-29 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         SpyScan
// @namespace    http://greasyfork.icu/fr/users/1451802
// @version      2.1
// @description  Uncover tracking scripts, fingerprinting, and surveillance tactics lurking on the websites you visit
// @description:de Untersuche Websites auf Tracking-Skripte, Fingerprinting und Überwachungsmethoden.
// @description:es Descubre scripts de seguimiento, técnicas de huellas digitales y tácticas de vigilancia en las páginas web que visitas.
// @description:fr Détecte les scripts de suivi, le fingerprinting et les techniques de surveillance cachées sur les sites que vous visitez.
// @description:it Scopri script di tracciamento, tecniche di fingerprinting e metodi di sorveglianza sui siti web che visiti.
// @description:ru Раскрывает трекинговые скрипты, отпечатки браузера и методы слежки на посещаемых сайтах.
// @description:zh-CN 发现网站上的跟踪脚本、指纹识别和监控技术。
// @description:zh-TW 發現網站上的追蹤腳本、指紋辨識和監控技術。
// @description:ja 訪問したサイトに潜むトラッキングスクリプト、フィンガープリント、監視技術を検出。
// @description:ko 방문한 웹사이트에서 추적 스크립트, 브라우저 지문, 감시 기술을 찾아냅니다.
// @author       NormalRandomPeople (https://github.com/NormalRandomPeople)
// @match        *://*/*
// @grant        GM_addStyle
// @license      MIT
// @icon         https://www.svgrepo.com/show/360090/analyse.svg
// @compatible      chrome
// @compatible      firefox
// @compatible      opera
// @compatible      edge
// @compatible      brave
// @run-at document-end
// ==/UserScript==

/* jshint esversion: 11 */

(function() {
    'use strict';

    let detectedETags = [], detectedIPGeolocationRequests = [], detectedWebRTCUsage = [];
    let detectedCanvasFingerprinting = [], detectedWebGLFingerprinting = [], detectedAudioFingerprinting = [];
    let detectedFontFingerprinting = [], detectedScreenFingerprinting = [], detectedBatteryFingerprinting = [];
    let detectedMediaDevices = [], detectedSensors = [], detectedServiceWorkers = [];
    let detectedCacheAPI = [], detectedWebSQL = [], detectedFileSystem = [], detectedThirdPartyIframes = [];

    (function() {
        const methods = {
            toDataURL: HTMLCanvasElement.prototype.toDataURL,
            toBlob: HTMLCanvasElement.prototype.toBlob,
            getImageData: CanvasRenderingContext2D.prototype.getImageData
        };

        ['toDataURL', 'toBlob'].forEach(method => {
            HTMLCanvasElement.prototype[method] = function(...args) {
                detectedCanvasFingerprinting.push({method, timestamp: Date.now(), stack: new Error().stack});
                return methods[method].apply(this, args);
            };
        });

        CanvasRenderingContext2D.prototype.getImageData = function(...args) {
            detectedCanvasFingerprinting.push({method: 'getImageData', timestamp: Date.now(), stack: new Error().stack});
            return methods.getImageData.apply(this, args);
        };
    })();

    (function() {
        const sensitiveParams = ['VENDOR', 'RENDERER', 'VERSION', 'SHADING_LANGUAGE_VERSION', 37445, 37446];

        [WebGLRenderingContext, window.WebGL2RenderingContext].filter(Boolean).forEach(ctx => {
            const original = ctx.prototype.getParameter;
            ctx.prototype.getParameter = function(param) {
                if (sensitiveParams.includes(param) || sensitiveParams.includes(this[param])) {
                    detectedWebGLFingerprinting.push({parameter: param, timestamp: Date.now(), stack: new Error().stack});
                }
                return original.apply(this, arguments);
            };
        });
    })();

    (function() {
        const OriginalAudioContext = window.AudioContext || window.webkitAudioContext;
        if (OriginalAudioContext) {
            window.AudioContext = window.webkitAudioContext = function(...args) {
                detectedAudioFingerprinting.push({method: 'AudioContext created', timestamp: Date.now(), stack: new Error().stack});
                return new OriginalAudioContext(...args);
            };
        }
    })();

    (function() {
        const original = CanvasRenderingContext2D.prototype.measureText;
        let count = 0;
        CanvasRenderingContext2D.prototype.measureText = function(...args) {
            if (++count > 10) {
                detectedFontFingerprinting.push({method: 'measureText', count, timestamp: Date.now(), stack: new Error().stack});
                count = 0;
            }
            return original.apply(this, args);
        };
    })();

    (function() {
        let count = 0;
        ['width', 'height', 'availWidth', 'availHeight', 'colorDepth', 'pixelDepth'].forEach(prop => {
            const desc = Object.getOwnPropertyDescriptor(Screen.prototype, prop) || Object.getOwnPropertyDescriptor(window.screen, prop);
            if (desc?.get) {
                Object.defineProperty(Screen.prototype, prop, {
                    get: function() {
                        if (++count > 5) {
                            detectedScreenFingerprinting.push({property: prop, timestamp: Date.now(), stack: new Error().stack});
                            count = 0;
                        }
                        return desc.get.call(this);
                    }
                });
            }
        });
    })();

    if (navigator.getBattery) {
        const original = navigator.getBattery;
        navigator.getBattery = function(...args) {
            detectedBatteryFingerprinting.push({method: 'getBattery', timestamp: Date.now(), stack: new Error().stack});
            return original.apply(this, args);
        };
    }

    if (navigator.mediaDevices?.enumerateDevices) {
        const original = navigator.mediaDevices.enumerateDevices;
        navigator.mediaDevices.enumerateDevices = function(...args) {
            detectedMediaDevices.push({method: 'enumerateDevices', timestamp: Date.now()});
            return original.apply(this, args);
        };
    }

    (function() {
        ['Accelerometer', 'Gyroscope', 'Magnetometer', 'AbsoluteOrientationSensor', 'RelativeOrientationSensor'].forEach(type => {
            if (window[type]) {
                const Original = window[type];
                window[type] = function(...args) {
                    detectedSensors.push({type, timestamp: Date.now()});
                    return new Original(...args);
                };
            }
        });

        ['deviceorientation', 'devicemotion'].forEach(type => {
            if (window[type === 'deviceorientation' ? 'DeviceOrientationEvent' : 'DeviceMotionEvent']) {
                window.addEventListener(type, function() {
                    if (!detectedSensors.some(s => s.type === type)) {
                        detectedSensors.push({type, timestamp: Date.now()});
                    }
                }, true);
            }
        });
    })();

    if (navigator.serviceWorker) {
        const original = navigator.serviceWorker.register;
        navigator.serviceWorker.register = function(...args) {
            detectedServiceWorkers.push({url: args[0], timestamp: Date.now()});
            return original.apply(this, args);
        };
    }

    if (window.caches) {
        const original = caches.open;
        caches.open = function(...args) {
            detectedCacheAPI.push({cacheName: args[0], timestamp: Date.now()});
            return original.apply(this, args);
        };
    }

    if (window.openDatabase) {
        const original = window.openDatabase;
        window.openDatabase = function(...args) {
            detectedWebSQL.push({dbName: args[0], timestamp: Date.now()});
            return original.apply(this, args);
        };
    }

    if (window.webkitRequestFileSystem) {
        const original = window.webkitRequestFileSystem;
        window.webkitRequestFileSystem = function(...args) {
            detectedFileSystem.push({type: args[0], timestamp: Date.now()});
            return original.apply(this, args);
        };
    }

    const ipGeoServices = ["ipinfo.io", "ip-api.com", "ipgeolocation.io", "geoip-db.com", "freegeoip.app", "ip2location.com", "extreme-ip-lookup.com", "ip-geolocation.whoisxmlapi.com", "ipligence.com", "bigdatacloud.com", "maxmind.com", "db-ip.com", "ipinfodb.com", "ipdata.co", "abstractapi.com", "ipapi.com", "ipstack.com", "geo.ipify.org", "ipwhois.io", "ipregistry.co", "telize.com", "geoplugin.com", "api.iplocation.net", "geolocation-db.com", "ipapi.co"];
    const geoPatterns = [/\/geo/i, /\/location/i, /\/ip.*location/i, /\/geoip/i, /\/country/i, /\/city/i, /\/region/i, /geolocation/i, /whereami/i, /mylocation/i];

    function isGeolocationRequest(url) {
        try {
            const urlObj = new URL(url);
            const hostname = urlObj.hostname.toLowerCase();
            const pathname = urlObj.pathname.toLowerCase();
            const fullUrl = url.toLowerCase();

            if (ipGeoServices.some(s => hostname.includes(s) || fullUrl.includes(s))) return true;

            return geoPatterns.some(p => (p.test(pathname) || p.test(fullUrl)) &&
                (/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/.test(fullUrl) || fullUrl.includes('json') || fullUrl.includes('api') || pathname.includes('geo') || pathname.includes('location')));
        } catch (e) {
            return false;
        }
    }

    const originalFetch = window.fetch;
    window.fetch = async function(...args) {
        const reqUrl = typeof args[0] === "string" ? args[0] : args[0]?.url;
        if (reqUrl && isGeolocationRequest(reqUrl)) {
            detectedIPGeolocationRequests.push({url: reqUrl});
        }

        const response = await originalFetch.apply(this, args);
        const responseClone = response.clone();
        try {
            const etag = responseClone.headers.get("ETag");
            if (etag) detectedETags.push({url: responseClone.url, etag});
        } catch (err) {}
        return response;
    };

    const originalXHROpen = XMLHttpRequest.prototype.open;
    XMLHttpRequest.prototype.open = function(...args) {
        const reqUrl = args[1];
        if (reqUrl && isGeolocationRequest(reqUrl)) {
            detectedIPGeolocationRequests.push({url: reqUrl});
        }

        this.addEventListener("readystatechange", function() {
            if (this.readyState === 4) {
                try {
                    const etag = this.getResponseHeader("ETag");
                    if (etag) detectedETags.push({url: this.responseURL, etag});
                } catch (err) {}
            }
        });
        return originalXHROpen.apply(this, args);
    };

    const scanButton = document.createElement("button");
    scanButton.id = "aptScanButton";
    scanButton.innerHTML = '<svg width="32" height="32" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" style="display:block;margin:0 auto"><path d="M21 21L15 15M17 10C17 13.866 13.866 17 10 17C6.13401 17 3 13.866 3 10C3 6.13401 6.13401 3 10 3C13.866 3 17 6.13401 17 10Z" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M10 7V10L12 12" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>';
    scanButton.style.cssText = "position:fixed!important;bottom:10px!important;left:10px!important;padding:15px 20px!important;border:none!important;background-color:black!important;color:#fff!important;border-radius:10px!important;cursor:pointer!important;z-index:2147483647!important;box-shadow:0 4px 8px rgba(0,0,0,0.3)!important;transition:background-color 0.3s,transform 0.3s!important;font-family:Arial,sans-serif!important;font-size:18px!important;line-height:1!important;text-align:center!important;min-width:auto!important;min-height:auto!important;max-width:none!important;max-height:none!important;margin:0!important";

    scanButton.addEventListener("mouseover", () => {
        scanButton.style.backgroundColor = "#333";
        scanButton.style.transform = "scale(1.05)";
    });
    scanButton.addEventListener("mouseout", () => {
        scanButton.style.backgroundColor = "black";
        scanButton.style.transform = "scale(1)";
    });

    document.body.appendChild(scanButton);

    const auditWindow = document.createElement("div");
    auditWindow.id = "aptAuditWindow";
    auditWindow.style.cssText = "display:none!important;position:fixed!important;top:0!important;left:0!important;width:100%!important;height:100%!important;background-color:rgba(0,0,0,0.7)!important;color:#fff!important;font-family:Arial,sans-serif!important;overflow:auto!important;padding:20px!important;z-index:2147483646!important;box-sizing:border-box!important;margin:0!important;border:none!important";

    const windowContent = document.createElement("div");
    windowContent.className = "aptWindowContent";
    windowContent.style.cssText = "max-width:800px!important;margin:50px auto!important;background-color:#333!important;border-radius:8px!important;padding:20px!important;box-shadow:0 0 20px rgba(0,0,0,0.5)!important;overflow-y:auto!important;max-height:80%!important;box-sizing:border-box!important;color:#fff!important;font-family:Arial,sans-serif!important;font-size:16px!important;line-height:1.5!important";

    auditWindow.appendChild(windowContent);
    document.body.appendChild(auditWindow);

    auditWindow.addEventListener("click", e => {
        if (e.target === auditWindow) auditWindow.style.display = "none";
    });

    GM_addStyle(`#aptAuditWindow *{box-sizing:border-box!important;font-family:Arial,sans-serif!important;color:#fff!important;margin:0!important;padding:0!important;border:none!important;background:transparent!important;text-decoration:none!important;text-transform:none!important;letter-spacing:normal!important;word-spacing:normal!important;line-height:1.5!important}#aptAuditWindow .aptWindowContent{max-width:800px!important;margin:50px auto!important;background-color:#333!important;border-radius:8px!important;padding:20px!important;box-shadow:0 0 20px rgba(0,0,0,0.5)!important;overflow-y:auto!important;max-height:80%!important}#aptAuditWindow .aptWindowContent h2{text-align:center!important;margin-bottom:20px!important;margin-top:0!important;font-size:1.8em!important;font-weight:bold!important;padding:0!important}#aptAuditWindow .aptWindowContent p{font-size:1em!important;line-height:1.5!important;margin:10px 0!important;padding:0!important}#aptAuditWindow .aptWindowContent ul{list-style-type:none!important;padding:0!important;margin:0!important}#aptAuditWindow .aptWindowContent li{background-color:#444!important;padding:10px!important;margin:5px 0!important;border-radius:5px!important;word-wrap:break-word!important;position:relative!important;display:block!important}#aptAuditWindow .aptTitle{font-weight:bold!important;font-family:Arial,sans-serif!important;color:#fff!important}#aptAuditWindow .aptSectionTitle{font-size:1.3em!important;font-weight:bold!important;margin-bottom:10px!important;margin-top:20px!important;padding-bottom:5px!important;padding-top:0!important;padding-left:0!important;padding-right:0!important;border-bottom:2px solid #666!important;color:#fff!important;display:block!important}#aptAuditWindow .aptDangerLevel{font-weight:bold!important;font-size:1.1em!important}#aptAuditWindow .aptDangerLevelLow{color:#28A745!important}#aptAuditWindow .aptDangerLevelMedium{color:#FFA500!important}#aptAuditWindow .aptDangerLevelHigh{color:#FF4C4C!important}#aptAuditWindow .aptloading-spinner{border:4px solid rgba(255,255,255,0.3)!important;border-top:4px solid #fff!important;border-radius:50%!important;width:40px!important;height:40px!important;animation:spin 1s linear infinite!important;margin:20px auto!important;display:block!important}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}`);

    function getCookies() {
        return document.cookie.split(';').map(c => c.trim()).filter(c => c);
    }

    (function() {
        if (window.RTCPeerConnection) {
            const OriginalRTC = window.RTCPeerConnection;
            window.RTCPeerConnection = function(...args) {
                const stack = new Error().stack;
                detectedWebRTCUsage.push({
                    timestamp: Date.now(),
                    stack: stack,
                    config: args[0]
                });
                return new OriginalRTC(...args);
            };
            window.RTCPeerConnection.prototype = OriginalRTC.prototype;
        }

        ['webkitRTCPeerConnection', 'mozRTCPeerConnection'].forEach(variant => {
            if (window[variant]) {
                const Original = window[variant];
                window[variant] = function(...args) {
                    detectedWebRTCUsage.push({
                        timestamp: Date.now(),
                        stack: new Error().stack,
                        config: args[0]
                    });
                    return new Original(...args);
                };
                window[variant].prototype = Original.prototype;
            }
        });
    })();

    function detectWebRTCTracking() {
        if (detectedWebRTCUsage.length > 0) {
            return [{
                name: "WebRTC IP Tracking",
                danger: "high",
                description: `Website is using WebRTC to potentially collect your real IP address. Detected ${detectedWebRTCUsage.length} RTCPeerConnection(s) created by the site.`
            }];
        }
        return [];
    }

    function detectWebBeacons() {
        return Array.from(document.getElementsByTagName("img"))
            .filter(img => {
                const w = img.getAttribute("width") || img.width;
                const h = img.getAttribute("height") || img.height;
                const cs = window.getComputedStyle(img);
                return (parseInt(w) === 1 && parseInt(h) === 1) || (img.naturalWidth === 1 && img.naturalHeight === 1) || (cs.width === "1px" && cs.height === "1px");
            })
            .map(img => ({name: "Web Beacon", src: img.src, danger: "medium", description: "Detected a 1x1 pixel image that could be used as a web beacon."}));
    }

    function detectEtagTracking() {
        return detectedETags.map(item => ({name: "ETag Tracking", danger: "medium", description: `ETag detected from ${item.url} with value ${item.etag}`}));
    }

    function detectIPGeolocation() {
        return detectedIPGeolocationRequests.map(item => ({name: "IP Geolocation", danger: "high", description: `IP Geolocation request detected to ${item.url}`}));
    }

    function detectTrackersSync() {
        const knownTrackers = [
            {name: 'Google Analytics (Universal)', regex: /google-analytics\.com\/analytics\.js|google-analytics\.com\/ga\.js/, danger: 'high', description: 'Tracks user behavior for analytics and advertising purposes.'},
            {name: 'Google Analytics 4', regex: /googletagmanager\.com\/gtag\/js/, danger: 'high', description: 'Next generation Google Analytics tracking.'},
            {name: 'Google Tag Manager', regex: /googletagmanager\.com\/gtm\.js/, danger: 'high', description: 'Manages JavaScript and HTML tags for tracking purposes.'},
            {name: 'Google AdSense', regex: /pagead2\.googlesyndication\.com/, danger: 'medium', description: 'Google ad network, tracks user activity for ads.'},
            {name: 'Google DoubleClick', regex: /doubleclick\.net/, danger: 'high', description: 'Google ad serving and tracking platform.'},
            {name: 'Google Ads Conversion', regex: /googleadservices\.com/, danger: 'medium', description: 'Tracks ad conversions for Google Ads.'},
            {name: 'Google reCAPTCHA', regex: /recaptcha\/api\.js/, danger: 'low', description: 'Google CAPTCHA service that may track user behavior.'},
            {name: 'Facebook Pixel', regex: /connect\.facebook\.net\/.*\/fbevents\.js/, danger: 'high', description: 'Tracks user activity for targeted ads on Facebook.'},
            {name: 'Facebook SDK', regex: /connect\.facebook\.net\/.*\/sdk\.js/, danger: 'medium', description: 'Facebook social features and tracking.'},
            {name: 'Hotjar', regex: /static\.hotjar\.com\//, danger: 'high', description: 'Records user behavior including clicks, scrolling, and heatmaps.'},
            {name: 'Mixpanel', regex: /cdn\.mxpnl\.com/, danger: 'high', description: 'Advanced analytics tracking user events and behaviors.'},
            {name: 'Adobe Analytics', regex: /omtrdc\.net|2o7\.net/, danger: 'high', description: 'Enterprise analytics and marketing tracking.'},
            {name: 'Segment', regex: /cdn\.segment\.com/, danger: 'high', description: 'Customer data platform that tracks user interactions.'},
            {name: 'Heap Analytics', regex: /heapanalytics\.com/, danger: 'high', description: 'Automatically captures all user interactions.'},
            {name: 'Amplitude', regex: /cdn\.amplitude\.com/, danger: 'medium', description: 'Product analytics tracking user behavior.'},
            {name: 'Crazy Egg', regex: /script\.crazyegg\.com/, danger: 'medium', description: 'Heatmap and user behavior tracking.'},
            {name: 'FullStory', regex: /fullstory\.com/, danger: 'high', description: 'Session replay and user behavior recording.'},
            {name: 'Mouseflow', regex: /cdn\.mouseflow\.com/, danger: 'high', description: 'Session replay and heatmap tracking.'},
            {name: 'Lucky Orange', regex: /luckyorange\.com/, danger: 'high', description: 'Live chat and session recording.'},
            {name: 'Criteo', regex: /static\.criteo\.net/, danger: 'high', description: 'Retargeting and personalized advertising.'},
            {name: 'Taboola', regex: /cdn\.taboola\.com/, danger: 'medium', description: 'Content recommendation and advertising.'},
            {name: 'Outbrain', regex: /outbrain\.com/, danger: 'medium', description: 'Content discovery and advertising platform.'},
            {name: 'AdRoll', regex: /d\.adroll\.com/, danger: 'high', description: 'Retargeting and display advertising.'},
            {name: 'Amazon Ads', regex: /amazon-adsystem\.com/, danger: 'medium', description: 'Amazon advertising and tracking.'},
            {name: 'Bing Ads', regex: /bat\.bing\.com/, danger: 'medium', description: 'Microsoft Bing advertising tracking.'},
            {name: 'Twitter Ads', regex: /static\.ads-twitter\.com/, danger: 'medium', description: 'Twitter advertising pixel.'},
            {name: 'LinkedIn Insight', regex: /snap\.licdn\.com/, danger: 'medium', description: 'LinkedIn conversion tracking.'},
            {name: 'Pinterest Tag', regex: /ct\.pinterest\.com/, danger: 'medium', description: 'Pinterest advertising tracking.'},
            {name: 'TikTok Pixel', regex: /analytics\.tiktok\.com/, danger: 'medium', description: 'TikTok advertising and conversion tracking.'},
            {name: 'Snapchat Pixel', regex: /sc-static\.net/, danger: 'medium', description: 'Snapchat advertising tracking.'},
            {name: 'Twitter Widget', regex: /platform\.twitter\.com/, danger: 'low', description: 'Twitter social widgets and buttons.'},
            {name: 'LinkedIn Widget', regex: /platform\.linkedin\.com/, danger: 'low', description: 'LinkedIn social features.'},
            {name: 'Instagram Embed', regex: /instagram\.com\/embed\.js/, danger: 'low', description: 'Instagram content embedding.'},
            {name: 'HubSpot', regex: /js\.hs-scripts\.com/, danger: 'high', description: 'Marketing automation and CRM tracking.'},
            {name: 'Marketo', regex: /munchkin\.marketo\.net/, danger: 'high', description: 'Marketing automation platform.'},
            {name: 'Pardot', regex: /pi\.pardot\.com/, danger: 'high', description: 'Salesforce B2B marketing automation.'},
            {name: 'Mailchimp', regex: /chimpstatic\.com/, danger: 'medium', description: 'Email marketing and audience tracking.'},
            {name: 'ActiveCampaign', regex: /trackcmp\.net/, danger: 'medium', description: 'Email marketing automation.'},
            {name: 'Intercom', regex: /widget\.intercom\.io/, danger: 'medium', description: 'Customer messaging and behavior tracking.'},
            {name: 'Drift', regex: /js\.driftt\.com/, danger: 'medium', description: 'Conversational marketing and chat.'},
            {name: 'LiveChat', regex: /cdn\.livechatinc\.com/, danger: 'medium', description: 'Live chat and visitor tracking.'},
            {name: 'Zendesk', regex: /static\.zdassets\.com/, danger: 'low', description: 'Customer support platform.'},
            {name: 'Olark', regex: /static\.olark\.com/, danger: 'medium', description: 'Live chat with visitor monitoring.'},
            {name: 'Optimizely', regex: /cdn\.optimizely\.com/, danger: 'medium', description: 'A/B testing and experimentation platform.'},
            {name: 'VWO', regex: /dev\.visualwebsiteoptimizer\.com/, danger: 'medium', description: 'A/B testing and conversion optimization.'},
            {name: 'Google Optimize', regex: /www\.googleoptimize\.com/, danger: 'medium', description: 'Google A/B testing platform.'},
            {name: 'Cloudflare Insights', regex: /static\.cloudflareinsights\.com/, danger: 'low', description: 'Cloudflare analytics and performance monitoring.'},
            {name: 'New Relic', regex: /js-agent\.newrelic\.com/, danger: 'low', description: 'Application performance monitoring.'},
            {name: 'Stripe.js', regex: /js\.stripe\.com/, danger: 'low', description: 'Payment processing with user tracking.'},
            {name: 'PayPal Analytics', regex: /paypal\.com\/tagmanager/, danger: 'low', description: 'PayPal conversion tracking.'},
            {name: 'Quantcast', regex: /quantserve\.com/, danger: 'high', description: 'Audience measurement and targeting.'},
            {name: 'Chartbeat', regex: /static\.chartbeat\.com/, danger: 'medium', description: 'Real-time web analytics.'},
            {name: 'Kissmetrics', regex: /i\.kissmetrics\.com/, danger: 'high', description: 'Person-based analytics tracking.'},
            {name: 'Piwik/Matomo', regex: /matomo\.js|piwik\.js/, danger: 'medium', description: 'Open-source analytics platform.'},
            {name: 'Yandex Metrica', regex: /mc\.yandex\.ru/, danger: 'high', description: 'Russian analytics and tracking service.'},
            {name: 'Baidu Analytics', regex: /hm\.baidu\.com/, danger: 'high', description: 'Chinese analytics service.'},
            {name: 'Local Storage Usage', regex: /localStorage\.setItem|localStorage\[/, danger: 'medium', description: 'Stores data in browser for persistent tracking.'},
            {name: 'Session Storage Usage', regex: /sessionStorage\.setItem|sessionStorage\[/, danger: 'low', description: 'Stores data temporarily during browser session.'},
            {name: 'IndexedDB Usage', regex: /indexedDB\.open/, danger: 'medium', description: 'Advanced browser database for data storage.'}
        ];

        const trackers = knownTrackers.filter(t => document.body.innerHTML.match(t.regex)).map(t => ({name: t.name, danger: t.danger, description: t.description}));
        return [...trackers, ...detectWebBeacons(), ...detectEtagTracking(), ...detectIPGeolocation()];
    }

    function detectZombieCookies() {
        return new Promise(resolve => {
            const legitimatePatterns = [/^session/i, /^sess/i, /^sid/i, /^phpsessid/i, /^jsessionid/i, /^aspsessionid/i, /^asp\.net.*session/i, /^auth/i, /^token/i, /^access.*token/i, /^refresh.*token/i, /^jwt/i, /^oauth/i, /^sso/i, /^saml/i, /^login/i, /^user.*id/i, /^uid/i, /^remember/i, /^keep.*logged/i, /^stay.*logged/i, /^csrf/i, /^xsrf/i, /^x.*csrf/i, /^antiforgery/i, /^request.*verification/i, /^cart/i, /^basket/i, /^shopping/i, /^checkout/i, /^order/i, /^wishlist/i, /consent/i, /cookie.*accept/i, /cookie.*consent/i, /gdpr/i, /privacy.*accept/i, /terms.*accept/i, /preference/i, /settings/i, /language/i, /locale/i, /lang/i, /timezone/i, /theme/i, /dark.*mode/i, /view.*mode/i, /currency/i, /region/i, /^wordpress/i, /^wp.*nonce/i, /^laravel/i, /^symfony/i, /^django/i, /^rails/i, /^express/i, /^connect\.sid/i, /^cloudflare/i, /^cf_/i, /^__cf/i, /^captcha/i, /^recaptcha/i, /^hcaptcha/i, /^i18n/i, /^country/i, /^location.*preference/i, /^accessibility/i, /^a11y/i, /^font.*size/i, /^contrast/i, /^notification/i, /^alert/i, /^message.*seen/i, /^cdn/i, /^cache/i, /^static/i];
            const isLegitimate = name => legitimatePatterns.some(p => p.test(name));

            const initial = document.cookie.split(';').map(c => c.trim()).filter(c => c).map(c => c.split('=')[0]);
            const suspicious = initial.filter(n => !isLegitimate(n));

            if (suspicious.length === 0) {
                resolve([]);
                return;
            }

            const values = {};
            suspicious.forEach(name => {
                const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));
                if (match) values[name] = match[2];
            });

            suspicious.forEach(name => {
                document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
                document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; domain=' + window.location.hostname + ';';
                document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; domain=.' + window.location.hostname + ';';
            });

            setTimeout(() => {
                const current = document.cookie.split(';').map(c => c.trim()).filter(c => c).map(c => c.split('=')[0]);
                const zombies = suspicious.filter(name => current.includes(name)).map(name => {
                    const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));
                    return {name, originalValue: values[name], newValue: match ? match[2] : ''};
                });

                resolve(zombies.length > 0 ? [{name: "Zombie Cookies", danger: "high", description: `Website automatically recreated ${zombies.length} tracking cookie(s) after deletion: ${zombies.map(c => c.name).join(', ')}. This indicates persistent tracking behavior.`}] : []);
            }, 2000);
        });
    }

    async function detectAllTrackers() {
        const trackers = detectTrackersSync();
        const zombies = await detectZombieCookies();
        const webrtc = detectWebRTCTracking();

        const iframes = document.querySelectorAll('iframe');
        const currentDomain = window.location.hostname;
        iframes.forEach(iframe => {
            try {
                const src = iframe.src;
                if (src) {
                    const url = new URL(src);
                    const domain = url.hostname;
                    if (domain && domain !== currentDomain && !domain.includes(currentDomain)) {
                        detectedThirdPartyIframes.push({domain, src, timestamp: Date.now()});
                    }
                }
            } catch (e) {}
        });

        return [...trackers, ...zombies, ...webrtc];
    }

    async function detectFingerprinting() {
        const methods = [];

        if (detectedCanvasFingerprinting.length > 0) {
            const unique = [...new Set(detectedCanvasFingerprinting.map(d => d.method))];
            methods.push({name: 'Canvas Fingerprinting', danger: 'high', description: `Website is actively using Canvas API for fingerprinting. Methods: ${unique.join(', ')}. Detected ${detectedCanvasFingerprinting.length} call(s).`});
        }

        if (detectedWebGLFingerprinting.length > 0) {
            methods.push({name: 'WebGL Fingerprinting', danger: 'high', description: `Website is querying WebGL parameters for fingerprinting. Detected ${detectedWebGLFingerprinting.length} suspicious parameter request(s).`});
        }

        if (detectedFontFingerprinting.length > 0) {
            methods.push({name: 'Font Fingerprinting', danger: 'high', description: 'Website is measuring text extensively to detect installed fonts. This is a common fingerprinting technique.'});
        }

        if (detectedAudioFingerprinting.length > 0) {
            methods.push({name: 'AudioContext Fingerprinting', danger: 'high', description: `Website created ${detectedAudioFingerprinting.length} AudioContext(s), potentially for audio fingerprinting.`});
        }

        if (detectedScreenFingerprinting.length > 0) {
            const props = [...new Set(detectedScreenFingerprinting.map(s => s.property))];
            methods.push({name: 'Screen Fingerprinting', danger: 'medium', description: `Website is extensively querying screen properties for device identification: ${props.join(', ')}.`});
        }

        if (detectedBatteryFingerprinting.length > 0) {
            methods.push({name: 'Battery API Fingerprinting', danger: 'medium', description: 'Website is accessing Battery API, which can be used for fingerprinting.'});
        }

        if (detectedMediaDevices.length > 0) {
            methods.push({name: 'Media Devices Enumeration', danger: 'high', description: `Website is enumerating cameras, microphones, and speakers. Device lists are unique identifiers. Detected ${detectedMediaDevices.length} enumeration(s).`});
        }

        if (detectedSensors.length > 0) {
            const types = [...new Set(detectedSensors.map(s => s.type))];
            methods.push({name: 'Device Sensors Access', danger: 'high', description: `Website is accessing device sensors: ${types.join(', ')}. Sensor data can fingerprint devices.`});
        }

        if (detectedServiceWorkers.length > 0) {
            const urls = detectedServiceWorkers.map(sw => sw.url).slice(0, 2);
            methods.push({name: 'Service Worker Storage', danger: 'medium', description: `Website registered ${detectedServiceWorkers.length} Service Worker(s): ${urls.join(', ')}${detectedServiceWorkers.length > 2 ? '...' : ''}. These can store persistent identifiers.`});
        }

        if (detectedCacheAPI.length > 0) {
            const names = [...new Set(detectedCacheAPI.map(c => c.cacheName))];
            methods.push({name: 'Cache API Storage', danger: 'medium', description: `Website is using Cache API for storage (${names.length} cache(s)). Can be used for persistent tracking.`});
        }

        if (detectedWebSQL.length > 0) {
            const dbs = [...new Set(detectedWebSQL.map(db => db.dbName))];
            methods.push({name: 'WebSQL Database', danger: 'medium', description: `Website is using deprecated WebSQL for storage: ${dbs.join(', ')}. Often used for supercookies.`});
        }

        if (detectedFileSystem.length > 0) {
            methods.push({name: 'FileSystem API', danger: 'high', description: `Website is using FileSystem API (${detectedFileSystem.length} request(s)). Can store large amounts of tracking data.`});
        }

        if (detectedThirdPartyIframes.length > 0) {
            const domains = [...new Set(detectedThirdPartyIframes.map(i => i.domain))];
            methods.push({name: 'Third-Party Iframes', danger: 'high', description: `Detected ${detectedThirdPartyIframes.length} third-party iframe(s) from: ${domains.slice(0, 3).join(', ')}${domains.length > 3 ? ` and ${domains.length - 3} more` : ''}`});
        }

        return methods;
    }

    async function showAuditResults() {
        windowContent.innerHTML = '<div class="aptloading-spinner"></div><p style="text-align:center!important">Scanning...</p>';
        auditWindow.style.display = "block";

        const trackers = await detectAllTrackers();
        await new Promise(r => setTimeout(r, 3000));
        const fingerprinting = await detectFingerprinting();
        const cookies = getCookies();
        const allThreats = [...trackers, ...fingerprinting];

        windowContent.innerHTML = `<h2 class="aptTitle">Privacy Audit Results</h2><div class="aptSectionTitle">Trackers & Fingerprinting (${allThreats.length})</div><ul>${allThreats.length > 0 ? allThreats.map(t => `<li>${t.name} <span class="aptDangerLevel aptDangerLevel${t.danger.charAt(0).toUpperCase() + t.danger.slice(1)}">${t.danger.charAt(0).toUpperCase() + t.danger.slice(1)}</span> - ${t.description}</li>`).join('') : '<li>No trackers or fingerprinting detected.</li>'}</ul><div class="aptSectionTitle">Cookies (${cookies.length})</div><ul>${cookies.length > 0 ? cookies.map(c => `<li>${c}</li>`).join('') : '<li>No cookies found.</li>'}</ul>`;

        detectedWebRTCUsage = [];
        detectedCanvasFingerprinting = [];
        detectedWebGLFingerprinting = [];
        detectedAudioFingerprinting = [];
        detectedFontFingerprinting = [];
        detectedScreenFingerprinting = [];
        detectedBatteryFingerprinting = [];
        detectedMediaDevices = [];
        detectedSensors = [];
        detectedServiceWorkers = [];
        detectedCacheAPI = [];
        detectedWebSQL = [];
        detectedFileSystem = [];
        detectedThirdPartyIframes = [];
    }

    scanButton.addEventListener("click", showAuditResults);
})();