SpyScan

發現網站上的追蹤腳本、指紋辨識和監控技術。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         SpyScan
// @namespace    https://greasyfork.org/fr/users/1451802
// @version      2.1.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
// @noframes
// ==/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);
})();