Created
February 6, 2026 18:12
-
-
Save HackingLZ/3fd449a884dad5375f444507e08ea124 to your computer and use it in GitHub Desktop.
Browser Fingerprint Analyzer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>VexTrio Fingerprint Analyzer</title> | |
| <link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@300;400;500;600;700&family=Space+Grotesk:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet"> | |
| <style> | |
| *,*::before,*::after{box-sizing:border-box;margin:0;padding:0} | |
| :root{ | |
| --bg-deep:#07080a;--bg-primary:#0c0e12;--bg-card:#11141a;--bg-card-hover:#161a22; | |
| --bg-elevated:#1a1e28;--border:#1e2330;--border-active:#2a3045; | |
| --text-primary:#e2e6ef;--text-secondary:#8891a5;--text-muted:#555d72; | |
| --green:#00e5a0;--green-dim:#00e5a033;--green-glow:#00e5a066; | |
| --red:#ff3b5c;--red-dim:#ff3b5c33;--red-glow:#ff3b5c66; | |
| --cyan:#00d4ff;--cyan-dim:#00d4ff22;--cyan-glow:#00d4ff44; | |
| --amber:#ffb020;--amber-dim:#ffb02033; | |
| --purple:#a78bfa; | |
| --font-mono:'JetBrains Mono','IBM Plex Mono',monospace; | |
| --font-display:'IBM Plex Mono',monospace; | |
| } | |
| html{font-size:14px;scroll-behavior:smooth} | |
| body{ | |
| background:var(--bg-deep);color:var(--text-primary); | |
| font-family:var(--font-mono);line-height:1.6; | |
| min-height:100vh;overflow-x:hidden; | |
| } | |
| body::before{ | |
| content:'';position:fixed;inset:0;z-index:0;pointer-events:none; | |
| background: | |
| radial-gradient(ellipse 80% 50% at 50% -20%,#00e5a008,transparent), | |
| radial-gradient(ellipse 60% 40% at 80% 100%,#00d4ff06,transparent), | |
| repeating-linear-gradient(0deg,transparent,transparent 1px,#ffffff01 1px,#ffffff01 2px); | |
| background-size:100% 100%,100% 100%,100% 4px; | |
| } | |
| .wrapper{position:relative;z-index:1;max-width:1440px;margin:0 auto;padding:24px 28px 60px} | |
| /* ── HEADER ── */ | |
| .header{ | |
| display:flex;align-items:flex-start;justify-content:space-between;flex-wrap:wrap;gap:16px; | |
| padding:28px 0 24px;border-bottom:1px solid var(--border);margin-bottom:28px; | |
| } | |
| .header-left h1{ | |
| font-family:var(--font-display);font-size:1.6rem;font-weight:700;letter-spacing:-0.5px; | |
| background:linear-gradient(135deg,var(--green),var(--cyan));-webkit-background-clip:text; | |
| -webkit-text-fill-color:transparent;background-clip:text; | |
| } | |
| .header-left .subtitle{font-size:.75rem;color:var(--text-muted);margin-top:4px;max-width:520px} | |
| .header-left .subtitle span{color:var(--amber)} | |
| .header-actions{display:flex;gap:10px;flex-wrap:wrap;align-items:center} | |
| .btn{ | |
| display:inline-flex;align-items:center;gap:6px;padding:8px 16px;border-radius:6px; | |
| font-family:var(--font-mono);font-size:.75rem;font-weight:500;cursor:pointer; | |
| border:1px solid var(--border);background:var(--bg-card);color:var(--text-primary); | |
| transition:all .2s;white-space:nowrap; | |
| } | |
| .btn:hover{background:var(--bg-elevated);border-color:var(--border-active)} | |
| .btn-primary{background:var(--green);color:#07080a;border-color:var(--green);font-weight:600} | |
| .btn-primary:hover{box-shadow:0 0 20px var(--green-dim);filter:brightness(1.1)} | |
| .btn-danger{border-color:var(--red-dim);color:var(--red)} | |
| .btn-danger:hover{background:var(--red-dim);border-color:var(--red)} | |
| .btn svg{width:14px;height:14px} | |
| /* ── VERDICT BANNER ── */ | |
| .verdict-strip{ | |
| display:grid;grid-template-columns:1fr 1fr 1fr 1fr;gap:16px;margin-bottom:28px; | |
| } | |
| .verdict-card{ | |
| background:var(--bg-card);border:1px solid var(--border);border-radius:10px;padding:18px 20px; | |
| position:relative;overflow:hidden; | |
| } | |
| .verdict-card::before{ | |
| content:'';position:absolute;top:0;left:0;right:0;height:2px; | |
| background:linear-gradient(90deg,transparent,var(--cyan),transparent);opacity:.5; | |
| } | |
| .verdict-card.verdict-main::before{background:linear-gradient(90deg,transparent,var(--green),transparent)} | |
| .verdict-card .label{font-size:.65rem;text-transform:uppercase;letter-spacing:1.5px;color:var(--text-muted);margin-bottom:6px} | |
| .verdict-card .value{font-size:1.1rem;font-weight:600;word-break:break-all} | |
| .verdict-card .value.pass{color:var(--green)} | |
| .verdict-card .value.fail{color:var(--red)} | |
| .verdict-card .value.pending{color:var(--text-muted)} | |
| .verdict-card .detail{font-size:.7rem;color:var(--text-secondary);margin-top:4px} | |
| /* ── CONTROLS BAR ── */ | |
| .controls-bar{ | |
| display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:12px; | |
| margin-bottom:20px;padding:12px 16px;background:var(--bg-card); | |
| border:1px solid var(--border);border-radius:8px; | |
| } | |
| .controls-bar .left{display:flex;gap:8px;flex-wrap:wrap} | |
| .controls-bar .right{display:flex;align-items:center;gap:12px} | |
| .stats{font-size:.72rem;color:var(--text-secondary)} | |
| .stats .num{font-weight:600;color:var(--green)} | |
| .stats .num.bad{color:var(--red)} | |
| /* ── TOGGLE ── */ | |
| .toggle{ | |
| position:relative;width:36px;height:20px;flex-shrink:0; | |
| } | |
| .toggle input{opacity:0;width:0;height:0;position:absolute} | |
| .toggle .slider{ | |
| position:absolute;inset:0;cursor:pointer;border-radius:20px; | |
| background:var(--bg-deep);border:1px solid var(--border);transition:all .25s; | |
| } | |
| .toggle .slider::before{ | |
| content:'';position:absolute;width:14px;height:14px;border-radius:50%; | |
| left:2px;bottom:2px;background:var(--text-muted);transition:all .25s; | |
| } | |
| .toggle input:checked+.slider{background:var(--green-dim);border-color:var(--green)} | |
| .toggle input:checked+.slider::before{transform:translateX(16px);background:var(--green)} | |
| /* ── CHECK GRID ── */ | |
| .checks-grid{ | |
| display:grid;grid-template-columns:repeat(auto-fill,minmax(420px,1fr));gap:12px; | |
| margin-bottom:32px; | |
| } | |
| .check-card{ | |
| background:var(--bg-card);border:1px solid var(--border);border-radius:8px; | |
| overflow:hidden;transition:all .25s; | |
| } | |
| .check-card:hover{border-color:var(--border-active);background:var(--bg-card-hover)} | |
| .check-card.disabled{opacity:.4} | |
| .check-card.pass{border-left:3px solid var(--green)} | |
| .check-card.fail{border-left:3px solid var(--red)} | |
| .check-card.error{border-left:3px solid var(--amber)} | |
| .check-card.pending{border-left:3px solid var(--text-muted)} | |
| .check-top{ | |
| display:flex;align-items:center;gap:12px;padding:14px 16px;cursor:pointer; | |
| user-select:none; | |
| } | |
| .check-status{ | |
| width:28px;height:28px;border-radius:6px;display:flex;align-items:center;justify-content:center; | |
| font-size:.7rem;font-weight:700;flex-shrink:0; | |
| } | |
| .check-card.pass .check-status{background:var(--green-dim);color:var(--green)} | |
| .check-card.fail .check-status{background:var(--red-dim);color:var(--red)} | |
| .check-card.error .check-status{background:var(--amber-dim);color:var(--amber)} | |
| .check-card.pending .check-status{background:#ffffff08;color:var(--text-muted)} | |
| .check-info{flex:1;min-width:0} | |
| .check-info .check-name{font-size:.72rem;font-weight:600;color:var(--text-primary);line-height:1.3} | |
| .check-info .check-desc{font-size:.65rem;color:var(--text-muted);margin-top:2px;line-height:1.3} | |
| .check-right{display:flex;align-items:center;gap:10px} | |
| .check-code{ | |
| font-size:.65rem;font-weight:600;padding:3px 8px;border-radius:4px; | |
| background:var(--bg-deep);border:1px solid var(--border);color:var(--text-secondary); | |
| white-space:nowrap; | |
| } | |
| .check-card.pass .check-code{color:var(--green);border-color:var(--green-dim)} | |
| .check-card.fail .check-code{color:var(--red);border-color:var(--red-dim)} | |
| .check-expand{ | |
| color:var(--text-muted);transition:transform .2s;font-size:10px; | |
| } | |
| .check-card.open .check-expand{transform:rotate(180deg)} | |
| .check-details{ | |
| display:none;padding:0 16px 14px;border-top:1px solid var(--border);margin-top:0; | |
| } | |
| .check-card.open .check-details{display:block;padding-top:12px} | |
| .detail-row{display:flex;gap:8px;margin-bottom:6px;font-size:.65rem} | |
| .detail-row .dk{color:var(--cyan);min-width:140px;flex-shrink:0} | |
| .detail-row .dv{color:var(--text-secondary);word-break:break-all} | |
| .detail-logic{ | |
| margin-top:10px;padding:10px 12px;background:var(--bg-deep);border-radius:6px; | |
| font-size:.63rem;color:var(--text-muted);line-height:1.7;white-space:pre-wrap; | |
| } | |
| .detail-logic .kw{color:var(--purple)} | |
| .detail-logic .val{color:var(--green)} | |
| .detail-logic .bad{color:var(--red)} | |
| /* ── RAW DATA PANEL ── */ | |
| .raw-panel{ | |
| background:var(--bg-card);border:1px solid var(--border);border-radius:10px; | |
| overflow:hidden; | |
| } | |
| .raw-panel-header{ | |
| display:flex;align-items:center;justify-content:space-between; | |
| padding:16px 20px;cursor:pointer;user-select:none; | |
| border-bottom:1px solid var(--border); | |
| } | |
| .raw-panel-header h2{font-size:.85rem;font-weight:600;color:var(--cyan)} | |
| .raw-panel-body{padding:20px;display:none} | |
| .raw-panel.open .raw-panel-body{display:block} | |
| .raw-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:12px} | |
| .raw-item{padding:10px 14px;background:var(--bg-deep);border-radius:6px;border:1px solid var(--border)} | |
| .raw-item .rk{font-size:.63rem;color:var(--cyan);text-transform:uppercase;letter-spacing:1px;margin-bottom:4px} | |
| .raw-item .rv{font-size:.72rem;color:var(--text-secondary);word-break:break-all;line-height:1.5} | |
| /* ── ANIMATIONS ── */ | |
| @keyframes fadeIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}} | |
| @keyframes pulse{0%,100%{opacity:1}50%{opacity:.5}} | |
| @keyframes scan{0%{transform:translateX(-100%)}100%{transform:translateX(100%)}} | |
| .check-card{animation:fadeIn .3s ease both} | |
| .scanning .verdict-card.verdict-main::after{ | |
| content:'';position:absolute;top:0;left:0;width:100%;height:2px; | |
| background:linear-gradient(90deg,transparent,var(--green),transparent); | |
| animation:scan 1s ease infinite; | |
| } | |
| /* ── RESPONSIVE ── */ | |
| @media(max-width:900px){ | |
| .verdict-strip{grid-template-columns:1fr 1fr} | |
| .checks-grid{grid-template-columns:1fr} | |
| } | |
| @media(max-width:540px){ | |
| .wrapper{padding:16px 14px 40px} | |
| .verdict-strip{grid-template-columns:1fr} | |
| .header{flex-direction:column} | |
| } | |
| /* ── CODE GENERATOR PANEL ── */ | |
| .codegen-panel{ | |
| background:var(--bg-card);border:1px solid var(--border);border-radius:10px; | |
| overflow:hidden;margin-bottom:20px; | |
| } | |
| .codegen-panel-header{ | |
| display:flex;align-items:center;justify-content:space-between; | |
| padding:16px 20px;cursor:pointer;user-select:none; | |
| border-bottom:1px solid var(--border); | |
| } | |
| .codegen-panel-header h2{font-size:.85rem;font-weight:600;color:var(--green)} | |
| .codegen-panel-body{padding:20px;display:none} | |
| .codegen-panel.open .codegen-panel-body{display:block} | |
| .codegen-config{ | |
| display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:12px; | |
| margin-bottom:16px; | |
| } | |
| .codegen-field label{ | |
| display:block;font-size:.63rem;text-transform:uppercase;letter-spacing:1px; | |
| color:var(--text-muted);margin-bottom:4px; | |
| } | |
| .codegen-field input,.codegen-field select{ | |
| width:100%;padding:8px 12px;font-family:var(--font-mono);font-size:.75rem; | |
| background:var(--bg-deep);border:1px solid var(--border);border-radius:6px; | |
| color:var(--text-primary);outline:none;transition:border-color .2s; | |
| } | |
| .codegen-field input:focus,.codegen-field select:focus{border-color:var(--green)} | |
| .codegen-field select option{background:var(--bg-deep);color:var(--text-primary)} | |
| .codegen-actions{display:flex;gap:10px;margin-bottom:16px;flex-wrap:wrap;align-items:center} | |
| .codegen-actions .gen-info{font-size:.65rem;color:var(--text-muted);margin-left:auto} | |
| .code-output-wrap{position:relative} | |
| .code-output{ | |
| background:#0a0c10;border:1px solid var(--border);border-radius:8px; | |
| padding:16px 20px;font-size:.7rem;line-height:1.7; | |
| max-height:600px;overflow:auto;white-space:pre;color:var(--text-secondary); | |
| tab-size:2;-moz-tab-size:2; | |
| } | |
| .code-output .cm-kw{color:var(--purple)} | |
| .code-output .cm-fn{color:var(--cyan)} | |
| .code-output .cm-str{color:var(--green)} | |
| .code-output .cm-cmt{color:var(--text-muted);font-style:italic} | |
| .code-output .cm-num{color:var(--amber)} | |
| .code-output .cm-ret{color:var(--red)} | |
| .code-output .cm-var{color:#e0def4} | |
| .copy-btn{ | |
| position:absolute;top:8px;right:8px;padding:6px 12px; | |
| font-family:var(--font-mono);font-size:.65rem;font-weight:500; | |
| background:var(--bg-elevated);border:1px solid var(--border);border-radius:4px; | |
| color:var(--text-secondary);cursor:pointer;transition:all .2s;z-index:2; | |
| } | |
| .copy-btn:hover{background:var(--green);color:#07080a;border-color:var(--green)} | |
| .copy-btn.copied{background:var(--green);color:#07080a;border-color:var(--green)} | |
| </style> | |
| </head> | |
| <body> | |
| <div class="wrapper"> | |
| <!-- HEADER --> | |
| <div class="header"> | |
| <div class="header-left"> | |
| <h1>>_ VexTrio Fingerprint Analyzer</h1> | |
| <div class="subtitle"> | |
| <span>EDUCATIONAL / SECURITY RESEARCH TOOL</span> — Implements all 29 browser fingerprinting checks used by VexTrio TDS. Test your browser to see what a malicious traffic distribution system can detect. | |
| <br>Source: <a href="https://gi7w0rm.medium.com/vextrios-browser-fingerprinting-aeb721be6e30" target="_blank" style="color:var(--cyan);text-decoration:none">gi7w0rm's analysis</a> | |
| </div> | |
| </div> | |
| <div class="header-actions"> | |
| <button class="btn btn-primary" onclick="runAllChecks()"> | |
| <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polygon points="5 3 19 12 5 21 5 3"/></svg> | |
| Run All Checks | |
| </button> | |
| <button class="btn" onclick="resetAll()"> | |
| <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 4v6h6M23 20v-6h-6"/><path d="M20.49 9A9 9 0 0 0 5.64 5.64L1 10m22 4l-4.64 4.36A9 9 0 0 1 3.51 15"/></svg> | |
| Reset | |
| </button> | |
| </div> | |
| </div> | |
| <!-- VERDICT STRIP --> | |
| <div class="verdict-strip" id="verdictStrip"> | |
| <div class="verdict-card verdict-main"> | |
| <div class="label">Overall Verdict</div> | |
| <div class="value pending" id="verdictValue">WAITING</div> | |
| <div class="detail" id="verdictDetail">Click "Run All Checks" to begin</div> | |
| </div> | |
| <div class="verdict-card"> | |
| <div class="label">Detected OS</div> | |
| <div class="value" id="detectedOS" style="color:var(--cyan)">--</div> | |
| <div class="detail" id="detectedOSDetail">via UserAgent</div> | |
| </div> | |
| <div class="verdict-card"> | |
| <div class="label">Detected Browser</div> | |
| <div class="value" id="detectedBrowser" style="color:var(--cyan)">--</div> | |
| <div class="detail" id="detectedBrowserDetail">via UserAgent</div> | |
| </div> | |
| <div class="verdict-card"> | |
| <div class="label">WebGL Vendor / Renderer</div> | |
| <div class="value" id="webglInfo" style="color:var(--purple);font-size:.8rem">--</div> | |
| <div class="detail" id="webglDetail">GPU fingerprint</div> | |
| </div> | |
| </div> | |
| <!-- CONTROLS BAR --> | |
| <div class="controls-bar"> | |
| <div class="left"> | |
| <button class="btn" onclick="enableAll()">Enable All</button> | |
| <button class="btn" onclick="disableAll()">Disable All</button> | |
| <button class="btn" onclick="expandAll()">Expand All</button> | |
| <button class="btn" onclick="collapseAll()">Collapse All</button> | |
| </div> | |
| <div class="right"> | |
| <span class="stats"> | |
| Passed: <span class="num" id="statPass">0</span> | | |
| Failed: <span class="num bad" id="statFail">0</span> | | |
| Errors: <span class="num" style="color:var(--amber)" id="statErr">0</span> | | |
| Disabled: <span class="num" style="color:var(--text-muted)" id="statOff">0</span> | |
| </span> | |
| </div> | |
| </div> | |
| <!-- CHECKS GRID --> | |
| <div class="checks-grid" id="checksGrid"></div> | |
| <!-- CODE GENERATOR PANEL --> | |
| <div class="codegen-panel open" id="codegenPanel"> | |
| <div class="codegen-panel-header" onclick="document.getElementById('codegenPanel').classList.toggle('open')"> | |
| <h2>> Code Generator</h2> | |
| <span class="check-expand">▼</span> | |
| </div> | |
| <div class="codegen-panel-body"> | |
| <div class="codegen-config"> | |
| <div class="codegen-field"> | |
| <label>Function Name</label> | |
| <input type="text" id="cgFnName" value="chk" placeholder="chk"> | |
| </div> | |
| <div class="codegen-field"> | |
| <label>Pass Return Value</label> | |
| <input type="text" id="cgPassVal" value="a0:0" placeholder="a0:0"> | |
| </div> | |
| <div class="codegen-field"> | |
| <label>Fail Return Value</label> | |
| <input type="text" id="cgFailVal" value="a0:1" placeholder="a0:1"> | |
| </div> | |
| <div class="codegen-field"> | |
| <label>Error Return Value</label> | |
| <input type="text" id="cgErrVal" value="a0:e" placeholder="a0:e"> | |
| </div> | |
| <div class="codegen-field"> | |
| <label>Output Style</label> | |
| <select id="cgStyle"> | |
| <option value="chained">Chained if/else (VexTrio)</option> | |
| <option value="loop">Loop over array</option> | |
| <option value="standalone">Standalone functions</option> | |
| </select> | |
| </div> | |
| <div class="codegen-field"> | |
| <label>Include Helpers</label> | |
| <select id="cgHelpers"> | |
| <option value="yes">Yes (self-contained)</option> | |
| <option value="no">No (assume global)</option> | |
| </select> | |
| </div> | |
| </div> | |
| <div class="codegen-actions"> | |
| <button class="btn btn-primary" onclick="generateCode()"> | |
| <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" style="width:14px;height:14px"><path d="M16 18l6-6-6-6M8 6l-6 6 6 6"/></svg> | |
| Generate JS | |
| </button> | |
| <button class="btn" onclick="downloadCode()"> | |
| <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:14px;height:14px"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M7 10l5 5 5-5M12 15V3"/></svg> | |
| Download .js | |
| </button> | |
| <span class="gen-info" id="cgInfo">Toggle checks above, configure outputs, then generate</span> | |
| </div> | |
| <div class="code-output-wrap"> | |
| <button class="copy-btn" id="copyBtn" onclick="copyCode()">Copy</button> | |
| <div class="code-output" id="codeOutput">// Toggle checks on/off above, set your pass/fail values, then click "Generate JS"</div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- RAW DATA PANEL --> | |
| <div class="raw-panel" id="rawPanel"> | |
| <div class="raw-panel-header" onclick="document.getElementById('rawPanel').classList.toggle('open')"> | |
| <h2>> Raw Browser Data</h2> | |
| <span class="check-expand">▼</span> | |
| </div> | |
| <div class="raw-panel-body"> | |
| <div class="raw-grid" id="rawGrid"></div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // ═══════════════════════════════════════════════════════ | |
| // HELPER FUNCTIONS (exact VexTrio implementations) | |
| // ═══════════════════════════════════════════════════════ | |
| function CHECK_DEVICE_IN_USERAGENT_STRING() { | |
| var c = navigator.userAgent.toLowerCase(); | |
| if (c.indexOf("windows phone") >= 0) return "Windows Phone"; | |
| if (c.indexOf("win") >= 0) return "Windows"; | |
| if (c.indexOf("kaios") >= 0) return "Kaios"; | |
| if (c.indexOf("android") >= 0 || c.indexOf("spreadtrum") >= 0) return "Android"; | |
| if (c.indexOf("linux") >= 0 || c.indexOf("cros") >= 0) return "Linux"; | |
| if (c.indexOf("iphone") >= 0 || c.indexOf("ipad") >= 0) return "iOS"; | |
| if (c.indexOf("mac") >= 0) return "Mac"; | |
| return "Other"; | |
| } | |
| function DETECT_Browser_Type() { | |
| var c = navigator.userAgent; | |
| if (c.indexOf("OPR/") !== -1 || c.indexOf("Opera") !== -1) return "Opera"; | |
| if ((c.indexOf("MSIE") !== -1 || c.indexOf("Trident") !== -1) && c.indexOf("MAXTHON") === -1) return "Internet Explorer"; | |
| if (c.indexOf("Edge") !== -1 || c.indexOf("EdgA") !== -1) return "Edge"; | |
| if (c.indexOf("SamsungBrowser") !== -1) return "Samsung Browser"; | |
| if (c.indexOf("UCBrowser") !== -1) return "UC Browser"; | |
| if (c.indexOf("Android") !== -1 && c.indexOf("Chrome") === -1 && c.indexOf("Firefox") === -1) return "Android Browser"; | |
| if (c.indexOf("Chrome") !== -1 || c.indexOf("CriOS") !== -1) return "Chrome"; | |
| if (c.indexOf("Safari") !== -1 && c.indexOf("Chrome") === -1) return "Safari"; | |
| if (c.indexOf("Firefox") !== -1) return "Firefox"; | |
| return "Other"; | |
| } | |
| function HELPER_WEBGL_DEBUG_TOKENS_FOR_RENDERER_AND_VENDOR() { | |
| try { | |
| var c = document.createElement("canvas"); | |
| var d = c.getContext("webgl"); | |
| var e = d.getExtension("WEBGL_debug_renderer_info"); | |
| var f = d.getParameter(e.UNMASKED_VENDOR_WEBGL); | |
| var g = d.getParameter(e.UNMASKED_RENDERER_WEBGL); | |
| return [f, g]; | |
| } catch (h) { return false; } | |
| } | |
| var voiceslist = ""; | |
| function populateVoiceList() { | |
| try { | |
| var c = speechSynthesis.getVoices(); | |
| if (c.length !== 0) { | |
| var d = ""; | |
| for (var f = 0; f < c.length; f++) { d = d + " " + c[f].name; } | |
| voiceslist = d; | |
| } else { | |
| setTimeout(function() { populateVoiceList(); }, 50); | |
| } | |
| } catch (g) {} | |
| } | |
| try { populateVoiceList(); if (typeof speechSynthesis !== 'undefined') speechSynthesis.onvoiceschanged = populateVoiceList; } catch(e) {} | |
| var permissionsFlag = false; | |
| function getPermissionStatus() { | |
| try { | |
| var browser = DETECT_Browser_Type(); | |
| if (browser !== "Samsung Browser" && browser !== "Firefox") { | |
| navigator.permissions.query({ name: "notifications" }).then(function(c) { | |
| if (typeof Notification !== 'undefined' && Notification.permission === "denied" && c.state === "prompt") { | |
| permissionsFlag = true; | |
| } else { | |
| permissionsFlag = false; | |
| } | |
| }); | |
| } | |
| } catch (c) {} | |
| } | |
| getPermissionStatus(); | |
| // ═══════════════════════════════════════════════════════ | |
| // ALL 29 CHECKS | |
| // ═══════════════════════════════════════════════════════ | |
| const CHECKS = [ | |
| { | |
| id: 'a1', num: 1, | |
| name: 'Language ISO Code Mismatch', | |
| fnName: 'CHECK_COMPARE_ISO_CODE_LANGUAGE_ARRAY_VS_ISO_CODE_PRIMARY_LANGUAGE', | |
| desc: 'Compares navigator.language with first entry in navigator.languages array', | |
| run: function() { | |
| var data = {}; | |
| if (typeof navigator.languages !== "undefined") { | |
| try { | |
| data.language = navigator.language; | |
| data.languages0 = navigator.languages[0]; | |
| var c = navigator.languages[0].substr(0, 2); | |
| data.iso_languages0 = c; | |
| data.iso_language = navigator.language.substr(0, 2); | |
| if (c !== navigator.language.substr(0, 2)) { | |
| return { result: "a1:1", data: data, logic: `languages[0] ISO "${c}" !== language ISO "${navigator.language.substr(0,2)}" → FAIL` }; | |
| } | |
| } catch (d) { | |
| return { result: "a1:e", data: data, logic: 'Exception thrown during comparison' }; | |
| } | |
| } else { | |
| data.languages = 'undefined'; | |
| } | |
| return { result: "a1:0", data: data, logic: `languages[0] ISO matches language ISO → PASS` }; | |
| } | |
| }, | |
| { | |
| id: 'a2', num: 2, | |
| name: 'Window vs Screen Size Mismatch', | |
| fnName: 'CHECK_COMPARE_AVAILABLE_WINDOW_SIZE_TO_CURRENT_WINDOW_SIZE', | |
| desc: 'Compares screen dimensions against available window dimensions', | |
| run: function() { | |
| try { | |
| var data = { | |
| 'screen.width': window.screen.width, 'screen.height': window.screen.height, | |
| 'screen.availWidth': window.screen.availWidth, 'screen.availHeight': window.screen.availHeight | |
| }; | |
| if (window.screen.width < window.screen.availWidth || window.screen.height < window.screen.availHeight) { | |
| if ((window.screen.width === window.screen.availHeight && window.screen.height === window.screen.availWidth) || | |
| (window.screen.width === window.screen.availHeight + 20 && window.screen.height === window.screen.availWidth)) { | |
| return { result: "a2:0", data: data, logic: 'Screen < avail but rotation edge case matched → PASS' }; | |
| } else { | |
| return { result: "a2:1", data: data, logic: `screen(${data['screen.width']}x${data['screen.height']}) < avail(${data['screen.availWidth']}x${data['screen.availHeight']}) → FAIL` }; | |
| } | |
| } | |
| return { result: "a2:0", data: data, logic: 'screen dimensions >= available dimensions → PASS' }; | |
| } catch (c) { return { result: "a2:e", data: {}, logic: 'Exception' }; } | |
| } | |
| }, | |
| { | |
| id: 'a3', num: 3, | |
| name: 'OSCPU vs UserAgent OS Mismatch', | |
| fnName: 'CHECK_COMPARE_OSCPU_TO_DEVICE_USERAGENT_STRING', | |
| desc: 'Compares navigator.oscpu with OS detected from UserAgent (Firefox-only property)', | |
| run: function() { | |
| try { | |
| var d = navigator.oscpu; | |
| var f = CHECK_DEVICE_IN_USERAGENT_STRING(); | |
| var data = { 'navigator.oscpu': String(d), 'UA_OS': f, 'typeof_oscpu': typeof d }; | |
| if (typeof d !== "undefined") { | |
| d = d.toLowerCase(); | |
| if (d === "" && f === "Kaios") return { result: "a3:0", data, logic: 'Empty oscpu + KaiOS → PASS' }; | |
| if (d.indexOf("win") >= 0 && f !== "Windows" && f !== "Windows Phone") return { result: "a3:1", data, logic: `oscpu="${d}" says Win but UA says "${f}" → FAIL` }; | |
| if (d.indexOf("linux") >= 0 && f !== "Linux" && f !== "Android") return { result: "a3:1", data, logic: `oscpu="${d}" says Linux but UA says "${f}" → FAIL` }; | |
| if (d.indexOf("mac") >= 0 && f !== "Mac" && f !== "iOS") return { result: "a3:1", data, logic: `oscpu="${d}" says Mac but UA says "${f}" → FAIL` }; | |
| if ((d.indexOf("win") === -1 && d.indexOf("linux") === -1 && d.indexOf("mac") === -1) !== (f === "Other")) | |
| return { result: "a3:1", data, logic: `oscpu/UA OS mismatch → FAIL` }; | |
| return { result: "a3:0", data, logic: 'oscpu matches UA OS → PASS' }; | |
| } | |
| return { result: "a3:0", data, logic: 'oscpu is undefined (non-Firefox) → PASS' }; | |
| } catch (g) { return { result: "a3:e", data: {}, logic: 'Exception' }; } | |
| } | |
| }, | |
| { | |
| id: 'a4', num: 4, | |
| name: 'OSCPU Defined on Non-Firefox', | |
| fnName: 'CHECK_OSCPU_NOT_UNDEFINED_AND_BROWSER_NOT_FIREFOX', | |
| desc: 'navigator.oscpu should only be defined in Firefox', | |
| run: function() { | |
| try { | |
| var c = navigator.oscpu; | |
| var d = DETECT_Browser_Type(); | |
| var data = { 'navigator.oscpu': String(c), 'typeof_oscpu': typeof c, browser: d }; | |
| if (typeof c !== "undefined" && d !== "Firefox") { | |
| return { result: "a4:1", data, logic: `oscpu defined (${c}) but browser is ${d}, not Firefox → FAIL` }; | |
| } | |
| return { result: "a4:0", data, logic: typeof c === "undefined" ? 'oscpu is undefined → PASS' : `oscpu defined but browser is Firefox → PASS` }; | |
| } catch (f) { return { result: "a4:e", data: {}, logic: 'Exception' }; } | |
| } | |
| }, | |
| { | |
| id: 'a5', num: 5, | |
| name: 'Platform vs UserAgent Mismatch', | |
| fnName: 'CHECK_COMPARE_PLATFORM_USERAGENT_DEVICEINUSERAGENT', | |
| desc: 'Cross-references navigator.platform with UserAgent OS detection', | |
| run: function() { | |
| try { | |
| var c = navigator.platform.toLowerCase(); | |
| var d = navigator.userAgent.toLowerCase(); | |
| var f = CHECK_DEVICE_IN_USERAGENT_STRING(); | |
| var data = { 'navigator.platform': navigator.platform, 'UA_OS': f, platform_lower: c }; | |
| if (c === "" && f === "Kaios") return { result: "a5:0", data, logic: 'Empty platform + KaiOS → PASS' }; | |
| if (d.indexOf("maui") >= 0 && c.indexOf("pike") >= 0) return { result: "a5:0", data, logic: 'MAUI + Pike → PASS' }; | |
| if (d.indexOf("j2me/midp") >= 0 && c.indexOf("pike") >= 0) return { result: "a5:0", data, logic: 'J2ME + Pike → PASS' }; | |
| if (c === "arm" && f === "Windows Phone") return { result: "a5:0", data, logic: 'ARM + WinPhone → PASS' }; | |
| if (c.indexOf("win") >= 0 && f !== "Windows" && f !== "Windows Phone") return { result: "a5:1", data, logic: `Platform says Win but UA says ${f} → FAIL` }; | |
| if ((c.indexOf("linux") >= 0 || c.indexOf("android") >= 0 || c.indexOf("pike") >= 0) && f !== "Linux" && f !== "Android" && f !== "Kaios") return { result: "a5:1", data, logic: `Platform says Linux/Android but UA says ${f} → FAIL` }; | |
| if ((c.indexOf("mac") >= 0 || c.indexOf("ipad") >= 0 || c.indexOf("ipod") >= 0 || c.indexOf("iphone") >= 0) && f !== "Mac" && f !== "iOS") return { result: "a5:1", data, logic: `Platform says Apple but UA says ${f} → FAIL` }; | |
| if (c === "macintel" && d.indexOf("iphone") >= 0) return { result: "a5:1", data, logic: `Platform=MacIntel but UA has iPhone → FAIL` }; | |
| var g = c.indexOf("win") < 0 && c.indexOf("linux") < 0 && c.indexOf("mac") < 0 && c.indexOf("iphone") < 0 && c.indexOf("pike") < 0 && c.indexOf("ipod") < 0 && c.indexOf("ipad") < 0; | |
| if (g !== (f === "Other")) return { result: "a5:1", data, logic: `Platform/UA "Other" mismatch → FAIL` }; | |
| return { result: "a5:0", data, logic: 'Platform matches UA OS → PASS' }; | |
| } catch (h) { return { result: "a5:e", data: {}, logic: 'Exception' }; } | |
| } | |
| }, | |
| { | |
| id: 'a6', num: 6, | |
| name: 'Plugins Undefined on Non-Windows', | |
| fnName: 'CHECK_PLUGINSUNDEFINED_BUT_USERAGENTOS_NOT_WINDOWS', | |
| desc: 'navigator.plugins should be defined on non-Windows platforms', | |
| run: function() { | |
| try { | |
| var c = CHECK_DEVICE_IN_USERAGENT_STRING(); | |
| var data = { 'typeof_plugins': typeof navigator.plugins, 'plugins_length': navigator.plugins ? navigator.plugins.length : 'N/A', 'UA_OS': c }; | |
| if (typeof navigator.plugins === "undefined" && c !== "Windows" && c !== "Windows Phone") { | |
| return { result: "a6:1", data, logic: `Plugins undefined but OS is ${c} (not Windows) → FAIL` }; | |
| } | |
| return { result: "a6:0", data, logic: typeof navigator.plugins === "undefined" ? 'Plugins undefined but OS is Windows → PASS' : `Plugins defined (${navigator.plugins.length} items) → PASS` }; | |
| } catch (d) { return { result: "a6:e", data: {}, logic: 'Exception' }; } | |
| } | |
| }, | |
| { | |
| id: 'a7', num: 7, | |
| name: 'Browser Build Number Check', | |
| fnName: 'CHECK_BROWSER_BUILDNUMBER_AGAINST_BROWSERTYPE', | |
| desc: 'Chrome/Safari/Opera should have productSub === "20030107"', | |
| run: function() { | |
| try { | |
| var c = navigator.productSub; | |
| var d = DETECT_Browser_Type(); | |
| var data = { 'navigator.productSub': String(c), browser: d }; | |
| if ((d === "Chrome" || d === "Safari") && c !== "20030107") return { result: "a7:1", data, logic: `${d} should have productSub=20030107, got "${c}" → FAIL` }; | |
| if (d === "Opera" && c !== "20030107" && typeof c !== "undefined") return { result: "a7:1", data, logic: `Opera should have productSub=20030107, got "${c}" → FAIL` }; | |
| return { result: "a7:0", data, logic: `productSub="${c}" is expected for ${d} → PASS` }; | |
| } catch (f) { return { result: "a7:e", data: {}, logic: 'Exception' }; } | |
| } | |
| }, | |
| { | |
| id: 'a8', num: 8, | |
| name: 'Eval Function Length Check', | |
| fnName: 'CHECK_LENGTH_OF_EVAL_FUNCTION_AGAINST_BROWSER_TYPE', | |
| desc: 'eval.toString().length varies by browser (37/39/33)', | |
| run: function() { | |
| try { | |
| var c = DETECT_Browser_Type(); | |
| var d = CHECK_DEVICE_IN_USERAGENT_STRING(); | |
| var f = eval.toString().length; | |
| var data = { 'eval.toString().length': f, browser: c, os: d }; | |
| if (f === 37 && c !== "Safari" && c !== "Firefox" && c !== "Other" && d === "iOS" && c !== "Chrome") | |
| return { result: "a8:1", data, logic: `eval length=37 but browser=${c}, os=${d} (unexpected combo) → FAIL` }; | |
| if (f === 39 && c !== "Internet Explorer" && c !== "Other") | |
| return { result: "a8:1", data, logic: `eval length=39 (IE) but browser=${c} → FAIL` }; | |
| if (f === 33 && c !== "Chrome" && c !== "Opera" && c !== "Edge" && c !== "UC Browser" && c !== "Samsung Browser" && c !== "Other" && c !== "Android Browser") | |
| return { result: "a8:1", data, logic: `eval length=33 (Chromium) but browser=${c} → FAIL` }; | |
| return { result: "a8:0", data, logic: `eval length=${f} matches expected for ${c} → PASS` }; | |
| } catch (g) { return { result: "a8:e", data: {}, logic: 'Exception' }; } | |
| } | |
| }, | |
| { | |
| id: 'a9', num: 9, | |
| name: 'toSource() Function Check', | |
| fnName: 'CHECK_Mozilla_UNIQUE_TOSOURCE_FKT_AGAINST_BROWSER_TYPE', | |
| desc: 'Obsolete toSource() should only exist in old Firefox', | |
| run: function() { | |
| try { | |
| var c = DETECT_Browser_Type(); | |
| var d; | |
| try { throw "a"; } catch (f) { | |
| try { f.toSource(); d = true; } catch (g) { d = false; } | |
| } | |
| var data = { 'toSource_exists': d, browser: c }; | |
| if (d && c !== "Firefox" && c !== "Other") return { result: "a9:1", data, logic: `toSource() exists but browser=${c} (not Firefox) → FAIL` }; | |
| return { result: "a9:0", data, logic: d ? `toSource() exists, browser=Firefox → PASS` : `toSource() does not exist → PASS` }; | |
| } catch (h) { return { result: "a9:e", data: {}, logic: 'Exception' }; } | |
| } | |
| }, | |
| { | |
| id: 'a10', num: 10, | |
| name: 'WebGL Vendor vs OS/Browser', | |
| fnName: 'CHECK_Compare_DEVICEUSERAGENTSTRING_to_BrowserType_to_WEBGL_DEBUG_TOKENS', | |
| desc: 'Cross-references WebGL GPU vendor with OS and browser type', | |
| run: function() { | |
| try { | |
| var c = CHECK_DEVICE_IN_USERAGENT_STRING(); | |
| var d = DETECT_Browser_Type(); | |
| var f = HELPER_WEBGL_DEBUG_TOKENS_FOR_RENDERER_AND_VENDOR(); | |
| var data = { UA_OS: c, browser: d, webgl_vendor: f ? f[0] : 'N/A', webgl_renderer: f ? f[1] : 'N/A' }; | |
| if (!f) return { result: "a10:0", data, logic: 'WebGL not available → PASS (skipped)' }; | |
| if (c === "iOS" && f[0].indexOf("Apple") === -1 && f[0].indexOf("Imagination Technologies") === -1) | |
| return { result: "a10:1", data, logic: `iOS but vendor="${f[0]}" (not Apple) → FAIL` }; | |
| if (c === "Mac" && f[0].indexOf("Intel") === -1 && f[0].indexOf("ATI Technologies") === -1 && f[0].indexOf("NVIDIA Corporation") === -1 && f[0].indexOf("Apple") === -1) | |
| return { result: "a10:1", data, logic: `Mac but vendor="${f[0]}" (unexpected) → FAIL` }; | |
| if (c === "Android" && (f[0] === "Google Inc. (NVIDIA)" || f[0] === "Google Inc. (Intel)" || f[0] === "Google Inc. (Google)" || f[0] === "Google Inc." || f[0].indexOf("NVIDIA Corporation") !== -1)) | |
| return { result: "a10:1", data, logic: `Android with suspicious Google/NVIDIA vendor → FAIL` }; | |
| if (c === "Windows" && d === "Edge" && f[0].indexOf("Microsoft") === -1) | |
| return { result: "a10:1", data, logic: `Windows Edge but vendor not Microsoft → FAIL` }; | |
| if (c === "Windows" && (d === "Chrome" || d === "Firefox") && f[0].indexOf("Google Inc") === -1) | |
| return { result: "a10:1", data, logic: `Windows ${d} but vendor not Google → FAIL` }; | |
| if (f[0].indexOf("VMware") !== -1) | |
| return { result: "a10:1", data, logic: `VMware detected in vendor → FAIL (VM detected)` }; | |
| return { result: "a10:0", data, logic: `WebGL vendor="${f[0]}" matches OS=${c} → PASS` }; | |
| } catch (g) { return { result: "a10:e", data: {}, logic: 'Exception' }; } | |
| } | |
| }, | |
| { | |
| id: 'a11', num: 11, | |
| name: 'WebDriver Detection', | |
| fnName: 'CHECK_Webdriver_in_Navigator_Interface', | |
| desc: 'Checks if browser is running under automation (navigator.webdriver)', | |
| run: function() { | |
| try { | |
| var c = "webdriver" in navigator && navigator.webdriver; | |
| var data = { 'navigator.webdriver': navigator.webdriver, '"webdriver" in navigator': "webdriver" in navigator, result: c }; | |
| if (c) return { result: "a11:1", data, logic: 'navigator.webdriver is true → FAIL (automation detected)' }; | |
| return { result: "a11:0", data, logic: 'navigator.webdriver is false/absent → PASS' }; | |
| } catch (d) { return { result: "a11:e", data: {}, logic: 'Exception' }; } | |
| } | |
| }, | |
| { | |
| id: 'a12', num: 12, | |
| name: 'Permission State Anomaly', | |
| fnName: 'CHECK_Permission_DENIED_in_State_Prompt', | |
| desc: 'Notification.permission="denied" + state="prompt" should never co-exist', | |
| run: function() { | |
| try { | |
| var data = { permissionsFlag: permissionsFlag, 'Notification.permission': typeof Notification !== 'undefined' ? Notification.permission : 'N/A' }; | |
| if (permissionsFlag) return { result: "a12:1", data, logic: 'Permission denied + state prompt anomaly → FAIL' }; | |
| return { result: "a12:0", data, logic: 'Permission states consistent → PASS' }; | |
| } catch (c) { return { result: "a12:e", data: {}, logic: 'Exception' }; } | |
| } | |
| }, | |
| { | |
| id: 'a13', num: 13, | |
| name: 'Permissions API Integrity', | |
| fnName: 'DETECT_NAVIGATORPERMISSIONS_PROPERTIES_ODD', | |
| desc: 'Verifies permissions.query returns native code, checks for proxy/tamper indicators', | |
| run: function() { | |
| try { | |
| var c = window.navigator.permissions; | |
| var data = {}; | |
| if (!c) return { result: "a13:0", data: { permissions: 'undefined' }, logic: 'Permissions API not available → PASS' }; | |
| data['query.toString()'] = c.query.toString().replace(/\s+/g, ""); | |
| if (c.query.toString().replace(/\s+/g, "") !== "function query() { [native code] }".replace(/\s+/g, "")) { | |
| data.issue = 'query.toString() not native'; | |
| return { result: "a13:1", data, logic: `query.toString() !== native code pattern → FAIL` }; | |
| } | |
| data['query.toString.toString()'] = c.query.toString.toString().replace(/\s+/g, ""); | |
| if (c.query.toString.toString().replace(/\s+/g, "") !== "function toString() { [native code] }".replace(/\s+/g, "")) { | |
| data.issue = 'toString.toString() not native'; | |
| return { result: "a13:1", data, logic: `toString.toString() !== native code pattern → FAIL` }; | |
| } | |
| if (c.query.toString.hasOwnProperty("[[Handler]]") && c.query.toString.hasOwnProperty("[[Target]]") && c.query.toString.hasOwnProperty("[[IsRevoked]]")) { | |
| data.issue = 'Proxy properties detected'; | |
| return { result: "a13:1", data, logic: `Proxy properties [[Handler]]/[[Target]]/[[IsRevoked]] found → FAIL` }; | |
| } | |
| if (c.hasOwnProperty("query")) { | |
| data.issue = 'query is own property'; | |
| return { result: "a13:1", data, logic: `permissions.hasOwnProperty("query") is true → FAIL` }; | |
| } | |
| data.status = 'All integrity checks passed'; | |
| return { result: "a13:0", data, logic: 'All permissions integrity checks passed → PASS' }; | |
| } catch (d) { return { result: "a13:e", data: {}, logic: 'Exception' }; } | |
| } | |
| }, | |
| { | |
| id: 'a14', num: 14, | |
| name: 'DevTools / Console Detection', | |
| fnName: 'CHECK_SPOOKYOSCHECK_likely_Devtools', | |
| desc: 'Overrides regex toString, console.debug, checks call count (Chrome DevTools)', | |
| run: function() { | |
| try { | |
| var os = CHECK_DEVICE_IN_USERAGENT_STRING(); | |
| var browser = DETECT_Browser_Type(); | |
| var data = { os: os, browser: browser }; | |
| if (browser === "Chrome" && os !== "iOS") { | |
| var c = 0; | |
| var d = /./; | |
| d.toString = function() { c++; return "spooky"; }; | |
| console.debug(d); | |
| data.toStringCallCount = c; | |
| if (c > 1) return { result: "a14:1", data, logic: `toString called ${c} times (>1) → FAIL (DevTools likely open)` }; | |
| return { result: "a14:0", data, logic: `toString called ${c} time → PASS` }; | |
| } | |
| return { result: "a14:0", data, logic: `Browser=${browser}, OS=${os} → skipped (Chrome-only check) → PASS` }; | |
| } catch (f) { return { result: "a14:e", data: {}, logic: 'Exception' }; } | |
| } | |
| }, | |
| { | |
| id: 'a15', num: 15, | |
| name: 'PhantomJS Detection', | |
| fnName: 'DETECT_Phantom_in_Window', | |
| desc: 'Checks for callPhantom, _phantom, phantom in window object', | |
| run: function() { | |
| try { | |
| var results = { callPhantom: "callPhantom" in window, _phantom: "_phantom" in window, phantom: "phantom" in window }; | |
| var data = results; | |
| var result = results.callPhantom || results._phantom || results.phantom; | |
| if (result) return { result: "a15:1", data, logic: 'PhantomJS properties found in window → FAIL' }; | |
| return { result: "a15:0", data, logic: 'No PhantomJS properties in window → PASS' }; | |
| } catch (d) { return { result: "a15:e", data: {}, logic: 'Exception' }; } | |
| } | |
| }, | |
| { | |
| id: 'a16', num: 16, | |
| name: 'Browser Automation DOM Check', | |
| fnName: 'DETECT_BROWSERAUTOMATION_via_domElements', | |
| desc: 'Checks window/document for Selenium, WebDriver, Nightmare, PhantomJS, Sequentum properties', | |
| run: function() { | |
| try { | |
| var docProps = ["__webdriver_evaluate","__selenium_evaluate","__webdriver_script_function","__webdriver_script_func","__webdriver_script_fn","__fxdriver_evaluate","__driver_unwrapped","__webdriver_unwrapped","__driver_evaluate","__selenium_unwrapped","__fxdriver_unwrapped"]; | |
| var winProps = ["webdriver","_phantom","__nightmare","_selenium","callPhantom","callSelenium","_Selenium_IDE_Recorder","__stopAllTimers"]; | |
| var found = []; | |
| for (var f in winProps) { if (window[winProps[f]]) found.push("window." + winProps[f]); } | |
| for (var h in docProps) { if (window.document[docProps[h]]) found.push("document." + docProps[h]); } | |
| try { | |
| if (window.external && window.external.toString && window.external.toString().indexOf("Sequentum") !== -1) found.push("Sequentum"); | |
| } catch(e){} | |
| try { | |
| if (window.document.documentElement.getAttribute("selenium")) found.push("attr:selenium"); | |
| if (window.document.documentElement.getAttribute("webdriver")) found.push("attr:webdriver"); | |
| if (window.document.documentElement.getAttribute("driver")) found.push("attr:driver"); | |
| } catch(e){} | |
| var data = { found: found.length ? found.join(', ') : 'none', checked_win: winProps.length, checked_doc: docProps.length }; | |
| if (found.length > 0) return { result: "a16:1", data, logic: `Automation indicators found: ${found.join(', ')} → FAIL` }; | |
| return { result: "a16:0", data, logic: `No automation indicators in ${winProps.length + docProps.length} properties → PASS` }; | |
| } catch (k) { return { result: "a16:e", data: {}, logic: 'Exception' }; } | |
| } | |
| }, | |
| { | |
| id: 'a17', num: 17, | |
| name: 'Phantomas Detection', | |
| fnName: 'DETECT_PHANTOMAS', | |
| desc: 'Checks for __phantomas property in window', | |
| run: function() { | |
| try { | |
| var result = "__phantomas" in window; | |
| var data = { '__phantomas_in_window': result }; | |
| if (result) return { result: "a17:1", data, logic: '__phantomas found in window → FAIL' }; | |
| return { result: "a17:0", data, logic: '__phantomas not in window → PASS' }; | |
| } catch (d) { return { result: "a17:e", data: {}, logic: 'Exception' }; } | |
| } | |
| }, | |
| { | |
| id: 'a18', num: 18, | |
| name: 'Selenium DOM Cache Detection', | |
| fnName: 'DETECT_Selenium_DOM_based', | |
| desc: 'Searches document properties for $[a-z]dc_ pattern with .cache_ (Selenium)', | |
| run: function() { | |
| try { | |
| var found = []; | |
| for (var c in window.document) { | |
| if (c.match(/\$[a-z]dc_/) && window.document[c].cache_) { found.push(c); } | |
| } | |
| var data = { found: found.length ? found.join(', ') : 'none' }; | |
| if (found.length > 0) return { result: "a18:1", data, logic: `Selenium cache properties found: ${found.join(', ')} → FAIL` }; | |
| return { result: "a18:0", data, logic: 'No Selenium DOM cache properties found → PASS' }; | |
| } catch (d) { return { result: "a18:e", data: {}, logic: 'Exception' }; } | |
| } | |
| }, | |
| { | |
| id: 'a19', num: 19, | |
| name: 'Node.js Buffer Detection', | |
| fnName: 'DETECT_NodeJS_Buffer', | |
| desc: 'Checks if window.Buffer is defined (indicates Node.js environment)', | |
| run: function() { | |
| try { | |
| var data = { 'window.Buffer': typeof window.Buffer, defined: window.Buffer !== undefined }; | |
| if (window.Buffer !== undefined) return { result: "a19:1", data, logic: 'window.Buffer is defined → FAIL (Node.js detected)' }; | |
| return { result: "a19:0", data, logic: 'window.Buffer is undefined → PASS' }; | |
| } catch (c) { return { result: "a19:e", data: {}, logic: 'Exception' }; } | |
| } | |
| }, | |
| { | |
| id: 'a20', num: 20, | |
| name: 'Chromium Automation Driver', | |
| fnName: 'DETECT_Chromium_based_automation_driver', | |
| desc: 'Checks for domAutomation / domAutomationController in window', | |
| run: function() { | |
| try { | |
| var data = { 'window.domAutomation': !!window.domAutomation, 'window.domAutomationController': !!window.domAutomationController }; | |
| if (window.domAutomation || window.domAutomationController) return { result: "a20:1", data, logic: 'domAutomation(Controller) found → FAIL' }; | |
| return { result: "a20:0", data, logic: 'No Chromium automation driver properties → PASS' }; | |
| } catch (c) { return { result: "a20:e", data: {}, logic: 'Exception' }; } | |
| } | |
| }, | |
| { | |
| id: 'a21', num: 21, | |
| name: 'setTimeout Integrity', | |
| fnName: 'CHECK_setTimeout_Integrity', | |
| desc: 'setTimeout.toString() should return native code signature', | |
| run: function() { | |
| try { | |
| var s = setTimeout.toString().replace(/\s+/g, ""); | |
| var expected = "function setTimeout() { [native code] }".replace(/\s/g, ""); | |
| var data = { 'setTimeout.toString()': setTimeout.toString(), normalized: s, expected: expected, match: s === expected }; | |
| if (s !== expected) return { result: "a21:1", data, logic: `setTimeout toString mismatch → FAIL (possibly tampered)` }; | |
| return { result: "a21:0", data, logic: 'setTimeout returns native code → PASS' }; | |
| } catch (c) { return { result: "a21:e", data: {}, logic: 'Exception' }; } | |
| } | |
| }, | |
| { | |
| id: 'a22', num: 22, | |
| name: 'setInterval Integrity', | |
| fnName: 'CHECK_setInterval_Integrity', | |
| desc: 'setInterval.toString() should return native code signature', | |
| run: function() { | |
| try { | |
| var s = setInterval.toString().replace(/\s+/g, ""); | |
| var expected = "function setInterval() { [native code] }".replace(/\s/g, ""); | |
| var data = { 'setInterval.toString()': setInterval.toString(), normalized: s, expected: expected, match: s === expected }; | |
| if (s !== expected) return { result: "a22:1", data, logic: `setInterval toString mismatch → FAIL (possibly tampered)` }; | |
| return { result: "a22:0", data, logic: 'setInterval returns native code → PASS' }; | |
| } catch (c) { return { result: "a22:e", data: {}, logic: 'Exception' }; } | |
| } | |
| }, | |
| { | |
| id: 'a42', num: 23, | |
| name: 'XMLHttpRequest.open Integrity', | |
| fnName: 'CHECK_XMLHTTPRequest_1', | |
| desc: 'XMLHttpRequest.prototype.open should not contain "klIsCORSRequest"', | |
| run: function() { | |
| try { | |
| var h = "kl" + "IsCO" + "RSRequest"; | |
| var s = window.XMLHttpRequest.prototype.open.toString(); | |
| var data = { 'open.toString()': s, searchFor: h, found: s.indexOf(h) !== -1 }; | |
| if (s.indexOf(h) !== -1) return { result: "a42:1", data, logic: `"klIsCORSRequest" found in open() → FAIL` }; | |
| return { result: "a42:0", data, logic: '"klIsCORSRequest" not in open() → PASS' }; | |
| } catch (i) { return { result: "a42:e", data: {}, logic: 'Exception' }; } | |
| } | |
| }, | |
| { | |
| id: 'a43', num: 24, | |
| name: 'XMLHttpRequest.send Integrity', | |
| fnName: 'CHECK_XMLHTTPRequest_2', | |
| desc: 'XMLHttpRequest.prototype.send should not contain "klIsCORSRequest"', | |
| run: function() { | |
| try { | |
| var g = "klI" + "sCOR" + "SRequest"; | |
| var s = window.XMLHttpRequest.prototype.send.toString(); | |
| var data = { 'send.toString()': s, searchFor: g, found: s.indexOf(g) !== -1 }; | |
| if (s.indexOf(g) !== -1) return { result: "a43:1", data, logic: `"klIsCORSRequest" found in send() → FAIL` }; | |
| return { result: "a43:0", data, logic: '"klIsCORSRequest" not in send() → PASS' }; | |
| } catch (h) { return { result: "a43:e", data: {}, logic: 'Exception' }; } | |
| } | |
| }, | |
| { | |
| id: 'a60', num: 25, | |
| name: 'UserAgent Automation Strings', | |
| fnName: 'CHECK_UserAgent_not_automation', | |
| desc: 'UA should not contain phantomjs, headless, avira, googleweblight', | |
| run: function() { | |
| try { | |
| var h = navigator.userAgent.toLowerCase(); | |
| var checks = { phantomjs: h.indexOf("phantomjs") !== -1, headless: h.indexOf("headless") !== -1, avira: h.indexOf("avira") !== -1, googleweblight: h.indexOf("googleweblight") !== -1 }; | |
| var data = Object.assign({ userAgent: navigator.userAgent }, checks); | |
| if (checks.phantomjs || checks.headless || checks.avira || checks.googleweblight) { | |
| var found = Object.keys(checks).filter(k => checks[k]); | |
| return { result: "a60:1", data, logic: `Found: ${found.join(', ')} in UserAgent → FAIL` }; | |
| } | |
| return { result: "a60:0", data, logic: 'No automation strings in UserAgent → PASS' }; | |
| } catch (i) { return { result: "a60:e", data: {}, logic: 'Exception' }; } | |
| } | |
| }, | |
| { | |
| id: 'a78', num: 26, | |
| name: 'Stack Trace Behavior (Puppeteer)', | |
| fnName: 'CHECK__STACKTRACE_Behavior', | |
| desc: 'Intentionally triggers error, checks stack trace for puppeteer-stealth pattern', | |
| run: function() { | |
| var c, d; | |
| try { | |
| document.createElement(0); | |
| } catch (e) { | |
| try { | |
| d = e.stack ? e.stack.split("\n") : []; | |
| var data = { stackLines: d.length, stack_preview: d.slice(0, 4).join(' | ') }; | |
| c = d.length >= 2 ? !!d[1].match(/Ob[cej]{3}t\.a[lp]{3}y[\(< ]{3}an[oynm]{5}us>/) : true; | |
| data.patternMatch = c; | |
| if (c) return { result: "a78:1", data, logic: `Puppeteer-stealth stack trace pattern detected → FAIL` }; | |
| return { result: "a78:0", data, logic: 'Normal stack trace → PASS' }; | |
| } catch (f) { | |
| return { result: "a78:0", data: { error: 'stack parse failed' }, logic: 'Stack parse failed → PASS' }; | |
| } | |
| } | |
| return { result: "a78:0", data: { note: 'createElement(0) did not throw' }, logic: 'No error thrown → PASS' }; | |
| } | |
| }, | |
| { | |
| id: 'a86', num: 27, | |
| name: 'iOS Logical Processors Check', | |
| fnName: 'CHECK_NumberofLogicalProcessors_IOS', | |
| desc: 'iOS devices with hardwareConcurrency > 4 flagged (Safari dropped support)', | |
| run: function() { | |
| try { | |
| var os = CHECK_DEVICE_IN_USERAGENT_STRING(); | |
| var hc = window.navigator.hardwareConcurrency; | |
| var data = { os: os, 'hardwareConcurrency': hc, 'typeof': typeof hc }; | |
| if (os === "iOS" && hc !== undefined && hc > 4) return { result: "a86:1", data, logic: `iOS + hardwareConcurrency=${hc} (>4) → FAIL` }; | |
| return { result: "a86:0", data, logic: os !== "iOS" ? `OS=${os} (not iOS) → PASS` : hc === undefined ? 'hardwareConcurrency undefined → PASS' : `hardwareConcurrency=${hc} (<=4) → PASS` }; | |
| } catch (c) { return { result: "a86:e", data: {}, logic: 'Exception' }; } | |
| } | |
| }, | |
| { | |
| id: 'a89', num: 28, | |
| name: 'Browser Voice List Check', | |
| fnName: 'CHECK_Browser_VoiceList', | |
| desc: '"Lekha" voice (Apple-only) should not exist on Windows/Android/Linux', | |
| run: function() { | |
| var c = CHECK_DEVICE_IN_USERAGENT_STRING(); | |
| var data = { os: c, voiceslist_length: voiceslist.length, has_lekha: voiceslist.toLowerCase().indexOf("lekha") !== -1, sample: voiceslist.substring(0, 200) }; | |
| if (voiceslist.toLowerCase().indexOf("lekha") !== -1 && (c.indexOf("Win") !== -1 || c === "Kaios" || c === "Android" || c === "Linux")) { | |
| return { result: "a89:1", data, logic: `"Lekha" voice found on ${c} (Apple-only voice) → FAIL` }; | |
| } | |
| return { result: "a89:0", data, logic: voiceslist.toLowerCase().indexOf("lekha") === -1 ? '"Lekha" voice not found → PASS' : `"Lekha" found but OS=${c} (Apple) → PASS` }; | |
| } | |
| }, | |
| { | |
| id: 'a92', num: 29, | |
| name: 'VirtualBox Detection', | |
| fnName: 'CHECK_VirtualBox', | |
| desc: 'WebGL renderer should not contain "VirtualBox"', | |
| run: function() { | |
| try { | |
| var c = HELPER_WEBGL_DEBUG_TOKENS_FOR_RENDERER_AND_VENDOR(); | |
| if (!c) return { result: "a92:0", data: { webgl: 'unavailable' }, logic: 'WebGL unavailable → PASS (skipped)' }; | |
| var data = { vendor: c[0], renderer: c[1] }; | |
| if (c[1].indexOf("VirtualBox") !== -1) return { result: "a92:1", data, logic: `"VirtualBox" found in renderer → FAIL (VM detected)` }; | |
| return { result: "a92:0", data, logic: `Renderer="${c[1]}" — no VirtualBox → PASS` }; | |
| } catch (d) { return { result: "a92:e", data: {}, logic: 'Exception' }; } | |
| } | |
| } | |
| ]; | |
| // ═══════════════════════════════════════════════════════ | |
| // STATE & UI | |
| // ═══════════════════════════════════════════════════════ | |
| let checkStates = {}; // { id: { enabled: true, result: null, data: {}, logic: '' } } | |
| let hasRun = false; | |
| function initChecks() { | |
| CHECKS.forEach(ch => { | |
| checkStates[ch.id] = { enabled: true, result: null, data: {}, logic: '' }; | |
| }); | |
| } | |
| function renderGrid() { | |
| const grid = document.getElementById('checksGrid'); | |
| grid.innerHTML = ''; | |
| CHECKS.forEach((ch, i) => { | |
| const st = checkStates[ch.id]; | |
| let statusClass = 'pending'; | |
| let statusIcon = '?'; | |
| if (st.result) { | |
| const code = st.result.split(':')[1]; | |
| if (code === '0') { statusClass = 'pass'; statusIcon = '\u2713'; } | |
| else if (code === '1') { statusClass = 'fail'; statusIcon = '\u2717'; } | |
| else { statusClass = 'error'; statusIcon = '!'; } | |
| } | |
| if (!st.enabled) statusClass += ' disabled'; | |
| const card = document.createElement('div'); | |
| card.className = `check-card ${statusClass}`; | |
| card.id = `card-${ch.id}`; | |
| card.style.animationDelay = `${i * 30}ms`; | |
| let detailRows = ''; | |
| if (st.data && Object.keys(st.data).length > 0) { | |
| Object.entries(st.data).forEach(([k, v]) => { | |
| detailRows += `<div class="detail-row"><span class="dk">${escHtml(k)}</span><span class="dv">${escHtml(String(v))}</span></div>`; | |
| }); | |
| } | |
| card.innerHTML = ` | |
| <div class="check-top" onclick="toggleExpand('${ch.id}')"> | |
| <div class="check-status">${statusIcon}</div> | |
| <div class="check-info"> | |
| <div class="check-name">#${ch.num} ${escHtml(ch.name)}</div> | |
| <div class="check-desc">${escHtml(ch.desc)}</div> | |
| </div> | |
| <div class="check-right"> | |
| <span class="check-code">${st.result || ch.id + ':?'}</span> | |
| <label class="toggle" onclick="event.stopPropagation()"> | |
| <input type="checkbox" ${st.enabled ? 'checked' : ''} onchange="toggleCheck('${ch.id}', this.checked)"> | |
| <span class="slider"></span> | |
| </label> | |
| <span class="check-expand">▼</span> | |
| </div> | |
| </div> | |
| <div class="check-details"> | |
| <div style="font-size:.63rem;color:var(--text-muted);margin-bottom:8px;font-style:italic">${escHtml(ch.fnName)}</div> | |
| ${detailRows} | |
| ${st.logic ? `<div class="detail-logic">${formatLogic(st.logic)}</div>` : ''} | |
| </div> | |
| `; | |
| grid.appendChild(card); | |
| }); | |
| } | |
| function formatLogic(logic) { | |
| return escHtml(logic) | |
| .replace(/PASS/g, '<span class="val">PASS</span>') | |
| .replace(/FAIL/g, '<span class="bad">FAIL</span>') | |
| .replace(/(→)/g, '<span class="kw">$1</span>'); | |
| } | |
| function escHtml(s) { | |
| const d = document.createElement('div'); | |
| d.textContent = s; | |
| return d.innerHTML; | |
| } | |
| function toggleExpand(id) { | |
| document.getElementById(`card-${id}`).classList.toggle('open'); | |
| } | |
| function toggleCheck(id, enabled) { | |
| checkStates[id].enabled = enabled; | |
| if (hasRun) { | |
| if (enabled) { | |
| const ch = CHECKS.find(c => c.id === id); | |
| const res = ch.run(); | |
| checkStates[id].result = res.result; | |
| checkStates[id].data = res.data; | |
| checkStates[id].logic = res.logic; | |
| } | |
| updateVerdict(); | |
| } | |
| renderGrid(); | |
| } | |
| function enableAll() { | |
| CHECKS.forEach(ch => { checkStates[ch.id].enabled = true; }); | |
| if (hasRun) runAllChecks(); | |
| else renderGrid(); | |
| } | |
| function disableAll() { | |
| CHECKS.forEach(ch => { checkStates[ch.id].enabled = false; }); | |
| updateVerdict(); | |
| renderGrid(); | |
| } | |
| function expandAll() { | |
| document.querySelectorAll('.check-card').forEach(c => c.classList.add('open')); | |
| } | |
| function collapseAll() { | |
| document.querySelectorAll('.check-card').forEach(c => c.classList.remove('open')); | |
| } | |
| function runAllChecks() { | |
| hasRun = true; | |
| document.getElementById('verdictStrip').classList.add('scanning'); | |
| // Update header info | |
| document.getElementById('detectedOS').textContent = CHECK_DEVICE_IN_USERAGENT_STRING(); | |
| document.getElementById('detectedBrowser').textContent = DETECT_Browser_Type(); | |
| var wgl = HELPER_WEBGL_DEBUG_TOKENS_FOR_RENDERER_AND_VENDOR(); | |
| if (wgl) { | |
| document.getElementById('webglInfo').textContent = wgl[0] + ' / ' + wgl[1]; | |
| } else { | |
| document.getElementById('webglInfo').textContent = 'Unavailable'; | |
| } | |
| CHECKS.forEach(ch => { | |
| if (checkStates[ch.id].enabled) { | |
| try { | |
| var res = ch.run(); | |
| checkStates[ch.id].result = res.result; | |
| checkStates[ch.id].data = res.data; | |
| checkStates[ch.id].logic = res.logic; | |
| } catch (e) { | |
| checkStates[ch.id].result = ch.id + ':e'; | |
| checkStates[ch.id].data = { error: e.message }; | |
| checkStates[ch.id].logic = 'Unhandled exception: ' + e.message; | |
| } | |
| } | |
| }); | |
| updateVerdict(); | |
| renderGrid(); | |
| renderRawData(); | |
| setTimeout(() => { | |
| document.getElementById('verdictStrip').classList.remove('scanning'); | |
| }, 1200); | |
| } | |
| function updateVerdict() { | |
| let pass = 0, fail = 0, err = 0, off = 0; | |
| let overallFail = false; | |
| let overallErr = false; | |
| CHECKS.forEach(ch => { | |
| const st = checkStates[ch.id]; | |
| if (!st.enabled) { off++; return; } | |
| if (!st.result) return; | |
| const code = st.result.split(':')[1]; | |
| if (code === '0') pass++; | |
| else if (code === '1') { fail++; overallFail = true; } | |
| else { err++; overallErr = true; } | |
| }); | |
| document.getElementById('statPass').textContent = pass; | |
| document.getElementById('statFail').textContent = fail; | |
| document.getElementById('statErr').textContent = err; | |
| document.getElementById('statOff').textContent = off; | |
| const vEl = document.getElementById('verdictValue'); | |
| const vDet = document.getElementById('verdictDetail'); | |
| if (!hasRun) { | |
| vEl.textContent = 'WAITING'; | |
| vEl.className = 'value pending'; | |
| vDet.textContent = 'Click "Run All Checks" to begin'; | |
| return; | |
| } | |
| if (overallFail) { | |
| vEl.textContent = 'a0:1 DETECTED'; | |
| vEl.className = 'value fail'; | |
| vDet.textContent = `${fail} check(s) flagged your browser as suspicious`; | |
| } else if (overallErr) { | |
| vEl.textContent = 'a0:e ERROR'; | |
| vEl.className = 'value fail'; | |
| vDet.textContent = `${err} check(s) threw errors`; | |
| } else { | |
| vEl.textContent = 'a0:0 CLEAN'; | |
| vEl.className = 'value pass'; | |
| vDet.textContent = `All ${pass} enabled checks passed — browser appears legitimate`; | |
| } | |
| } | |
| function resetAll() { | |
| hasRun = false; | |
| initChecks(); | |
| renderGrid(); | |
| document.getElementById('verdictValue').textContent = 'WAITING'; | |
| document.getElementById('verdictValue').className = 'value pending'; | |
| document.getElementById('verdictDetail').textContent = 'Click "Run All Checks" to begin'; | |
| document.getElementById('detectedOS').textContent = '--'; | |
| document.getElementById('detectedBrowser').textContent = '--'; | |
| document.getElementById('webglInfo').textContent = '--'; | |
| document.getElementById('statPass').textContent = '0'; | |
| document.getElementById('statFail').textContent = '0'; | |
| document.getElementById('statErr').textContent = '0'; | |
| document.getElementById('statOff').textContent = '0'; | |
| document.getElementById('rawGrid').innerHTML = ''; | |
| } | |
| function renderRawData() { | |
| const grid = document.getElementById('rawGrid'); | |
| const rawItems = [ | |
| { k: 'UserAgent', v: navigator.userAgent }, | |
| { k: 'Platform', v: navigator.platform }, | |
| { k: 'Language', v: navigator.language }, | |
| { k: 'Languages', v: JSON.stringify(navigator.languages) }, | |
| { k: 'oscpu', v: String(navigator.oscpu) }, | |
| { k: 'Vendor', v: navigator.vendor }, | |
| { k: 'ProductSub', v: String(navigator.productSub) }, | |
| { k: 'CookieEnabled', v: String(navigator.cookieEnabled) }, | |
| { k: 'DoNotTrack', v: String(navigator.doNotTrack) }, | |
| { k: 'HardwareConcurrency', v: String(navigator.hardwareConcurrency) }, | |
| { k: 'DeviceMemory', v: String(navigator.deviceMemory) }, | |
| { k: 'MaxTouchPoints', v: String(navigator.maxTouchPoints) }, | |
| { k: 'Webdriver', v: String(navigator.webdriver) }, | |
| { k: 'Plugins Count', v: navigator.plugins ? String(navigator.plugins.length) : 'undefined' }, | |
| { k: 'screen.width', v: String(screen.width) }, | |
| { k: 'screen.height', v: String(screen.height) }, | |
| { k: 'screen.availWidth', v: String(screen.availWidth) }, | |
| { k: 'screen.availHeight', v: String(screen.availHeight) }, | |
| { k: 'screen.colorDepth', v: String(screen.colorDepth) }, | |
| { k: 'screen.pixelDepth', v: String(screen.pixelDepth) }, | |
| { k: 'devicePixelRatio', v: String(window.devicePixelRatio) }, | |
| { k: 'Timezone', v: Intl.DateTimeFormat().resolvedOptions().timeZone }, | |
| { k: 'TimezoneOffset', v: String(new Date().getTimezoneOffset()) }, | |
| { k: 'eval.toString().length', v: String(eval.toString().length) }, | |
| { k: 'Notification.permission', v: typeof Notification !== 'undefined' ? Notification.permission : 'N/A' }, | |
| ]; | |
| var wgl = HELPER_WEBGL_DEBUG_TOKENS_FOR_RENDERER_AND_VENDOR(); | |
| if (wgl) { | |
| rawItems.push({ k: 'WebGL Vendor', v: wgl[0] }); | |
| rawItems.push({ k: 'WebGL Renderer', v: wgl[1] }); | |
| } | |
| rawItems.push({ k: 'VoiceList (sample)', v: voiceslist.substring(0, 300) || '(loading...)' }); | |
| rawItems.push({ k: 'setTimeout.toString()', v: setTimeout.toString() }); | |
| rawItems.push({ k: 'setInterval.toString()', v: setInterval.toString() }); | |
| grid.innerHTML = rawItems.map(item => | |
| `<div class="raw-item"><div class="rk">${escHtml(item.k)}</div><div class="rv">${escHtml(item.v)}</div></div>` | |
| ).join(''); | |
| } | |
| // ═══════════════════════════════════════════════════════ | |
| // CODE GENERATOR — source templates for each check | |
| // ═══════════════════════════════════════════════════════ | |
| const CHECK_SOURCES = { | |
| a1: `function CHECK_COMPARE_ISO_CODE_LANGUAGE_ARRAY_VS_ISO_CODE_PRIMARY_LANGUAGE() { | |
| if (typeof navigator.languages !== "undefined") { | |
| try { | |
| var c = navigator.languages[0].substr(0, 2); | |
| if (c !== navigator.language.substr(0, 2)) { | |
| return "a1:" + 1; | |
| } | |
| } catch (d) { | |
| return "a1:e"; | |
| } | |
| } | |
| return "a1:" + 0; | |
| }`, | |
| a2: `function CHECK_COMPARE_AVAILABLE_WINDOW_SIZE_TO_CURRENT_WINDOW_SIZE() { | |
| try { | |
| if (window.screen.width < window.screen.availWidth || window.screen.height < window.screen.availHeight) { | |
| if (window.screen.width === window.screen.availHeight && window.screen.height === window.screen.availWidth || window.screen.width === window.screen.availHeight + 20 && window.screen.height === window.screen.availWidth) { | |
| return "a2:" + 0; | |
| } else { | |
| return "a2:" + 1; | |
| } | |
| } else { | |
| return "a2:" + 0; | |
| } | |
| } catch (c) { | |
| return "a2:e"; | |
| } | |
| }`, | |
| a3: `function CHECK_COMPARE_OSCPU_TO_DEVICE_USERAGENT_STRING() { | |
| try { | |
| var d = navigator.oscpu; | |
| var f = CHECK_DEVICE_IN_USERAGENT_STRING(); | |
| if (typeof d !== "undefined") { | |
| d = d.toLowerCase(); | |
| if (d === "" && f === "Kaios") { return "a3:" + 0; } | |
| else if (d.indexOf("win") >= 0 && f !== "Windows" && f !== "Windows Phone") { return "a3:" + 1; } | |
| else if (d.indexOf("linux") >= 0 && f !== "Linux" && f !== "Android") { return "a3:" + 1; } | |
| else if (d.indexOf("mac") >= 0 && f !== "Mac" && f !== "iOS") { return "a3:" + 1; } | |
| else if ((d.indexOf("win") === -1 && d.indexOf("linux") === -1 && d.indexOf("mac") === -1) !== (f === "Other")) { return "a3:" + 1; } | |
| else { return "a3:" + 0; } | |
| } else { | |
| return "a3:" + 0; | |
| } | |
| } catch (g) { | |
| return "a3:e"; | |
| } | |
| }`, | |
| a4: `function CHECK_OSCPU_NOT_UNDEFINED_AND_BROWSER_NOT_FIREFOX() { | |
| try { | |
| var c = navigator.oscpu; | |
| var d = DETECT_Browser_Type(); | |
| if (typeof c !== "undefined" && d !== "Firefox") { | |
| return "a4:" + 1; | |
| } else { | |
| return "a4:" + 0; | |
| } | |
| } catch (f) { | |
| return "a4:e"; | |
| } | |
| }`, | |
| a5: `function CHECK_COMPARE_PLATFORM_USERAGENT_DEVICEINUSERAGENT() { | |
| try { | |
| var c = navigator.platform.toLowerCase(); | |
| var d = navigator.userAgent.toLowerCase(); | |
| var f = CHECK_DEVICE_IN_USERAGENT_STRING(); | |
| if (c === "" && f === "Kaios") { return "a5:" + 0; } | |
| else if (d.indexOf("maui") >= 0 && c.indexOf("pike") >= 0) { return "a5:" + 0; } | |
| else if (d.indexOf("j2me/midp") >= 0 && c.indexOf("pike") >= 0) { return "a5:" + 0; } | |
| else if (c === "arm" && f === "Windows Phone") { return "a5:" + 0; } | |
| else if (c.indexOf("win") >= 0 && f !== "Windows" && f !== "Windows Phone") { return "a5:" + 1; } | |
| else if ((c.indexOf("linux") >= 0 || c.indexOf("android") >= 0 || c.indexOf("pike") >= 0) && f !== "Linux" && f !== "Android" && f !== "Kaios") { return "a5:" + 1; } | |
| else if ((c.indexOf("mac") >= 0 || c.indexOf("ipad") >= 0 || c.indexOf("ipod") >= 0 || c.indexOf("iphone") >= 0) && f !== "Mac" && f !== "iOS") { return "a5:" + 1; } | |
| else if (c === "macintel" && d.indexOf("iphone") >= 0) { return "a5:" + 1; } | |
| else { | |
| var g = c.indexOf("win") < 0 && c.indexOf("linux") < 0 && c.indexOf("mac") < 0 && c.indexOf("iphone") < 0 && c.indexOf("pike") < 0 && c.indexOf("ipod") < 0 && c.indexOf("ipad") < 0; | |
| if (g !== (f === "Other")) { return "a5:" + 1; } | |
| } | |
| return "a5:" + 0; | |
| } catch (h) { | |
| return "a5:e"; | |
| } | |
| }`, | |
| a6: `function CHECK_PLUGINSUNDEFINED_BUT_USERAGENTOS_NOT_WINDOWS() { | |
| try { | |
| var c = CHECK_DEVICE_IN_USERAGENT_STRING(); | |
| if (typeof navigator.plugins === "undefined" && c !== "Windows" && c !== "Windows Phone") { | |
| return "a6:" + 1; | |
| } else { | |
| return "a6:" + 0; | |
| } | |
| } catch (d) { | |
| return "a6:e"; | |
| } | |
| }`, | |
| a7: `function CHECK_BROWSER_BUILDNUMBER_AGAINST_BROWSERTYPE() { | |
| try { | |
| var c = navigator.productSub; | |
| var d = DETECT_Browser_Type(); | |
| if ((d === "Chrome" || d === "Safari") && c !== "20030107") { return "a7:" + 1; } | |
| else if (d === "Opera" && c !== "20030107" && typeof c !== "undefined") { return "a7:" + 1; } | |
| else { return "a7:" + 0; } | |
| } catch (f) { | |
| return "a7:e"; | |
| } | |
| }`, | |
| a8: `function CHECK_LENGTH_OF_EVAL_FUNCTION_AGAINST_BROWSER_TYPE() { | |
| try { | |
| var c = DETECT_Browser_Type(); | |
| var d = CHECK_DEVICE_IN_USERAGENT_STRING(); | |
| var f = eval.toString().length; | |
| if (f === 37 && c !== "Safari" && c !== "Firefox" && c !== "Other" && d === "iOS" && c !== "Chrome") { return "a8:" + 1; } | |
| else if (f === 39 && c !== "Internet Explorer" && c !== "Other") { return "a8:" + 1; } | |
| else if (f === 33 && c !== "Chrome" && c !== "Opera" && c !== "Edge" && c !== "UC Browser" && c !== "Samsung Browser" && c !== "Other" && c !== "Android Browser") { return "a8:" + 1; } | |
| else { return "a8:" + 0; } | |
| } catch (g) { | |
| return "a8:e"; | |
| } | |
| }`, | |
| a9: `function CHECK_Mozilla_UNIQUE_TOSOURCE_FKT_AGAINST_BROWSER_TYPE() { | |
| try { | |
| var c = DETECT_Browser_Type(); | |
| var d; | |
| try { throw "a"; } catch (f) { | |
| try { f.toSource(); d = true; } catch (g) { d = false; } | |
| } | |
| if (d && c !== "Firefox" && c !== "Other") { return "a9:" + 1; } | |
| else { return "a9:" + 0; } | |
| } catch (h) { | |
| return "a9:e"; | |
| } | |
| }`, | |
| a10: `function CHECK_Compare_DEVICEUSERAGENTSTRING_to_BrowserType_to_WEBGL_DEBUG_TOKENS() { | |
| try { | |
| var c = CHECK_DEVICE_IN_USERAGENT_STRING(); | |
| var d = DETECT_Browser_Type(); | |
| var f = HELPER_WEBGL_DEBUG_TOKENS_FOR_RENDERER_AND_VENDOR(); | |
| if (!f) { return "a10:" + 0; } | |
| else if (c === "iOS" && f[0].indexOf("Apple") === -1 && f[0].indexOf("Imagination Technologies") === -1) { return "a10:" + 1; } | |
| else if (c === "Mac" && f[0].indexOf("Intel") === -1 && f[0].indexOf("ATI Technologies") === -1 && f[0].indexOf("NVIDIA Corporation") === -1 && f[0].indexOf("Apple") === -1) { return "a10:" + 1; } | |
| else if (c === "Android" && (f[0] === "Google Inc. (NVIDIA)" || f[0] === "Google Inc. (Intel)" || f[0] === "Google Inc. (Google)" || f[0] === "Google Inc." || f[0].indexOf("NVIDIA Corporation") !== -1)) { return "a10:" + 1; } | |
| else if (c === "Windows" && d === "Edge" && f[0].indexOf("Microsoft") === -1) { return "a10:" + 1; } | |
| else if (c === "Windows" && (d === "Chrome" || d === "Firefox") && f[0].indexOf("Google Inc") === -1) { return "a10:" + 1; } | |
| else if (f[0].indexOf("VMware") !== -1) { return "a10:" + 1; } | |
| else { return "a10:" + 0; } | |
| } catch (g) { | |
| return "a10:e"; | |
| } | |
| }`, | |
| a11: `function CHECK_Webdriver_in_Navigator_Interface() { | |
| try { | |
| var c = "webdriver" in navigator && navigator.webdriver; | |
| if (c) { return "a11:" + 1; } | |
| else { return "a11:" + 0; } | |
| } catch (d) { | |
| return "a11:e"; | |
| } | |
| }`, | |
| a12: `function CHECK_Permission_DENIED_in_State_Prompt() { | |
| try { | |
| if (permissions) { return "a12:" + 1; } | |
| else { return "a12:" + 0; } | |
| } catch (c) { | |
| return "a12:e"; | |
| } | |
| }`, | |
| a13: `function DETECT_NAVIGATORPERMISSIONS_PROPERTIES_ODD() { | |
| try { | |
| var c = window.navigator.permissions; | |
| if (c.query.toString().replace(/\\s+/g, "") !== "function query() { [native code] }".replace(/\\s+/g, "")) { return "a13:" + 1; } | |
| if (c.query.toString.toString().replace(/\\s+/g, "") !== "function toString() { [native code] }".replace(/\\s+/g, "")) { return "a13:" + 1; } | |
| if (c.query.toString.hasOwnProperty("[[Handler]]") && c.query.toString.hasOwnProperty("[[Target]]") && c.query.toString.hasOwnProperty("[[IsRevoked]]")) { return "a13:" + 1; } | |
| if (c.hasOwnProperty("query")) { return "a13:" + 1; } | |
| return "a13:" + 0; | |
| } catch (d) { | |
| return "a13:e"; | |
| } | |
| }`, | |
| a14: `function CHECK_SPOOKYOSCHECK_likely_Devtools() { | |
| try { | |
| os = CHECK_DEVICE_IN_USERAGENT_STRING(); | |
| browser = DETECT_Browser_Type(); | |
| if (browser === "Chrome" && os !== "iOS") { | |
| var c = 0; | |
| var d = /./; | |
| d.toString = function() { c++; return "spooky"; }; | |
| console.debug(d); | |
| if (c > 1) { return "a14:" + 1; } | |
| else { return "a14:" + 0; } | |
| } else { | |
| return "a14:" + 0; | |
| } | |
| } catch (f) { | |
| return "a14:e"; | |
| } | |
| }`, | |
| a15: `function DETECT_Phantom_in_Window() { | |
| try { | |
| function c() { return ["callPhantom" in window, "_phantom" in window, "phantom" in window]; } | |
| result = c().some(function(d) { return d; }); | |
| if (result) { return "a15:" + 1; } | |
| else { return "a15:" + 0; } | |
| } catch (d) { | |
| return "a15:e"; | |
| } | |
| }`, | |
| a16: `function DETECT_BROWSERAUTOMATION_via_domElements() { | |
| try { | |
| var c = ["__webdriver_evaluate","__selenium_evaluate","__webdriver_script_function","__webdriver_script_func","__webdriver_script_fn","__fxdriver_evaluate","__driver_unwrapped","__webdriver_unwrapped","__driver_evaluate","__selenium_unwrapped","__fxdriver_unwrapped"]; | |
| var d = ["webdriver","_phantom","__nightmare","_selenium","callPhantom","callSelenium","_Selenium_IDE_Recorder","__stopAllTimers"]; | |
| for (var f in d) { if (window[d[f]]) { return "a16:" + 1; } } | |
| for (var h in c) { if (window.document[c[h]]) { return "a16:" + 1; } } | |
| try { | |
| if (window.external && window.external.toString() && window.external.toString().indexOf("Sequentum") != -1) { return "a16:" + 1; } | |
| if (window.document.documentElement.getAttribute("selenium")) { return "a16:" + 1; } | |
| if (window.document.documentElement.getAttribute("webdriver")) { return "a16:" + 1; } | |
| if (window.document.documentElement.getAttribute("driver")) { return "a16:" + 1; } | |
| } catch (j) {} | |
| return "a16:" + 0; | |
| } catch (k) { | |
| return "a16:e"; | |
| } | |
| }`, | |
| a17: `function DETECT_PHANTOMAS() { | |
| try { | |
| function c() { return ["__phantomas" in window]; } | |
| result = c().some(function(d) { return d; }); | |
| if (result) { return "a17:" + 1; } | |
| else { return "a17:" + 0; } | |
| } catch (d) { | |
| return "a17:e"; | |
| } | |
| }`, | |
| a18: `function DETECT_Selenium_DOM_based() { | |
| try { | |
| for (var c in window.document) { | |
| if (c.match(/\\$[a-z]dc_/) && window.document[c].cache_) { return "a18:" + 1; } | |
| } | |
| return "a18:" + 0; | |
| } catch (d) { | |
| return "a18:e"; | |
| } | |
| }`, | |
| a19: `function DETECT_NodeJS_Buffer() { | |
| try { | |
| if (window.Buffer !== undefined) { return "a19:" + 1; } | |
| else { return "a19:" + 0; } | |
| } catch (c) { | |
| return "a19:e"; | |
| } | |
| }`, | |
| a20: `function DETECT_Chromium_based_automation_driver() { | |
| try { | |
| if (window.domAutomation || window.domAutomationController) { return "a20:" + 1; } | |
| else { return "a20:" + 0; } | |
| } catch (c) { | |
| return "a20:e"; | |
| } | |
| }`, | |
| a21: `function CHECK_setTimeout_Integrity() { | |
| try { | |
| if (setTimeout.toString().replace(/\\s+/g, "") !== "function setTimeout() { [native code] }".replace(/\\s/g, "")) { return "a21:" + 1; } | |
| else { return "a21:" + 0; } | |
| } catch (c) { | |
| return "a21:e"; | |
| } | |
| }`, | |
| a22: `function CHECK_setInterval_Integrity() { | |
| try { | |
| if (setInterval.toString().replace(/\\s+/g, "") !== "function setInterval() { [native code] }".replace(/\\s/g, "")) { return "a22:" + 1; } | |
| else { return "a22:" + 0; } | |
| } catch (c) { | |
| return "a22:e"; | |
| } | |
| }`, | |
| a42: `function CHECK_XMLHTTPRequest_1() { | |
| try { | |
| var c = "kl"; var f = "IsCO"; var g = "RSRequest"; var h = c + f + g; | |
| if (window.XMLHttpRequest.prototype.open.toString().indexOf(h) !== -1) { return "a42:" + 1; } | |
| else { return "a42:" + 0; } | |
| } catch (i) { | |
| return "a42:e"; | |
| } | |
| }`, | |
| a43: `function CHECK_XMLHTTPRequest_2() { | |
| try { | |
| var c = "klI"; var d = "sCOR"; var f = "SRequest"; var g = c + d + f; | |
| if (window.XMLHttpRequest.prototype.send.toString().indexOf(g) !== -1) { return "a43:" + 1; } | |
| else { return "a43:" + 0; } | |
| } catch (h) { | |
| return "a43:e"; | |
| } | |
| }`, | |
| a60: `function CHECK_UserAgent_not_automation() { | |
| try { | |
| var c = "phantomjs"; var d = "headless"; var f = "avira"; var g = "googleweblight"; | |
| var h = navigator.userAgent.toLowerCase(); | |
| if (h.indexOf(c) !== -1 || h.indexOf(d) !== -1 || h.indexOf(f) !== -1 || h.indexOf(g) !== -1) { return "a60:" + 1; } | |
| else { return "a60:" + 0; } | |
| } catch (i) { | |
| return "a60:e"; | |
| } | |
| }`, | |
| a78: `function CHECK_STACKTRACE_Behavior() { | |
| var c; var d; | |
| try { document.createElement(0); } catch (e) { | |
| try { | |
| d = e.stack.split("\\n"); | |
| c = d.length >= 2 ? !!d[1].match(/Ob[cej]{3}t\\.a[lp]{3}y[\\(< ]{3}an[oynm]{5}us>/) : true; | |
| } catch (f) {} | |
| if (c) { return "a78:" + 1; } | |
| else { return "a78:" + 0; } | |
| } | |
| }`, | |
| a86: `function CHECK_NumberofLogicalProcessors_IOS() { | |
| try { | |
| if (CHECK_DEVICE_IN_USERAGENT_STRING() === "iOS" && window.navigator.hardwareConcurrency !== undefined && window.navigator.hardwareConcurrency > 4) { | |
| return "a86:" + 1; | |
| } else { | |
| return "a86:" + 0; | |
| } | |
| } catch (c) { | |
| return "a86:e"; | |
| } | |
| }`, | |
| a89: `function CHECK_Browser_VoiceList() { | |
| var c = CHECK_DEVICE_IN_USERAGENT_STRING(); | |
| if (voiceslist.toLowerCase().indexOf("lekha") !== -1 && (c.indexOf("Win") !== -1 || c === "Kaios" || c === "Android" || c === "Linux")) { | |
| return "a89:" + 1; | |
| } else { | |
| return "a89:" + 0; | |
| } | |
| }`, | |
| a92: `function CHECK_VirtualBox() { | |
| try { | |
| var c = HELPER_WEBGL_DEBUG_TOKENS_FOR_RENDERER_AND_VENDOR(); | |
| if (c[1].indexOf("VirtualBox") !== -1) { return "a92:1"; } | |
| else { return "a92:0"; } | |
| } catch (d) { | |
| return "a92:e"; | |
| } | |
| }` | |
| }; | |
| const HELPER_SOURCES = { | |
| CHECK_DEVICE_IN_USERAGENT_STRING: `function CHECK_DEVICE_IN_USERAGENT_STRING() { | |
| var c = navigator.userAgent.toLowerCase(); | |
| var d; | |
| if (c.indexOf("windows phone") >= 0) { d = "Windows Phone"; } | |
| else if (c.indexOf("win") >= 0) { d = "Windows"; } | |
| else if (c.indexOf("kaios") >= 0) { d = "Kaios"; } | |
| else if (c.indexOf("android") >= 0 || c.indexOf("spreadtrum") >= 0) { d = "Android"; } | |
| else if (c.indexOf("linux") >= 0 || c.indexOf("cros") >= 0) { d = "Linux"; } | |
| else if (c.indexOf("iphone") >= 0 || c.indexOf("ipad") >= 0) { d = "iOS"; } | |
| else if (c.indexOf("mac") >= 0) { d = "Mac"; } | |
| else { d = "Other"; } | |
| return d; | |
| }`, | |
| DETECT_Browser_Type: `function DETECT_Browser_Type() { | |
| var c = navigator.userAgent; | |
| var d; | |
| if (c.indexOf("OPR/") !== -1 || c.indexOf("Opera") !== -1) { d = "Opera"; } | |
| else if ((c.indexOf("MSIE") !== -1 || c.indexOf("Trident") !== -1) && c.indexOf("MAXTHON") === -1) { d = "Internet Explorer"; } | |
| else if (c.indexOf("Edge") !== -1 || c.indexOf("EdgA") !== -1) { d = "Edge"; } | |
| else if (c.indexOf("SamsungBrowser") !== -1) { d = "Samsung Browser"; } | |
| else if (c.indexOf("UCBrowser") !== -1) { d = "UC Browser"; } | |
| else if (c.indexOf("Android") !== -1 && c.indexOf("Chrome") === -1 && c.indexOf("Firefox") === -1) { d = "Android Browser"; } | |
| else if (c.indexOf("Chrome") !== -1 || c.indexOf("CriOS") !== -1) { d = "Chrome"; } | |
| else if (c.indexOf("Safari") !== -1 && c.indexOf("Chrome") === -1) { d = "Safari"; } | |
| else if (c.indexOf("Firefox") !== -1) { d = "Firefox"; } | |
| else { d = "Other"; } | |
| return d; | |
| }`, | |
| HELPER_WEBGL_DEBUG_TOKENS_FOR_RENDERER_AND_VENDOR: `function HELPER_WEBGL_DEBUG_TOKENS_FOR_RENDERER_AND_VENDOR() { | |
| try { | |
| var c = document.createElement("canvas"); | |
| var d = c.getContext("webgl"); | |
| var e = d.getExtension("WEBGL_debug_renderer_info"); | |
| var f = d.getParameter(e.UNMASKED_VENDOR_WEBGL); | |
| var g = d.getParameter(e.UNMASKED_RENDERER_WEBGL); | |
| return [f, g]; | |
| } catch (h) { | |
| return false; | |
| } | |
| }`, | |
| getPermissionStatus: `var permissions = false; | |
| getPermissionStatus(); | |
| function getPermissionStatus() { | |
| try { | |
| browser = DETECT_Browser_Type(); | |
| if (browser !== "Samsung Browser" && browser !== "Firefox") { | |
| navigator.permissions.query({ name: "notifications" }).then(function(c) { | |
| if (Notification.permission === "denied" && c.state === "prompt") { | |
| permissions = true; | |
| } else { | |
| permissions = false; | |
| } | |
| }); | |
| return permissions; | |
| } | |
| } catch (c) {} | |
| }`, | |
| populateVoiceList: `var voiceslist = ""; | |
| function populateVoiceList() { | |
| try { | |
| var c = speechSynthesis.getVoices(); | |
| if (c.length !== 0) { | |
| var d = ""; | |
| for (var f = 0; f < c.length; f++) { d = d + " " + c[f].name; } | |
| voiceslist = d; | |
| } else { | |
| setTimeout(function() { populateVoiceList(); }, 5); | |
| } | |
| } catch (g) {} | |
| } | |
| populateVoiceList();` | |
| }; | |
| // Which helpers each check needs | |
| const CHECK_DEPS = { | |
| a1: [], a2: [], | |
| a3: ['CHECK_DEVICE_IN_USERAGENT_STRING'], | |
| a4: ['DETECT_Browser_Type'], | |
| a5: ['CHECK_DEVICE_IN_USERAGENT_STRING'], | |
| a6: ['CHECK_DEVICE_IN_USERAGENT_STRING'], | |
| a7: ['DETECT_Browser_Type'], | |
| a8: ['DETECT_Browser_Type', 'CHECK_DEVICE_IN_USERAGENT_STRING'], | |
| a9: ['DETECT_Browser_Type'], | |
| a10: ['CHECK_DEVICE_IN_USERAGENT_STRING', 'DETECT_Browser_Type', 'HELPER_WEBGL_DEBUG_TOKENS_FOR_RENDERER_AND_VENDOR'], | |
| a11: [], a12: ['getPermissionStatus', 'DETECT_Browser_Type'], | |
| a13: [], a14: ['CHECK_DEVICE_IN_USERAGENT_STRING', 'DETECT_Browser_Type'], | |
| a15: [], a16: [], a17: [], a18: [], a19: [], a20: [], a21: [], a22: [], | |
| a42: [], a43: [], a60: [], a78: [], | |
| a86: ['CHECK_DEVICE_IN_USERAGENT_STRING'], | |
| a89: ['CHECK_DEVICE_IN_USERAGENT_STRING', 'populateVoiceList'], | |
| a92: ['HELPER_WEBGL_DEBUG_TOKENS_FOR_RENDERER_AND_VENDOR'] | |
| }; | |
| // Extract the function name from a check's source | |
| function getFnNameFromSource(src) { | |
| var m = src.match(/function\s+(\w+)/); | |
| return m ? m[1] : null; | |
| } | |
| function generateCode() { | |
| const fnName = document.getElementById('cgFnName').value || 'chk'; | |
| const passVal = document.getElementById('cgPassVal').value || 'a0:0'; | |
| const failVal = document.getElementById('cgFailVal').value || 'a0:1'; | |
| const errVal = document.getElementById('cgErrVal').value || 'a0:e'; | |
| const style = document.getElementById('cgStyle').value; | |
| const includeHelpers = document.getElementById('cgHelpers').value === 'yes'; | |
| const enabledChecks = CHECKS.filter(ch => checkStates[ch.id].enabled); | |
| if (enabledChecks.length === 0) { | |
| document.getElementById('codeOutput').innerHTML = '<span class="cm-cmt">// No checks enabled. Toggle some checks on above.</span>'; | |
| return; | |
| } | |
| let parts = []; | |
| parts.push('// ══════════════════════════════════════════════════'); | |
| parts.push('// VexTrio-style Browser Fingerprint Script'); | |
| parts.push('// Generated: ' + new Date().toISOString()); | |
| parts.push('// Checks enabled: ' + enabledChecks.length + ' / ' + CHECKS.length); | |
| parts.push('// Pass="' + passVal + '" Fail="' + failVal + '" Error="' + errVal + '"'); | |
| parts.push('// ══════════════════════════════════════════════════'); | |
| parts.push(''); | |
| // Collect needed helpers | |
| if (includeHelpers) { | |
| const neededHelpers = new Set(); | |
| enabledChecks.forEach(ch => { | |
| (CHECK_DEPS[ch.id] || []).forEach(h => neededHelpers.add(h)); | |
| }); | |
| if (neededHelpers.size > 0) { | |
| parts.push('// ── Helper Functions ──'); | |
| parts.push(''); | |
| neededHelpers.forEach(h => { | |
| if (HELPER_SOURCES[h]) { | |
| parts.push(HELPER_SOURCES[h]); | |
| parts.push(''); | |
| } | |
| }); | |
| } | |
| } | |
| // Individual check functions | |
| parts.push('// ── Check Functions ──'); | |
| parts.push(''); | |
| enabledChecks.forEach(ch => { | |
| if (CHECK_SOURCES[ch.id]) { | |
| parts.push(CHECK_SOURCES[ch.id]); | |
| parts.push(''); | |
| } | |
| }); | |
| // Main chk() function | |
| parts.push('// ── Main Fingerprint Evaluator ──'); | |
| parts.push(''); | |
| if (style === 'chained') { | |
| parts.push('function ' + fnName + '() {'); | |
| parts.push(' try {'); | |
| enabledChecks.forEach((ch, i) => { | |
| const src = CHECK_SOURCES[ch.id]; | |
| const checkFnName = getFnNameFromSource(src); | |
| const prefix = i === 0 ? ' if' : ' } else if'; | |
| parts.push(prefix + ' (' + checkFnName + '().split(":")[1] === "1") {'); | |
| parts.push(' return "' + failVal + '";'); | |
| }); | |
| parts.push(' } else {'); | |
| parts.push(' return "' + passVal + '";'); | |
| parts.push(' }'); | |
| parts.push(' } catch (e) {'); | |
| parts.push(' return "' + errVal + '";'); | |
| parts.push(' }'); | |
| parts.push('}'); | |
| } else if (style === 'loop') { | |
| parts.push('function ' + fnName + '() {'); | |
| parts.push(' var checks = ['); | |
| enabledChecks.forEach((ch, i) => { | |
| const src = CHECK_SOURCES[ch.id]; | |
| const checkFnName = getFnNameFromSource(src); | |
| const comma = i < enabledChecks.length - 1 ? ',' : ''; | |
| parts.push(' ' + checkFnName + comma); | |
| }); | |
| parts.push(' ];'); | |
| parts.push(' try {'); | |
| parts.push(' for (var i = 0; i < checks.length; i++) {'); | |
| parts.push(' var result = checks[i]();'); | |
| parts.push(' if (result.split(":")[1] === "1") {'); | |
| parts.push(' return "' + failVal + '";'); | |
| parts.push(' }'); | |
| parts.push(' }'); | |
| parts.push(' return "' + passVal + '";'); | |
| parts.push(' } catch (e) {'); | |
| parts.push(' return "' + errVal + '";'); | |
| parts.push(' }'); | |
| parts.push('}'); | |
| } else if (style === 'standalone') { | |
| parts.push('function ' + fnName + '() {'); | |
| parts.push(' var results = {};'); | |
| enabledChecks.forEach(ch => { | |
| const src = CHECK_SOURCES[ch.id]; | |
| const checkFnName = getFnNameFromSource(src); | |
| parts.push(' try { results["' + ch.id + '"] = ' + checkFnName + '(); } catch(e) { results["' + ch.id + '"] = "' + ch.id + ':e"; }'); | |
| }); | |
| parts.push(''); | |
| parts.push(' var dominated = false;'); | |
| parts.push(' for (var k in results) {'); | |
| parts.push(' if (results[k].split(":")[1] === "1") { dominated = true; break; }'); | |
| parts.push(' }'); | |
| parts.push(''); | |
| parts.push(' return {'); | |
| parts.push(' verdict: dominated ? "' + failVal + '" : "' + passVal + '",'); | |
| parts.push(' checks: results'); | |
| parts.push(' };'); | |
| parts.push('}'); | |
| } | |
| parts.push(''); | |
| parts.push('// ── Execute ──'); | |
| if (style === 'standalone') { | |
| parts.push('var fingerprint = ' + fnName + '();'); | |
| parts.push('console.log("Verdict:", fingerprint.verdict);'); | |
| parts.push('console.log("Details:", fingerprint.checks);'); | |
| } else { | |
| parts.push('var fingerprint = ' + fnName + '();'); | |
| parts.push('console.log("Fingerprint result:", fingerprint);'); | |
| } | |
| const raw = parts.join('\n'); | |
| lastGeneratedCode = raw; | |
| // Syntax highlight | |
| const highlighted = highlightJS(raw); | |
| document.getElementById('codeOutput').innerHTML = highlighted; | |
| document.getElementById('cgInfo').textContent = enabledChecks.length + ' checks included · ' + raw.split('\n').length + ' lines'; | |
| } | |
| var lastGeneratedCode = ''; | |
| function highlightJS(code) { | |
| // Escape HTML first | |
| let h = code.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'); | |
| // Comments | |
| h = h.replace(/(\/\/[^\n]*)/g, '<span class="cm-cmt">$1</span>'); | |
| // Strings (double-quoted) | |
| h = h.replace(/"([^"\\]*(\\.[^"\\]*)*)"/g, '<span class="cm-str">"$1"</span>'); | |
| // Keywords | |
| var kws = ['function','var','const','let','if','else','else if','for','try','catch','throw','return','typeof','in','new','true','false','undefined','null']; | |
| kws.forEach(function(kw) { | |
| h = h.replace(new RegExp('\\b(' + kw + ')\\b', 'g'), '<span class="cm-kw">$1</span>'); | |
| }); | |
| // Numbers | |
| h = h.replace(/\b(\d+)\b/g, '<span class="cm-num">$1</span>'); | |
| return h; | |
| } | |
| function copyCode() { | |
| if (!lastGeneratedCode) return; | |
| navigator.clipboard.writeText(lastGeneratedCode).then(function() { | |
| var btn = document.getElementById('copyBtn'); | |
| btn.textContent = 'Copied!'; | |
| btn.classList.add('copied'); | |
| setTimeout(function() { btn.textContent = 'Copy'; btn.classList.remove('copied'); }, 2000); | |
| }); | |
| } | |
| function downloadCode() { | |
| if (!lastGeneratedCode) { generateCode(); } | |
| if (!lastGeneratedCode) return; | |
| var blob = new Blob([lastGeneratedCode], { type: 'text/javascript' }); | |
| var url = URL.createObjectURL(blob); | |
| var a = document.createElement('a'); | |
| a.href = url; | |
| a.download = (document.getElementById('cgFnName').value || 'chk') + '_fingerprint.js'; | |
| document.body.appendChild(a); | |
| a.click(); | |
| document.body.removeChild(a); | |
| URL.revokeObjectURL(url); | |
| } | |
| // ═══════════════════════════════════════════════════════ | |
| // INIT | |
| // ═══════════════════════════════════════════════════════ | |
| initChecks(); | |
| renderGrid(); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment