// jsdet browser bootstrap — template file.
// Variables like {{JSDET_URL}} are replaced at runtime by BrowserBridge::bootstrap_js().
// This file is NOT valid JS until substitution. Use `cargo test` to verify correctness.
(function() {
    // Bridge call helper — routes to host via WASM import.
    function __call(api, args) {
        try {
            return __jsdet_bridge_call(api, JSON.stringify(args || []));
        } catch(e) { return undefined; }
    }

    // Element proxy — wraps node IDs with property getters/setters.
    // When JS does form.action = 'evil.com', the proxy catches it
    // and routes through the bridge for observation.
    function __makeElement(tag, nodeId) {
        var attrs = {};
        var style = {};
        var children = [];
        var el = {
            nodeType: 1,
            tagName: tag.toUpperCase(),
            nodeName: tag.toUpperCase(),
            __nodeId: nodeId,
            getAttribute: function(n) { return attrs[n] || null; },
            setAttribute: function(n, v) {
                attrs[n] = v;
                __call("element.setAttribute", [nodeId, n, v]);
            },
            removeAttribute: function(n) { delete attrs[n]; },
            hasAttribute: function(n) { return n in attrs; },
            appendChild: function(child) {
                children.push(child);
                __call("element.appendChild", [nodeId, child.__nodeId || 0]);
                return child;
            },
            removeChild: function(child) {
                children = children.filter(function(c) { return c !== child; });
                return child;
            },
            insertBefore: function(newNode, refNode) {
                children.push(newNode);
                return newNode;
            },
            cloneNode: function() { return __makeElement(tag, nodeId); },
            addEventListener: function(e, fn) {
                if (e === 'submit') el.__onsubmit = fn;
                if (e === 'click') el.__onclick = fn;
                if (e === 'input' || e === 'keydown' || e === 'keyup' || e === 'keypress' || e === 'change') {
                    // Keylogger/input monitor detection — log the event type
                    __call("element.addEventListener", [nodeId, e]);
                    el['__on' + e] = fn;
                }
            },
            removeEventListener: function() {},
            dispatchEvent: function() { return true; },
            get id() { return attrs.id || ''; },
            set id(v) { attrs.id = v; el.setAttribute('id', v); },
            get className() { return attrs['class'] || ''; },
            set className(v) { attrs['class'] = v; },
            get classList() {
                var self = el;
                return {
                    add: function(c) { self.className = (self.className + ' ' + c).trim(); },
                    remove: function(c) { self.className = self.className.replace(c, '').trim(); },
                    contains: function(c) { return self.className.indexOf(c) >= 0; },
                    toggle: function(c) { if (this.contains(c)) this.remove(c); else this.add(c); },
                };
            },
            get innerHTML() { return __call("element.innerHTML", [nodeId]) || ''; },
            set innerHTML(v) {
                __call("element.setInnerHTML", [nodeId, v]);
                // Taint check: if the bridge exposes taint-check helper, call it and emit a taint_sink_reached event.
                if (typeof __jsdet_check_taint_at_sink === 'function' && typeof v === 'string') {
                    try {
                        var tainted = __jsdet_check_taint_at_sink('innerHTML', v);
                        if (tainted) {
                            var preview = v.substring(0, 200);
                            __call("taint_sink_reached", ['innerHTML', preview, String(__jsdet_get_taint(v))]);
                        }
                    } catch (e) { /* do not let taint helpers break page execution */ }
                }
            },
            get innerText() { return attrs.__text || ''; },
            set innerText(v) { attrs.__text = v; },
            get textContent() { return attrs.__text || ''; },
            set textContent(v) { attrs.__text = v; },
            get src() { return attrs.src || ''; },
            set src(v) { attrs.src = v; el.setAttribute('src', v); },
            get href() { return attrs.href || ''; },
            set href(v) { attrs.href = v; el.setAttribute('href', v); },
            get action() { return attrs.action || ''; },
            set action(v) { attrs.action = v; el.setAttribute('action', v); },
            get method() { return attrs.method || 'GET'; },
            set method(v) { attrs.method = v; },
            get type() { return attrs.type || ''; },
            set type(v) { attrs.type = v; el.setAttribute('type', v); },
            get name() { return attrs.name || ''; },
            set name(v) { attrs.name = v; el.setAttribute('name', v); },
            get value() { return attrs.value || ''; },
            set value(v) { attrs.value = v; },
            get placeholder() { return attrs.placeholder || ''; },
            set placeholder(v) { attrs.placeholder = v; },
            get disabled() { return !!attrs.disabled; },
            set disabled(v) { attrs.disabled = v; },
            get checked() { return !!attrs.checked; },
            set checked(v) { attrs.checked = v; },
            get style() { return new Proxy(style, {
                set: function(t, p, v) {
                    t[p] = v;
                    __call("element.style", [nodeId, p, v]);
                    return true;
                },
                get: function(t, p) { return t[p] || ''; }
            }); },
            set style(v) {
                if (typeof v === 'string') {
                    // Parse "display:none;color:red"
                    v.split(';').forEach(function(decl) {
                        var parts = decl.split(':');
                        if (parts[0]) style[parts[0].trim()] = (parts[1] || '').trim();
                    });
                }
            },
            get parentNode() { return null; },
            get childNodes() { return children; },
            get children() { return children; },
            get firstChild() { return children[0] || null; },
            get lastChild() { return children[children.length - 1] || null; },
            get nextSibling() { return null; },
            querySelector: function(sel) {
                // Search children first
                for (var c = 0; c < children.length; c++) {
                    if (children[c].tagName && sel.toUpperCase().indexOf(children[c].tagName) >= 0) return children[c];
                    if (sel.indexOf('password') >= 0 && children[c].type === 'password') return children[c];
                    if (sel.indexOf('#') >= 0 && children[c].id && sel.indexOf(children[c].id) >= 0) return children[c];
                }
                // Fall back to bridge for DOM queries
                var nodeId = __call("document.querySelector", [sel]);
                if (nodeId !== null && nodeId !== undefined) return __makeElement("div", nodeId);
                return null;
            },
            querySelectorAll: function(sel) {
                var results = [];
                for (var c = 0; c < children.length; c++) {
                    if (children[c].tagName && sel.toUpperCase().indexOf(children[c].tagName) >= 0) results.push(children[c]);
                }
                return results.length > 0 ? results : [];
            },
            getElementsByTagName: function(tag) {
                var results = [];
                var upper = tag.toUpperCase();
                for (var c = 0; c < children.length; c++) {
                    if (children[c].tagName === upper) results.push(children[c]);
                }
                return results;
            },
            getElementsByClassName: function(cls) { return []; },
            focus: function() {},
            blur: function() {},
            click: function() { if (el.__onclick) el.__onclick(); },
            submit: function() { __call("form.submit", [attrs.action || '']); },
        };
        return el;
    }

    // document object
    globalThis.document = {
        createElement: function(tag) {
            var nodeId = __call("document.createElement", [tag]);
            return __makeElement(tag, nodeId || 0);
        },
        getElementById: function(id) {
            var nodeId = __call("document.getElementById", [id]);
            if (nodeId === null || nodeId === undefined) return null;
            return __makeElement("div", nodeId);
        },
        querySelector: function(sel) {
            var nodeId = __call("document.querySelector", [sel]);
            if (nodeId === null || nodeId === undefined) return null;
            return __makeElement("div", nodeId);
        },
        querySelectorAll: function(sel) {
            var result = __call("document.querySelectorAll", [sel]);
            if (Array.isArray(result)) {
                return result.map(function(id) { return __makeElement("div", id); });
            }
            return [];
        },
        write: function(html) { return __call("document.write", [html]); },
        writeln: function(html) { return __call("document.writeln", [html]); },
        get cookie() { return __call("document.cookie.get", []); },
        set cookie(v) { __call("document.cookie.set", [v]); },
        get referrer() { return "{{JSDET_REFERRER}}"; },
        get title() { return ""; },
        set title(v) {},
        get readyState() { return "complete"; },
        get domain() { return "{{JSDET_HOSTNAME}}"; },
        get body() { return __makeElement("body", 0); },
        get head() { return __makeElement("head", 0); },
        get documentElement() { return __makeElement("html", 0); },
        addEventListener: function() {},
        removeEventListener: function() {},
        createTextNode: function(t) { return {nodeType:3,textContent:t}; },
        createDocumentFragment: function() { return {nodeType:11,children:[],appendChild:function(c){this.children.push(c);return c;}}; },
        createEvent: function(t) { return new Event(t); },
        currentScript: null,
        get forms() {
            // Return forms from the bridge DOM
            var result = __call("document.querySelectorAll", ["form"]);
            if (Array.isArray(result)) return result.map(function(id) { return __makeElement("form", id); });
            return [];
        },
        get images() {
            var result = __call("document.querySelectorAll", ["img"]);
            if (Array.isArray(result)) return result.map(function(id) { return __makeElement("img", id); });
            return [];
        },
        links: [],
    };

    // window object (self-referential)
    globalThis.window = globalThis;
    globalThis.self = globalThis;
    globalThis.top = globalThis;
    globalThis.parent = globalThis;
    globalThis.frames = globalThis;
    globalThis.length = 0; // no iframes
    globalThis.outerWidth = {{JSDET_SCREEN_W}};
    globalThis.outerHeight = {{JSDET_SCREEN_H}};
    globalThis.innerWidth = {{JSDET_SCREEN_W}};
    globalThis.innerHeight = {{JSDET_SCREEN_H_TASKBAR}};
    globalThis.devicePixelRatio = 1;
    globalThis.isSecureContext = true;
    globalThis.origin = "{{JSDET_ORIGIN}}";

    // Chrome-specific objects — phishing kits check these to detect sandboxes.
    // If window.chrome is undefined, many kits abort.
    globalThis.chrome = {
        runtime: { id: undefined, getManifest: function() { return {}; } },
        app: { isInstalled: false },
    };

    // navigator.plugins and Intl are set after navigator definition below.

    // location
    // location — href setter triggers observation.
    // Phishing kits redirect via location.href = 'evil.com' — without a setter,
    // this is INVISIBLE to the sandbox. The most common redirect technique.
    var __locData = {
        _href: "{{JSDET_URL}}", protocol: "{{JSDET_PROTOCOL}}", hostname: "{{JSDET_HOSTNAME}}",
        host: "{{JSDET_HOSTNAME}}", port: "", pathname: "{{JSDET_PATHNAME}}",
        search: "{{JSDET_SEARCH}}", hash: "{{JSDET_HASH}}", origin: "{{JSDET_ORIGIN}}"
    };
    globalThis.location = {};
    Object.defineProperty(globalThis.location, 'href', {
        get: function() { return __locData._href; },
        set: function(v) { __locData._href = v; __call("window.location.href.set", [v]); },
        configurable: true, enumerable: true
    });
    // Copy other properties
    ['protocol','hostname','host','port','pathname','search','hash','origin'].forEach(function(p) {
        Object.defineProperty(globalThis.location, p, {
            get: function() { return __locData[p]; },
            set: function(v) { __locData[p] = v; },
            configurable: true, enumerable: true
        });
    });
    globalThis.location.assign = function(u) { __call("window.location.assign", [u]); __locData._href = u; };
    globalThis.location.replace = function(u) { __call("window.location.replace", [u]); __locData._href = u; };
    globalThis.location.reload = function() {};
    globalThis.location.toString = function() { return __locData._href; };

    // navigator
    globalThis.navigator = {
        userAgent: "{{JSDET_UA}}",
        platform: "{{JSDET_PLATFORM}}",
        language: "{{JSDET_LANGUAGE}}",
        languages: {{JSDET_LANGUAGES_JSON}},
        hardwareConcurrency: {{JSDET_HW_CONCURRENCY}},
        deviceMemory: {{JSDET_DEVICE_MEMORY}},
        maxTouchPoints: {{JSDET_MAX_TOUCH}},
        vendor: "{{JSDET_VENDOR}}",
        appName: "{{JSDET_APP_NAME}}",
        appVersion: "{{JSDET_APP_VERSION}}",
        cookieEnabled: true,
        onLine: true,
        webdriver: false,
        // clipboard, geolocation, serviceWorker are set later with __call hooks
        serviceWorker: { register: function(url) { __call("serviceworker.register", [url]); return Promise.reject(new Error("blocked by sandbox")); } },
        sendBeacon: function(url, data) {
            try {
                var d = data != null ? String(data).substring(0, 1000) : '';
                __call("navigator.sendBeacon", [url, d]);
            } catch(e) {}
            return true;
        },
        permissions: { query: function(desc) { return Promise.resolve({ state: 'prompt' }); } },
    };

    // Anti-detection: plugins, mimeTypes, Intl — must be set AFTER navigator object exists.
    globalThis.navigator.plugins = [
        { name: 'Chrome PDF Viewer', filename: 'internal-pdf-viewer' },
        { name: 'Chrome PDF Plugin', filename: 'internal-pdf-viewer' },
        { name: 'Native Client', filename: 'internal-nacl-plugin' },
    ];
    globalThis.navigator.mimeTypes = { length: 4 };
    if (!globalThis.Intl) globalThis.Intl = {};
    globalThis.Intl.DateTimeFormat = function() {
        return { resolvedOptions: function() { return { timeZone: 'America/New_York' }; } };
    };

    // screen
    globalThis.screen = {
        width: {{JSDET_SCREEN_W}},
        height: {{JSDET_SCREEN_H}},
        availWidth: {{JSDET_SCREEN_W}},
        availHeight: {{JSDET_SCREEN_H_TASKBAR}},
        colorDepth: {{JSDET_COLOR_DEPTH}},
        pixelDepth: {{JSDET_COLOR_DEPTH}},
    };

    // fetch — full observation including method, headers, body for exfiltration detection
    globalThis.fetch = function(url, opts) {
        // Handle Request object as first argument
        if (url && typeof url === 'object' && url.url) {
            opts = opts || url;
            url = url.url;
        }
        if (typeof url !== 'string') url = String(url);
        var o = opts || {};
        var method = o.method || "GET";
        var body = o.body;
        var bodyPreview = '';
        if (body) {
            if (typeof body === 'string') bodyPreview = body.substring(0, 1000);
            else if (body instanceof FormData) bodyPreview = '[FormData]';
            else if (body instanceof Blob) bodyPreview = '[Blob:' + body.size + ']';
            else try { bodyPreview = JSON.stringify(body).substring(0, 1000); } catch(e) { bodyPreview = '[non-serializable]'; }
        }
        __call("fetch", [url, method, bodyPreview]);
        // Return realistic response so attack chains continue executing
        var resp = {
            ok: true, status: 200, statusText: "OK", type: "cors", url: url, redirected: false,
            headers: new Headers({ "content-type": "application/json" }),
            json: function() { return Promise.resolve({}); },
            text: function() { return Promise.resolve(""); },
            blob: function() { return Promise.resolve(new Blob([])); },
            arrayBuffer: function() { return Promise.resolve(new ArrayBuffer(0)); },
            clone: function() { return resp; },
        };
        return Promise.resolve(resp);
    };

    // XMLHttpRequest
    globalThis.XMLHttpRequest = function() {
        this.readyState = 0;
        this.status = 0;
        this.responseText = "";
        this.open = function(m, u) { this._method = m; this._url = u; };
        this.send = function(body) { __call("xhr.send", [this._url, this._method, body || ""]); this.readyState = 4; this.status = 200; if(this.onreadystatechange) this.onreadystatechange(); if(this.onload) this.onload(); };
        this.setRequestHeader = function() {};
        this.getResponseHeader = function() { return null; };
        this.addEventListener = function(e, fn) { if(e==="load") this.onload = fn; };
    };

    // localStorage / sessionStorage
    var _stores = { local: {}, session: {} };
    function makeStorage(type) {
        return {
            getItem: function(k) { return _stores[type][k] || null; },
            setItem: function(k, v) { _stores[type][k] = String(v); __call(type+"Storage.setItem", [k, v]); },
            removeItem: function(k) { delete _stores[type][k]; },
            clear: function() { _stores[type] = {}; },
            get length() { return Object.keys(_stores[type]).length; },
            key: function(i) { return Object.keys(_stores[type])[i] || null; },
        };
    }
    globalThis.localStorage = makeStorage("local");
    globalThis.sessionStorage = makeStorage("session");

    // Hook eval() — observe dynamic code AND check taint.
    // If tainted data reaches eval(), that's a confirmed vulnerability.
    var __realEval = globalThis.eval;
    globalThis.eval = function(code) {
        var preview = typeof code === 'string' ? code.substring(0, 200) : '';
        __call("eval", [preview]);

        // CHECK TAINT: if the code argument carries a taint label,
        // that means user-controlled data reached eval() — XSS/RCE confirmed.
        if (typeof __jsdet_check_taint_at_sink === 'function' && typeof code === 'string') {
            var tainted = __jsdet_check_taint_at_sink("eval", code);
            if (tainted) {
                __call("taint_sink_reached", ["eval", preview, String(__jsdet_get_taint(code))]);
            }
        }

        return __realEval(code);
    };

    // Hook Function() — same taint check.
    var __realFunction = globalThis.Function;
    globalThis.Function = function() {
        var args = Array.prototype.slice.call(arguments);
        var body = args.length > 0 ? args[args.length - 1] : '';
        var preview = typeof body === 'string' ? body.substring(0, 200) : '';
        __call("Function", [preview]);

        if (typeof __jsdet_check_taint_at_sink === 'function' && typeof body === 'string') {
            var tainted = __jsdet_check_taint_at_sink("Function", body);
            if (tainted) {
                __call("taint_sink_reached", ["Function", preview, String(__jsdet_get_taint(body))]);
            }
        }

        return __realFunction.apply(this, arguments);
    };
    globalThis.Function.prototype = __realFunction.prototype;

    // Timer implementation — callbacks are stored and drained by the host.
    var __timers = [];
    var __timer_id = 1;
    globalThis.setTimeout = function(fn, delay) {
        var id = __timer_id++;
        if (typeof fn === 'function') {
            __timers.push({ id: id, fn: fn, delay: delay || 0, interval: false });
        } else if (typeof fn === 'string') {
            __timers.push({ id: id, fn: function() { eval(fn); }, delay: delay || 0, interval: false });
        }
        __call("timer.setTimeout", [id, delay || 0]);
        return id;
    };
    globalThis.setInterval = function(fn, delay) {
        var id = __timer_id++;
        if (typeof fn === 'function') {
            __timers.push({ id: id, fn: fn, delay: delay || 0, interval: true });
        }
        __call("timer.setInterval", [id, delay || 0]);
        return id;
    };
    globalThis.clearTimeout = function(id) {
        __timers = __timers.filter(function(t) { return t.id !== id; });
    };
    globalThis.clearInterval = globalThis.clearTimeout;

    // Timer drain function called by the host after script execution.
    globalThis.__jsdet_drain_timer = function() {
        if (__timers.length === 0) return;
        var timer = __timers.shift();
        if (timer && timer.fn) {
            try { timer.fn(); } catch(e) {}
            if (timer.interval) {
                __timers.push(timer);
            }
        }
    };

    // requestAnimationFrame — used by some kits for timing
    globalThis.requestAnimationFrame = function(fn) {
        var id = __timer_id++;
        if (typeof fn === 'function') {
            __timers.push({ id: id, fn: fn, delay: 0, interval: false });
        }
        return id;
    };
    globalThis.cancelAnimationFrame = globalThis.clearTimeout;

    // matchMedia — anti-bot checks query this
    globalThis.matchMedia = function(query) {
        return {
            matches: query.indexOf('prefers-color-scheme: light') >= 0 || query.indexOf('(min-width') >= 0,
            media: query,
            addEventListener: function() {},
            removeEventListener: function() {},
            addListener: function() {},
            removeListener: function() {},
        };
    };

    // getSelection — some kits check clipboard selection
    globalThis.getSelection = function() {
        return { toString: function() { return ''; }, removeAllRanges: function() {}, addRange: function() {} };
    };
    if (globalThis.document) globalThis.document.getSelection = globalThis.getSelection;

    // window.open — track popup URLs
    globalThis.open = function(url, name, features) {
        if (url) __call("window.open", [url]);
        return { closed: false, close: function() {}, focus: function() {} };
    };

    // history.pushState/replaceState — used for URL manipulation in phishing
    globalThis.history = {
        length: 1,
        state: null,
        pushState: function(state, title, url) {
            if (url) __call("history.pushState", [url]);
        },
        replaceState: function(state, title, url) {
            if (url) __call("history.replaceState", [url]);
        },
        back: function() {},
        forward: function() {},
        go: function() {},
    };

    // Automatic form probing — after all scripts execute, find credential forms
    // and simulate submission to observe where data goes.
    // This is how we catch phishing on free hosting platforms: the form submits
    // to the platform's form handler which forwards to the phisher's C2.
    globalThis.__jsdet_probe_forms = function() {
        // Collect forms from TWO sources:
        // 1. Forms created by JavaScript during execution
        // 2. Forms that were already in the static HTML
        var forms = __created_elements.filter(function(el) {
            return el.tagName === 'FORM';
        });

        // Also probe static forms in the original HTML.
        // The bridge parses the initial HTML for forms with password fields.
        var staticResult = __call("probe_html_forms", []);
        if (staticResult && typeof staticResult === 'string' && staticResult.indexOf('CREDENTIAL_FORM') === 0) {
            // Parse the result and emit credential_form_detected observation
            var parts = staticResult.split(';');
            var action = '';
            for (var p = 0; p < parts.length; p++) {
                if (parts[p].indexOf('action=') === 0) {
                    action = parts[p].substring(7);
                    break;
                }
            }
            __call("credential_form_detected", [action, "password", "true"]);
        }

        // Recursive helper to find inputs in nested DOM structures
        function __findInputs(node, fields) {
            if (!node) return;
            if (node.type === 'password') {
                fields.push({ type: 'password', name: node.name || '' });
            } else if (node.type === 'email' || node.type === 'text') {
                fields.push({ type: node.type, name: node.name || node.type });
            }
            // Recurse into children
            var kids = node.childNodes || node.children || [];
            for (var k = 0; k < kids.length; k++) {
                __findInputs(kids[k], fields);
            }
        }

        for (var i = 0; i < forms.length; i++) {
            var form = forms[i];
            var action = form.action || '';
            var fields = [];

            // Recursively find ALL inputs in the form (handles nested divs, fieldsets)
            __findInputs(form, fields);
            var hasPassword = fields.some(function(f) { return f.type === 'password'; });

            if (hasPassword) {
                // Report credential form with action URL
                __call("credential_form_detected", [
                    action,
                    fields.map(function(f) { return f.name || f.type; }).join(','),
                    hasPassword ? 'true' : 'false'
                ]);

                // Simulate submission — fill with canary and POST
                if (action) {
                    fetch(action, {
                        method: 'POST',
                        body: 'email=canary@jsdet.test&password=canary_password_probe'
                    });
                }

                // Fire submit event to trigger any attached event handlers.
                // Phishing kits intercept form.submit to steal creds before
                // the form actually submits. This triggers that interception.
                try {
                    if (form.__onsubmit) {
                        form.__onsubmit({ preventDefault: function() {}, target: form });
                    }
                    if (typeof form.dispatchEvent === 'function') {
                        form.dispatchEvent({ type: 'submit', preventDefault: function() {} });
                    }
                } catch(e) {
                    // Submit handler may throw — that's fine, we still observed it
                }
            }
        }
    };

    // Track created elements for form probing
    var __created_elements = [];
    var __origCreateElement = document.createElement.bind(document);
    document.createElement = function(tag) {
        var el = __origCreateElement(tag);
        __created_elements.push(el);
        return el;
    };

    // Stubs for common APIs
    globalThis.alert = function() {};
    globalThis.confirm = function() { return false; };
    globalThis.prompt = function() { return null; };
    globalThis.getComputedStyle = function() { return new Proxy({}, { get: function() { return ""; } }); };
    // requestAnimationFrame and cancelAnimationFrame — defined earlier with timer integration
    // Real base64 encode/decode — critical for phishing kit deobfuscation.
    // Every phishing kit on earth uses atob() to decode payloads.
    // A stub here means ZERO obfuscated payloads get detected.
    globalThis.btoa = function(s) {
        var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
        var o = '';
        for (var i = 0; i < s.length; i += 3) {
            var a = s.charCodeAt(i), b = s.charCodeAt(i+1), c = s.charCodeAt(i+2);
            var bits = (a << 16) | ((b || 0) << 8) | (c || 0);
            o += chars[(bits >> 18) & 63] + chars[(bits >> 12) & 63];
            o += (i+1 < s.length) ? chars[(bits >> 6) & 63] : '=';
            o += (i+2 < s.length) ? chars[bits & 63] : '=';
        }
        return o;
    };
    globalThis.atob = function(s) {
        var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
        s = s.replace(/[^A-Za-z0-9+/=]/g, '');
        var o = '';
        for (var i = 0; i < s.length; i += 4) {
            var a = chars.indexOf(s[i]), b = chars.indexOf(s[i+1]);
            var c = chars.indexOf(s[i+2]), d = chars.indexOf(s[i+3]);
            var bits = (a << 18) | (b << 12) | (c << 6) | d;
            o += String.fromCharCode((bits >> 16) & 255);
            if (c !== 64) o += String.fromCharCode((bits >> 8) & 255);
            if (d !== 64) o += String.fromCharCode(bits & 255);
        }
        return o;
    };
    // URL constructor — phishing kits parse URLs to extract domains, paths, params.
    globalThis.URL = function(u, base) {
        if (base && !u.match(/^https?:\/\//)) {
            u = base.replace(/\/[^/]*$/, '/') + u;
        }
        this.href = u;
        var m = u.match(/^(https?:)\/\/([^/:]+)(:\d+)?(\/[^?#]*)?(\?[^#]*)?(#.*)?$/);
        if (m) {
            this.protocol = m[1];
            this.hostname = m[2];
            this.host = m[2] + (m[3] || '');
            this.port = m[3] ? m[3].substring(1) : '';
            this.pathname = m[4] || '/';
            this.search = m[5] || '';
            this.hash = m[6] || '';
            this.origin = m[1] + '//' + m[2] + (m[3] || '');
        } else {
            this.protocol = ''; this.hostname = ''; this.host = '';
            this.port = ''; this.pathname = u; this.search = '';
            this.hash = ''; this.origin = '';
        }
        this.searchParams = {
            get: function(k) {
                var pairs = (this.search || '').replace('?','').split('&');
                for (var i = 0; i < pairs.length; i++) {
                    var p = pairs[i].split('=');
                    if (decodeURIComponent(p[0]) === k) return decodeURIComponent(p[1] || '');
                }
                return null;
            }.bind(this),
            has: function(k) { return this.get(k) !== null; }.bind(this),
        };
        this.toString = function() { return this.href; };
    };
    globalThis.console = { log: function(){}, warn: function(){}, error: function(){}, info: function(){}, debug: function(){}, dir: function(){}, table: function(){} };
    globalThis.Image = function() { this.src = ""; this.onload = null; this.onerror = null; };
    globalThis.Event = function(t) { this.type = t; this.target = null; this.preventDefault = function(){}; this.stopPropagation = function(){}; };
    globalThis.CustomEvent = function(t, o) { this.type = t; this.detail = (o||{}).detail; };
    globalThis.MutationObserver = function(cb) { this.observe = function(){}; this.disconnect = function(){}; };
    globalThis.IntersectionObserver = function(cb) { this.observe = function(){}; this.disconnect = function(){}; };
    globalThis.ResizeObserver = function(cb) { this.observe = function(){}; this.disconnect = function(){}; };
    // performance.now — return monotonically increasing fake time.
    // Returning 0 lets timing-based sandbox detection work (elapsed=0 means sandbox).
    // Returning incrementing values simulates real browser timing.
    var __perfBase = 1712345678000; // fake page load time
    var __perfCalls = 0;
    globalThis.performance = {
        now: function() { return (__perfCalls++) * 0.1 + 12.5; }, // ~0.1ms increments
        mark: function(){},
        measure: function(){},
        getEntriesByType: function() { return []; },
        timing: { navigationStart: __perfBase, domComplete: __perfBase + 450, loadEventEnd: __perfBase + 500 },
    };
    globalThis.crypto = { getRandomValues: function(a) { for(var i=0;i<a.length;i++) a[i]=Math.floor(Math.random()*256); return a; }, subtle: {} };

    // TextEncoder/TextDecoder — used by modern kits for binary data handling.
    globalThis.TextEncoder = function() {
        this.encode = function(s) {
            var arr = [];
            for (var i = 0; i < s.length; i++) {
                var c = s.charCodeAt(i);
                if (c < 128) { arr.push(c); }
                else if (c < 2048) { arr.push(192 | (c >> 6), 128 | (c & 63)); }
                else { arr.push(224 | (c >> 12), 128 | ((c >> 6) & 63), 128 | (c & 63)); }
            }
            return new Uint8Array(arr);
        };
    };
    globalThis.TextDecoder = function() {
        this.decode = function(buf) {
            var arr = buf instanceof Uint8Array ? buf : new Uint8Array(buf);
            var s = '';
            for (var i = 0; i < arr.length; i++) {
                s += String.fromCharCode(arr[i]);
            }
            return s;
        };
    };

    // DOMParser — kits parse HTML strings to extract data.
    globalThis.DOMParser = function() {
        this.parseFromString = function(str, type) {
            return { body: { innerHTML: str, textContent: str.replace(/<[^>]*>/g, '') } };
        };
    };

    // FormData — kits build form submissions.
    globalThis.FormData = function(form) {
        var data = {};
        this.append = function(k, v) { data[k] = v; };
        this.get = function(k) { return data[k] || null; };
        this.set = function(k, v) { data[k] = v; };
        this.has = function(k) { return k in data; };
        this.entries = function() { return Object.entries(data); };
    };

    // Request — for fetch(new Request(...))
    globalThis.Request = function(url, opts) {
        var o = opts || {};
        this.url = typeof url === 'string' ? url : String(url);
        this.method = o.method || 'GET';
        this.headers = new Headers(o.headers);
        this.body = o.body || null;
        this.mode = o.mode || 'cors';
        this.credentials = o.credentials || 'same-origin';
        this.clone = function() { return new Request(this.url, o); };
    };

    // Response — for Response constructor
    globalThis.Response = function(body, init) {
        var i = init || {};
        this.ok = (i.status || 200) >= 200 && (i.status || 200) < 300;
        this.status = i.status || 200;
        this.statusText = i.statusText || 'OK';
        this.headers = new Headers(i.headers);
        this.body = body;
        this.json = function() { return Promise.resolve(typeof body === 'string' ? JSON.parse(body) : {}); };
        this.text = function() { return Promise.resolve(typeof body === 'string' ? body : ''); };
        this.clone = function() { return new Response(body, init); };
    };
    globalThis.Response.json = function(data, init) { return new Response(JSON.stringify(data), init); };

    // Headers — for fetch API.
    globalThis.Headers = function(init) {
        var h = init || {};
        this.get = function(k) { return h[k.toLowerCase()] || null; };
        this.set = function(k, v) { h[k.toLowerCase()] = v; };
        this.has = function(k) { return k.toLowerCase() in h; };
        this.append = function(k, v) { h[k.toLowerCase()] = v; };
    };

    // Blob — for binary data.
    globalThis.Blob = function(parts, opts) {
        this.size = parts ? parts.reduce(function(s, p) { return s + (p.length || p.byteLength || 0); }, 0) : 0;
        this.type = (opts || {}).type || '';
        this.text = function() { return Promise.resolve(parts ? parts.join('') : ''); };
    };

    // AbortController — for fetch cancellation.
    globalThis.AbortController = function() {
        this.signal = { aborted: false };
        this.abort = function() { this.signal.aborted = true; };
    };

    // URLSearchParams
    globalThis.URLSearchParams = function(init) {
        var params = {};
        if (typeof init === 'string') {
            init.replace('?', '').split('&').forEach(function(pair) {
                var p = pair.split('=');
                if (p[0]) params[decodeURIComponent(p[0])] = decodeURIComponent(p[1] || '');
            });
        }
        this.get = function(k) { return params[k] || null; };
        this.has = function(k) { return k in params; };
        this.set = function(k, v) { params[k] = v; };
        this.toString = function() { return Object.entries(params).map(function(e) { return e[0]+'='+e[1]; }).join('&'); };
    };

    // ═══ WebSocket C2 Detection ═══
    var __RealWebSocket = globalThis.WebSocket;
    globalThis.WebSocket = function(url, protocols) {
        __call("websocket.connect", [url]);
        var ws = {
            url: url, readyState: 0,
            send: function(data) { __call("websocket.send", [url, typeof data === 'string' ? data.substring(0, 500) : '[binary]']); },
            close: function() { __call("websocket.close", [url]); },
            addEventListener: function(e, fn) { if (e === 'message') ws.onmessage = fn; },
            onopen: null, onmessage: null, onclose: null, onerror: null
        };
        setTimeout(function() { ws.readyState = 1; if (ws.onopen) ws.onopen({}); }, 0);
        return ws;
    };

    // ═══ postMessage Attack Detection ═══
    var __realPostMessage = globalThis.postMessage;
    globalThis.postMessage = function(message, targetOrigin) {
        __call("postMessage.send", [
            typeof message === 'string' ? message.substring(0, 500) : JSON.stringify(message).substring(0, 500),
            targetOrigin || '*'
        ]);
        if (__realPostMessage) return __realPostMessage.apply(this, arguments);
    };

    // ═══ Web Worker Detection ═══
    globalThis.Worker = function(url) {
        __call("worker.create", [url]);
        return {
            postMessage: function(msg) { __call("worker.postMessage", [url, JSON.stringify(msg).substring(0, 500)]); },
            terminate: function() { __call("worker.terminate", [url]); },
            addEventListener: function() {},
            onmessage: null, onerror: null
        };
    };
    globalThis.SharedWorker = function(url) {
        __call("sharedworker.create", [url]);
        return { port: { postMessage: function() {}, addEventListener: function() {}, start: function() {} } };
    };

    // ═══ Crypto Wallet Drainer Detection ═══
    // Fake window.ethereum (MetaMask, injected wallets)
    globalThis.ethereum = {
        isMetaMask: true,
        selectedAddress: '0x742d35Cc6634C0532925a3b844Bc9e7595f2bD38',
        chainId: '0x1',
        networkVersion: '1',
        request: function(args) {
            var method = args && args.method || 'unknown';
            var params = args && args.params || [];
            __call("ethereum.request", [method, JSON.stringify(params).substring(0, 1000)]);
            if (method === 'eth_requestAccounts' || method === 'eth_accounts') {
                return Promise.resolve(['0x742d35Cc6634C0532925a3b844Bc9e7595f2bD38']);
            }
            if (method === 'eth_chainId') return Promise.resolve('0x1');
            if (method === 'net_version') return Promise.resolve('1');
            if (method === 'eth_getBalance') return Promise.resolve('0x56BC75E2D63100000');
            if (method === 'eth_signTransaction') {
                __call("crypto.sign_transaction", [JSON.stringify(params).substring(0, 1000)]);
                return Promise.resolve('0x' + '0'.repeat(64));
            }
            if (method === 'eth_sendTransaction') {
                __call("crypto.send_transaction", [JSON.stringify(params).substring(0, 1000)]);
                return Promise.resolve('0x' + '0'.repeat(64));
            }
            if (method === 'personal_sign' || method === 'eth_sign' || method === 'eth_signTypedData' || method === 'eth_signTypedData_v4') {
                __call("crypto.sign_message", [method, JSON.stringify(params).substring(0, 1000)]);
                return Promise.resolve('0x' + '0'.repeat(130));
            }
            if (method === 'wallet_addEthereumChain' || method === 'wallet_switchEthereumChain') {
                __call("crypto.chain_switch", [JSON.stringify(params).substring(0, 500)]);
                return Promise.resolve(null);
            }
            if (method === 'eth_call') return Promise.resolve('0x');
            if (method === 'eth_estimateGas') return Promise.resolve('0x5208');
            return Promise.resolve(null);
        },
        on: function(event, cb) { __call("ethereum.on", [event]); },
        enable: function() {
            __call("ethereum.request", ["eth_requestAccounts", "[]"]);
            return Promise.resolve(['0x742d35Cc6634C0532925a3b844Bc9e7595f2bD38']);
        },
        send: function(method, params) {
            // Handle both old (send({method,params})) and new (send(method, params)) API styles
            if (typeof method === 'object' && method !== null && method.method) {
                return globalThis.ethereum.request(method);
            }
            return globalThis.ethereum.request({ method: method, params: params || [] });
        },
        sendAsync: function(payload, cb) {
            globalThis.ethereum.request(payload).then(function(r) { if (cb) cb(null, { result: r }); });
        },
    };

    // Solana wallet adapter (Phantom, Solflare)
    globalThis.solana = {
        isPhantom: true,
        publicKey: { toString: function() { return '7EcDhSYGxXyscszYEp35KHN8vvw3svAuLKTzXwCFLtV'; }, toBytes: function() { return new Uint8Array(32); } },
        connect: function() {
            __call("solana.connect", []);
            return Promise.resolve({ publicKey: globalThis.solana.publicKey });
        },
        disconnect: function() { return Promise.resolve(); },
        signTransaction: function(tx) {
            __call("crypto.solana_sign_tx", [JSON.stringify(tx).substring(0, 1000)]);
            return Promise.resolve(tx);
        },
        signAllTransactions: function(txs) {
            __call("crypto.solana_sign_all_tx", [txs.length.toString()]);
            return Promise.resolve(txs);
        },
        signMessage: function(msg) {
            __call("crypto.solana_sign_msg", [typeof msg === 'string' ? msg.substring(0, 500) : msg.length.toString()]);
            return Promise.resolve({ signature: new Uint8Array(64) });
        },
        signAndSendTransaction: function(tx) {
            __call("crypto.solana_sign_send_tx", [JSON.stringify(tx).substring(0, 1000)]);
            return Promise.resolve({ signature: '0'.repeat(128) });
        },
        on: function(event, cb) { __call("solana.on", [event]); },
        isConnected: true,
    };

    // ═══ Clipboard Hijacking Detection ═══
    if (!globalThis.navigator) globalThis.navigator = {};
    globalThis.navigator.clipboard = {
        writeText: function(text) {
            __call("clipboard.writeText", [text.substring(0, 1000)]);
            return Promise.resolve();
        },
        readText: function() {
            __call("clipboard.readText", []);
            return Promise.resolve('');
        },
        write: function(data) {
            __call("clipboard.write", [JSON.stringify(data).substring(0, 500)]);
            return Promise.resolve();
        },
        read: function() {
            __call("clipboard.read", []);
            return Promise.resolve([]);
        },
    };

    // ═══ Notification / Push API Abuse Detection ═══
    globalThis.Notification = function(title, opts) {
        __call("notification.create", [title, JSON.stringify(opts || {}).substring(0, 500)]);
    };
    globalThis.Notification.permission = 'default';
    globalThis.Notification.requestPermission = function() {
        __call("notification.requestPermission", []);
        return Promise.resolve('granted');
    };

    // ═══ Payment Request API Detection ═══
    globalThis.PaymentRequest = function(methods, details) {
        __call("payment.request", [JSON.stringify(methods).substring(0, 500), JSON.stringify(details).substring(0, 500)]);
        this.show = function() {
            __call("payment.show", []);
            return Promise.reject(new Error("Payment cancelled by sandbox"));
        };
        this.abort = function() { return Promise.resolve(); };
    };

    // ═══ Geolocation Tracking Detection ═══
    if (!globalThis.navigator.geolocation) {
        globalThis.navigator.geolocation = {
            getCurrentPosition: function(success, error) {
                __call("geolocation.getCurrentPosition", []);
                if (success) success({ coords: { latitude: 37.7749, longitude: -122.4194, accuracy: 100 } });
            },
            watchPosition: function(success, error) {
                __call("geolocation.watchPosition", []);
                return 1;
            },
        };
    }

    // ═══ WASM Boundary Taint Tracking & Crypto Miner Detection ═══
    var __RealWA = globalThis.WebAssembly;
    
    globalThis.WebAssembly = {
        compile: function(bytes) {
            __call("wasm.compile", [bytes ? (bytes.byteLength || bytes.length || 0) : 0]);
            if (__RealWA) return __RealWA.compile(bytes);
            return Promise.reject(new Error("blocked by sandbox"));
        },
        instantiate: function(bytes, imports) {
            var size = bytes ? (bytes.byteLength || bytes.length || 0) : 0;
            var importNames = imports ? Object.keys(imports).join(',') : '';
            __call("wasm.instantiate", [size, importNames]);
            
            var wrappedImports = {};
            if (imports) {
                for (var mod in imports) {
                    wrappedImports[mod] = {};
                    for (var key in imports[mod]) {
                        var val = imports[mod][key];
                        if (typeof val === 'function') {
                            wrappedImports[mod][key] = (function(orig, m, k) {
                                return function() {
                                    for (var i = 0; i < arguments.length; i++) {
                                        if (typeof arguments[i] === 'string' && typeof __jsdet_get_taint === 'function') {
                                            var label = __jsdet_get_taint(arguments[i]);
                                            if (label > 0) {
                                                __call("taint_sink_reached", ["wasm.import." + m + "." + k, arguments[i], String(label)]);
                                            }
                                        }
                                    }
                                    return orig.apply(this, arguments);
                                };
                            })(val, mod, key);
                        } else {
                            wrappedImports[mod][key] = val;
                        }
                    }
                }
            }
            
            var p;
            if (__RealWA) {
                p = __RealWA.instantiate(bytes, wrappedImports);
            } else {
                // Test with mock WASM module
                var mem = new ArrayBuffer(65536);
                mem.__jsdet_tainted = true; // Mark tainted memory regions
                
                var mockInstance = {
                    exports: {
                        memory: mem,
                        mock_export: function(arg) { return arg; }
                    }
                };
                p = Promise.resolve({ instance: mockInstance, module: {} });
            }
            
            return p.then(function(res) {
                var instance = res.instance || res;
                if (instance.exports) {
                    if (instance.exports.memory) {
                        instance.exports.memory.__jsdet_tainted = true;
                    }
                    var wrappedExports = {};
                    for (var exp in instance.exports) {
                        var val = instance.exports[exp];
                        if (typeof val === 'function') {
                            wrappedExports[exp] = (function(orig, e) {
                                return function() {
                                    var is_tainted = false;
                                    for (var i = 0; i < arguments.length; i++) {
                                        if (typeof arguments[i] === 'string' && typeof __jsdet_get_taint === 'function') {
                                            var label = __jsdet_get_taint(arguments[i]);
                                            if (label > 0) {
                                                __call("taint_sink_reached", ["wasm.export." + e, arguments[i], String(label)]);
                                                is_tainted = true;
                                            }
                                        }
                                    }
                                    var result = orig.apply(this, arguments);
                                    if (typeof result === 'string' && typeof __jsdet_set_taint === 'function') {
                                        if (is_tainted || (instance.exports.memory && instance.exports.memory.__jsdet_tainted)) {
                                            __jsdet_set_taint(result, 1); // Propagate back on export
                                        }
                                    }
                                    return result;
                                };
                            })(val, exp);
                        } else {
                            wrappedExports[exp] = val;
                        }
                    }
                    Object.defineProperty(instance, 'exports', { value: wrappedExports, configurable: true, writable: true });
                }
                return res;
            });
        },
        Module: function(bytes) {
            __call("wasm.module", [bytes ? (bytes.byteLength || bytes.length || 0) : 0]);
            if (__RealWA) return new __RealWA.Module(bytes);
            return {};
        },
        Instance: function() { return {}; },
        Memory: function(desc) { return { buffer: new ArrayBuffer((desc && desc.initial || 1) * 65536) }; },
        Table: function() { return { get: function() { return null; } }; },
        validate: function() { return true; },
    };
})();
