UNPKG

@trustcaptcha/trustcaptcha-frontend

Version:

Frondend library for trustcaptcha

383 lines (382 loc) 14.8 kB
import { VERSION } from "../../../../version"; export class InformationCollector { constructor(box) { this.box = box; } async collectInformation() { const browserInformation = await this.collectProperties(this.box.config.mode === 'minimal' ? this.minimalPropertiesToCollect() : this.standardPropertiesToCollect()); let userEvents = []; if (this.box.config.mode === 'standard') { userEvents = this.box.eventTracker.getEventsAndResetArray(); } return { custom: { boxCreationTimestamp: this.box.boxCreationTimestamp, startSolvingTimestamp: new Date(), timezone: this.getTimezone(), honeypotField: this.box.honeypotField.value, framework: this.safeGet(() => this.detectFrontendFramework()), version: VERSION, bypassToken: this.box.config.bypassToken, mode: this.getMode(), boxSettings: { language: this.box.config.language, currentLanguage: this.box.config.translation.language, theme: this.box.config.theme, currentTheme: this.box.config.currentTheme, autostart: this.box.config.autostart, mode: this.box.config.mode, slider: this.box.config.slider, hideBranding: this.box.config.hideBranding, hidePrivacy: this.box.config.hidePrivacy, customPrivacy: !this.box.config.privacyUrl.includes("https://www.trustcaptcha.com/"), invisible: this.box.config.invisible, } }, browserInformation: browserInformation, userEvents: userEvents, }; } getTimezone() { var _a; const DateTimeFormat = (_a = window.Intl) === null || _a === void 0 ? void 0 : _a.DateTimeFormat; if (DateTimeFormat) { const timezone = new DateTimeFormat().resolvedOptions().timeZone; if (timezone) { return timezone; } } const offset = -this.getTimezoneOffset(); return `UTC${offset >= 0 ? '+' : ''}${Math.abs(offset)}`; } getTimezoneOffset() { const currentYear = new Date().getFullYear(); return Math.max(this.toFloat(new Date(currentYear, 0, 1).getTimezoneOffset()), this.toFloat(new Date(currentYear, 6, 1).getTimezoneOffset())); } toFloat(value) { return parseFloat(value); } getMode() { return this.box.config.mode === 'minimal' ? 'MINIMAL_DATA' : 'STANDARD'; } minimalPropertiesToCollect() { return [ 'devicePixelRatio', 'navigator.connection.downlink', 'navigator.connection.effectiveType', 'navigator.connection.rtt', 'navigator.connection.saveData', 'location.href', 'navigator.cookieEnabled', 'navigator.deviceMemory', 'navigator.doNotTrack', 'navigator.hardwareConcurrency', 'navigator.language', 'navigator.languages', 'navigator.userAgent', 'origin', ]; } standardPropertiesToCollect() { return [ 'navigator.connection.downlink', 'navigator.connection.effectiveType', 'navigator.connection.rtt', 'navigator.connection.saveData', 'navigator.connection.type', 'devicePixelRatio', 'history.length', 'innerHeight', 'innerWidth', 'length', 'location.href', 'locationbar.visible', 'menubar.visible', 'navigator.cookieEnabled', 'navigator.deviceMemory', 'navigator.doNotTrack', 'navigator.hardwareConcurrency', 'navigator.language', 'navigator.languages', 'navigator.maxTouchPoints', 'navigator.pdfViewerEnabled', 'navigator.platform', 'navigator.userAgent', 'navigator.webdriver', 'navigator.vendor', 'origin', 'performance.navigation.type', 'performance.navigation.redirectCount', 'performance.now()', 'outerHeight', 'outerWidth', 'personalbar.visible', 'screen.availHeight', 'screen.availWidth', 'screen.colorDepth', 'screen.height', 'screen.orientation.angle', 'screen.orientation.type', 'screen.pixelDepth', 'screen.width', 'screenLeft', 'screenTop', 'screenX', 'screenY', 'scrollbars.visible' ]; } async collectProperties(paths) { const result = {}; for (const path of paths) { const fullPath = 'window.' + path; const key = fullPath.replace(/\./g, '-'); result[key] = this.safeGet(() => this.getValueAtPath(fullPath)); } Object.assign(result, { 'dom-automationNote': this.safeGet(() => this.getAutomationNote()), 'dom-webGlSupport': this.safeGet(() => this.isWebGLSupported()), 'dom-canvasSupport': this.safeGet(() => this.isCanvasSupported()), 'cookies': document.cookie.split('; ').map((cookie) => cookie.split('=')[0]), 'plugins': Array.from(navigator.plugins).map(plugin => plugin.name).join(','), 'fp-audio': await this.hashData(await this.getAudioFingerprint()), 'fp-canvas': await this.hashData(this.getCanvasFingerprint()), 'fp-webgl': await this.hashData(this.getWebGLFingerprint()), 'fp-navigator': await this.hashData(this.getNavigatorFingerprint()), 'fp-fonts': await this.hashData(this.getFontsFingerprint()), 'fp-screen': await this.hashData(this.getScreenFingerprint()), }); const sortedKeys = Object.keys(result).sort(); const serialized = sortedKeys .map(key => { const value = Array.isArray(result[key]) ? `[${result[key].join(', ')}]` : result[key]; return `${key}=${value}`; }) .join(', '); const mc = await this.hashData(serialized); Object.assign(result, { 'mc': mc }); return result; } getValueAtPath(path) { const parts = path.split('.'); let current = window; for (const part of parts.slice(1)) { if (!current) { return null; } const methodMatch = part.match(/^(\w+)\(\)$/); if (methodMatch) { const methodName = methodMatch[1]; try { current = current[methodName](); } catch (_a) { return null; } } else { current = current[part]; } } return current; } safeGet(fn) { try { const result = fn(); if (result === undefined) { return null; } return result; } catch (_a) { return null; } } getAutomationNote() { const documentDetectionKeys = [ '__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', ]; const windowDetectionKeys = [ '_phantom', '__nightmare', '_selenium', 'callPhantom', 'callSelenium', '_Selenium_IDE_Recorder', ]; for (const key of windowDetectionKeys) { if (window[key]) { return true; } } for (const key of documentDetectionKeys) { if (window.document[key]) { return true; } } for (const key in window.document) { if (key.match(/\$[a-z]dc_/) && window.document[key]['cache_']) { return true; } } if (window['external'] && window['external'].toString() && window['external'].toString().indexOf('Sequentum') !== -1) { return true; } if (window.document.documentElement.getAttribute('selenium')) return true; if (window.document.documentElement.getAttribute('webdriver')) return true; if (window.document.documentElement.getAttribute('driver')) return true; return false; } isCanvasSupported() { const elem = document.createElement('canvas'); return !!(elem.getContext && elem.getContext('2d')); } isWebGLSupported() { try { const canvas = document.createElement('canvas'); return !!(window.WebGLRenderingContext && (canvas.getContext('webgl') || canvas.getContext('experimental-webgl'))); } catch (e) { return false; } } detectFrontendFramework() { if (this.box.config.framework) { return this.box.config.framework; } try { if (!!document.querySelector('[data-reactroot], [data-reactid]')) { return 'react'; } if (!!document.querySelector('[ng-version]')) { return 'angular'; } if (!!document.querySelector('[data-vue]')) { return 'vue'; } return 'other'; } catch (error) { return 'other'; } } getCanvasFingerprint() { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); ctx.textBaseline = 'top'; ctx.font = '14px Arial'; ctx.fillText('Browser Fingerprint', 2, 2); return canvas.toDataURL(); } getWebGLFingerprint() { const canvas = document.createElement('canvas'); const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); if (!gl) return null; const debugInfo = gl.getExtension('WEBGL_debug_renderer_info'); const vendor = gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL); const renderer = gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL); return vendor + '|' + renderer; } getAudioFingerprint() { return new Promise((resolve, reject) => { try { const audioCtx = new (window.AudioContext || window.webkitAudioContext)(); const oscillator = audioCtx.createOscillator(); const analyser = audioCtx.createAnalyser(); const gain = audioCtx.createGain(); const scriptProcessor = audioCtx.createScriptProcessor(4096, 1, 1); let fingerprint = ''; scriptProcessor.onaudioprocess = function () { const array = new Uint8Array(analyser.frequencyBinCount); analyser.getByteFrequencyData(array); fingerprint = array.join(''); audioCtx.close(); resolve(fingerprint); }; oscillator.type = 'triangle'; oscillator.connect(analyser); analyser.connect(scriptProcessor); scriptProcessor.connect(gain); gain.connect(audioCtx.destination); oscillator.start(0); setTimeout(() => { if (!fingerprint) { audioCtx.close(); resolve(''); } }, 500); } catch (error) { reject(error); } }); } getNavigatorFingerprint() { return `${this.safeGet(() => navigator.cookieEnabled)}|${this.safeGet(() => navigator.doNotTrack)}|${this.safeGet(() => navigator.pdfViewerEnabled)}|${this.safeGet(() => navigator.platform)}|${this.safeGet(() => navigator.vendor)}`; } getScreenFingerprint() { return `${this.safeGet(() => window.screen.width)}x${this.safeGet(() => window.screen.height)}x${this.safeGet(() => window.screen.colorDepth)}x${this.safeGet(() => window.screen.pixelDepth)}x${this.safeGet(() => window.screen.orientation.type)}`; } getFontsFingerprint() { const baseFonts = ["monospace", "sans-serif", "serif"]; const testString = "mmmmmmmmmmlli"; const testSize = "72px"; const canvas = document.createElement("canvas"); const context = canvas.getContext("2d"); const widths = {}; baseFonts.forEach((font) => { context.font = `${testSize} ${font}`; widths[font] = context.measureText(testString).width; }); const detectedFonts = []; const fontList = [ "Arial", "Verdana", "Times New Roman", "Courier New", "Comic Sans MS", "Georgia", "Impact", "Trebuchet MS", "Palatino", "Tahoma", "Helvetica", "Geneva", "Menlo", "Monaco", "Didot", "Gill Sans", "Segoe UI", "Candara", "Consolas", "Franklin Gothic Medium", "Book Antiqua", "Roboto", "Open Sans", "Lato", "Oswald", "Montserrat", "Futura", "Garamond", "Baskerville", "Optima", "Lucida Bright", "Century Gothic", "Arial Unicode MS", "SimSun", "MS Mincho", "Batang", "Gulim" ]; fontList.forEach((font) => { const testFont = `${font}, monospace`; context.font = `${testSize} ${testFont}`; const width = context.measureText(testString).width; if (width !== widths["monospace"]) { detectedFonts.push(font); } }); return detectedFonts.join(","); } async hashData(data) { const encoder = new TextEncoder(); const dataBuffer = encoder.encode(data); const hashBuffer = await crypto.subtle.digest('SHA-256', dataBuffer); return Array.from(new Uint8Array(hashBuffer)) .map(b => b.toString(16).padStart(2, '0')) .join(''); } } //# sourceMappingURL=information-collector.js.map