@gamma-sweep/web-sdk 0.0.1-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +159 -0
- package/dist/gs.cjs +2 -0
- package/dist/gs.cjs.map +1 -0
- package/dist/gs.esm.js +2 -0
- package/dist/gs.esm.js.map +1 -0
- package/dist/gs.js +2 -0
- package/dist/gs.js.map +1 -0
- package/dist/index.d.ts +212 -0
- package/dist/sri.json +18 -0
- package/package.json +70 -0
package/dist/gs.esm.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e={v1:{kty:"RSA",n:"iudSshKiZs1hF1f3KvvoOU15MtR4iVuHHLQ1dx3wvWUbZYfSvbkRrV7WJe3lH3xcMWyiC2WIi7O9Remwr3qWI50RWBVEKNr9uLBCZUmZyPKCnGA6o3-Lm-BYjqpT8LO5QtS0G2jljX3DnOBHz0WDG56oE2g1u2nby_QyIpK_VdNLRq2xDx13_uYtIvQ5hZOu4-_5UQrXdU3IugmOVu7-YOpKoDwb3DjJyJ98iBNfqEi-WCfzw9CyURpY9i19sj0GHFQwxD7JqT7VpKJCIbGS0FTk7ZZe9XMPpABdVe3eR0FoOaEB5i7SHgtznJnkdjPiPb896rQS6CmCvubZ-iHrUGL4fky5Q5SIgYYkXofQN3qayei59h5clBSfkVM69fFQiK0swNAHF2FBAH-ZEardlF897c1uf8W8KsAbMKk5jgy_bZosZgf85GdOsRs-8uCqgO_fXELHj5Eb9-UtueE2LwDrOCwTsK1Ib_QlINotoXsClTk1MQiRGaEvV_fPyLLG5Ytrw82GIubqz2cZunr8h-yxVUv7Hq51Vnp3hUA3__mVt4TWsiImIdiCo6S6TS5T2FqBOOsNUdYmYpoutq_5aMm4byx0TNVDGf_5lKPN3ldllikM5TJ4Wr3yKLg1Oj_pbrMvIeCwnRIJwWUwmgzW2l1LOctK1kpf2hGJRkUF7pE",e:"AQAB"}};function t(e){let t="";for(let n=0;n<e.length;n++)t+=String.fromCharCode(e[n]);return btoa(t).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}const n=new TextEncoder;async function o(o,l="v1"){const r=e[l];if(!r)throw new Error(`unknown kid: ${l}`);const a={alg:"RSA-OAEP-256",enc:"A256GCM",kid:l,typ:"GSDS"},i=t(n.encode(JSON.stringify(a))),s=n.encode(i),u=await crypto.subtle.generateKey({name:"AES-GCM",length:256},!0,["encrypt"]),c=new Uint8Array(await crypto.subtle.exportKey("raw",u)),d=await async function(e){return crypto.subtle.importKey("jwk",{...e,alg:"RSA-OAEP-256",ext:!0},{name:"RSA-OAEP",hash:"SHA-256"},!1,["encrypt"])}(r),m=new Uint8Array(await crypto.subtle.encrypt({name:"RSA-OAEP"},d,c)),p=crypto.getRandomValues(new Uint8Array(12)),_=n.encode(JSON.stringify(o)),f=new Uint8Array(await crypto.subtle.encrypt({name:"AES-GCM",iv:p,additionalData:s,tagLength:128},u,_)),h=f.slice(0,f.length-16),g=f.slice(f.length-16);return[i,t(m),t(p),t(h),t(g)].join(".")}const l=new TextEncoder;async function r(e,t=32){const n=await async function(e){const t="string"==typeof e?l.encode(e):e,n=await crypto.subtle.digest("SHA-256",t);return a(new Uint8Array(n))}(e);return n.slice(0,t)}function a(e){let t="";for(let n=0;n<e.length;n++)t+=e[n].toString(16).padStart(2,"0");return t}function i(e){const t=new Uint8Array(e);return crypto.getRandomValues(t),a(t)}function s(){if("function"==typeof crypto.randomUUID)return crypto.randomUUID();const e=new Uint8Array(16);crypto.getRandomValues(e),e[6]=15&e[6]|64,e[8]=63&e[8]|128;const t=a(e);return`${t.slice(0,8)}-${t.slice(8,12)}-${t.slice(12,16)}-${t.slice(16,20)}-${t.slice(20)}`}async function u(e,t,n=0){try{return n>0?await Promise.race([Promise.resolve(e),new Promise(e=>setTimeout(()=>e(t),n))]):await Promise.resolve(e)}catch{return t}}function c(e,t){try{return e()}catch{return t}}const d=()=>"undefined"!=typeof performance&&performance.now?performance.now():Date.now(),m="gs_tdid",p="kv";function _(){return new Promise(e=>{try{if("undefined"==typeof indexedDB)return e(null);const t=indexedDB.open("gs_web_sdk",1);t.onupgradeneeded=()=>{try{t.result.createObjectStore(p)}catch{}},t.onsuccess=()=>e(t.result),t.onerror=()=>e(null),setTimeout(()=>e(null),600)}catch{e(null)}})}async function f(e){if(!e)return{id:s(),present_before:!1};const t=function(){try{return window.localStorage.getItem(m)}catch{return null}}(),n=function(){try{const e=document.cookie.match(new RegExp("(?:^|; )"+m+"=([^;]*)"));return e?decodeURIComponent(e[1]):null}catch{return null}}(),o=await async function(){const e=await _();return e?new Promise(t=>{try{const n=e.transaction(p,"readonly").objectStore(p).get(m);n.onsuccess=()=>t("string"==typeof n.result?n.result:null),n.onerror=()=>t(null)}catch{t(null)}}):null}(),l=t||n||o||null,r=l??s();return t!==r&&function(e){try{window.localStorage.setItem(m,e)}catch{}}(r),n!==r&&function(e){try{const t="https:"===location.protocol?"; Secure":"";document.cookie=`${m}=${encodeURIComponent(e)}; Max-Age=34560000; Path=/; SameSite=Lax${t}`}catch{}}(r),o!==r&&await async function(e){const t=await _();t&&await new Promise(n=>{try{const o=t.transaction(p,"readwrite");o.objectStore(p).put(e,m),o.oncomplete=()=>n(),o.onerror=()=>n()}catch{n()}})}(r),{id:r,present_before:null!==l}}function h(e){return c(()=>{const t=document.createElement("canvas"),n=t.getContext(e);if(!n)return{imageHash:null,paramsHash:null,vendor:null,renderer:null};const o=n.getExtension("WEBGL_debug_renderer_info"),l=[],r=(e,t)=>l.push(`${e}=${String(t)}`);let a=null,i=null;o&&(a=String(n.getParameter(o.UNMASKED_VENDOR_WEBGL)??"")||null,i=String(n.getParameter(o.UNMASKED_RENDERER_WEBGL)??"")||null,r("vendor",a),r("renderer",i));const s=[n.MAX_TEXTURE_SIZE,n.MAX_RENDERBUFFER_SIZE,n.MAX_VERTEX_ATTRIBS,n.MAX_VARYING_VECTORS,n.MAX_VERTEX_UNIFORM_VECTORS,n.MAX_FRAGMENT_UNIFORM_VECTORS,n.MAX_TEXTURE_IMAGE_UNITS,n.MAX_COMBINED_TEXTURE_IMAGE_UNITS,n.ALIASED_LINE_WIDTH_RANGE,n.ALIASED_POINT_SIZE_RANGE,n.MAX_VIEWPORT_DIMS];for(const e of s)r(String(e),n.getParameter(e));const u=n.getSupportedExtensions();u&&r("exts",u.slice().sort().join(","));let c=null;try{const e=n.createBuffer();n.bindBuffer(n.ARRAY_BUFFER,e);const o=new Float32Array([-.2,-.9,0,.4,-.26,0,0,.7,0]);n.bufferData(n.ARRAY_BUFFER,o,n.STATIC_DRAW);const l=new Uint8Array(t.width*t.height*4);n.readPixels(0,0,t.width,t.height,n.RGBA,n.UNSIGNED_BYTE,l),c=Array.from(l.slice(0,256)).join(",")}catch{c=null}return{imageHash:c,paramsHash:l.join("|")||null,vendor:a,renderer:i}},{imageHash:null,paramsHash:null,vendor:null,renderer:null})}async function g(){try{const e=navigator.userAgentData;if(!e)return null;let t={};"function"==typeof e.getHighEntropyValues&&(t=await e.getHighEntropyValues(["architecture","bitness","model","platformVersion","uaFullVersion"]));const n=e=>"string"==typeof e&&e.length>0?e:null;return{platform:n(t.platform)??n(e.platform),platform_version:n(t.platformVersion),architecture:n(t.architecture),bitness:n(t.bitness),mobile:"boolean"==typeof e.mobile?e.mobile:null,model:n(t.model),ua_full_version:n(t.uaFullVersion)}}catch{return null}}async function y(){try{const e=navigator.mediaDevices;if(!e||"function"!=typeof e.enumerateDevices)return null;const t=await e.enumerateDevices(),n={audio_input:0,audio_output:0,video_input:0};for(const e of t)"audioinput"===e.kind?n.audio_input++:"audiooutput"===e.kind?n.audio_output++:"videoinput"===e.kind&&n.video_input++;return n}catch{return null}}const w=["Arial","Arial Black","Arial Narrow","Calibri","Cambria","Comic Sans MS","Consolas","Courier New","Georgia","Helvetica","Impact","Lucida Console","Palatino Linotype","Segoe UI","Tahoma","Times New Roman","Trebuchet MS","Verdana","Menlo","Monaco","Roboto","Ubuntu","Cantarell","Noto Sans"];async function v(e){const t=navigator,[n,o]=await Promise.all([Promise.resolve(c(()=>{const e=document.createElement("canvas");e.width=280,e.height=60;const t=e.getContext("2d");return t?(t.textBaseline="top",t.font="14px 'Arial'",t.fillStyle="#f60",t.fillRect(125,1,62,20),t.fillStyle="#069",t.fillText("GS🔒 fp.io 0123",2,15),t.fillStyle="rgba(102, 204, 0, 0.7)",t.fillText("GS🔒 fp.io 0123",4,17),t.globalCompositeOperation="multiply",t.fillStyle="rgb(255,0,255)",t.beginPath(),t.arc(50,30,20,0,2*Math.PI,!0),t.fill(),e.toDataURL()):null},null)),u(new Promise(e=>{try{const t=window.OfflineAudioContext||window.webkitOfflineAudioContext;if(!t)return e(null);const n=new t(1,5e3,44100),o=n.createOscillator();o.type="triangle",o.frequency.value=1e4;const l=n.createDynamicsCompressor();l.threshold.value=-50,l.knee.value=40,l.ratio.value=12,l.attack.value=0,l.release.value=.25,o.connect(l),l.connect(n.destination),o.start(0),n.startRendering();const r=setTimeout(()=>e(null),800);n.oncomplete=t=>{clearTimeout(r);try{const n=t.renderedBuffer.getChannelData(0).slice(4500,4600);let o=0;for(let e=0;e<n.length;e++)o+=Math.abs(n[e]);e(o.toString())}catch{e(null)}}}catch{e(null)}}),null,1e3)]),l=h("webgl"),a=h("webgl2"),i=c(()=>{const e=["monospace","sans-serif","serif"],t=document.createElement("span");t.style.position="absolute",t.style.left="-9999px",t.style.fontSize="72px",t.textContent="mmmmmmmmmmlli",document.body.appendChild(t);const n={};for(const o of e)t.style.fontFamily=o,n[o]={w:t.offsetWidth,h:t.offsetHeight};const o=[];for(const l of w){let r=!1;for(const o of e)if(t.style.fontFamily=`'${l}',${o}`,t.offsetWidth!==n[o].w||t.offsetHeight!==n[o].h){r=!0;break}r&&o.push(l)}return document.body.removeChild(t),o},null),s=c(()=>{const e=screen.orientation;return{width:screen.width??null,height:screen.height??null,color_depth:screen.colorDepth??null,pixel_ratio:window.devicePixelRatio??null,refresh_rate:null,is_extended:screen.isExtended??null,available_width:screen.availWidth??null,available_height:screen.availHeight??null,pixel_depth:screen.pixelDepth??null,orientation_type:e?.type??null,orientation_angle:"number"==typeof e?.angle?e.angle:null}},{width:null,height:null,color_depth:null,pixel_ratio:null,refresh_rate:null,is_extended:null,available_width:null,available_height:null,pixel_depth:null,orientation_type:null,orientation_angle:null}),d=c(()=>{const e=navigator.plugins;if(!e||0===e.length)return[];const t=[];for(let n=0;n<e.length;n++){const o=e[n]?.name;o&&t.push(o)}return t},null),m=c(()=>{const e=navigator.mimeTypes;if(!e||0===e.length)return"";const t=[];for(let n=0;n<e.length;n++){const o=e[n]?.type;o&&t.push(o)}return t.sort().join(",")},null),p=c(()=>[Math.acos(.123456789),Math.acosh(1.0000001),Math.asin(.123456789),Math.asinh(.123456789),Math.atan(.123456789),Math.atanh(.123456789),Math.cbrt(100),Math.cos(1e10),Math.cosh(10),Math.exp(10),Math.expm1(1),Math.log1p(10),Math.sin(1e10),Math.sinh(10),Math.tan(1e10),Math.tanh(.123456789),Math.pow(Math.PI,-100)].map(e=>e.toString()).join(","),null),_=c(()=>{const e=["ActiveText","ButtonBorder","ButtonFace","ButtonText","Canvas","CanvasText","Field","FieldText","GrayText","Highlight","HighlightText","LinkText","Mark","MarkText","VisitedText"],t=document.createElement("div");t.style.position="absolute",t.style.left="-9999px",document.body.appendChild(t);const n=[];for(const o of e)t.style.color=o,n.push(`${o}=${getComputedStyle(t).color}`);return document.body.removeChild(t),n.join("|")},null),v=c(()=>Intl.DateTimeFormat().resolvedOptions().locale||null,null),[b,A]=await Promise.all([u(g(),null,800),u(y(),null,800)]),x=c(()=>t.platform||null,null),C=c(()=>window.location?.href||null,null),S=c(()=>window.location?.hostname||null,null),k=c(()=>t.vendor||null,null),T=c(()=>Intl.DateTimeFormat().resolvedOptions().timeZone||null,null),M=c(()=>(new Date).getTimezoneOffset(),null),E=c(()=>t.languages?Array.from(t.languages):t.language?[t.language]:null,null),P=c(()=>t.deviceMemory??null,null),I=c(()=>t.hardwareConcurrency??null,null),D=c(()=>"ontouchstart"in window||t.maxTouchPoints>0,null),R=c(()=>{const e=t.doNotTrack??window.doNotTrack;return"1"===e||"yes"===e||"0"!==e&&"no"!==e&&null},null),U=c(()=>t.globalPrivacyControl??null,null),{id:H,present_before:O}=await f(e),[N,F,L,j,B,G,V,q,W,K]=await Promise.all([n?r(n):Promise.resolve(null),l.imageHash?r(l.imageHash):Promise.resolve(null),l.paramsHash?r(l.paramsHash):Promise.resolve(null),a.paramsHash?r(a.paramsHash):Promise.resolve(null),o?r(o):Promise.resolve(null),i&&i.length?r(i.join(",")):Promise.resolve(null),d&&d.length?r(d.join(",")):Promise.resolve(null),m?r(m):Promise.resolve(null),p?r(p):Promise.resolve(null),_?r(_):Promise.resolve(null)]),$=[x,k,P,I,D,s.width,s.height,s.color_depth,s.pixel_ratio,N,F,L,j,B,G].join("~"),X=await r($),Z=[c(()=>t.userAgent,""),E?.join(",")??"",T,M,x,k].join("~");return{device_hash:X,true_device_id:H,browser_hash:await r(Z),cookie_hash:await r(`${H}~${X}`),canvas_hash:N,webgl_hash:L,webgl2_hash:j,webgl_image_hash:F,webgl_params_hash:L,audio_hash:B,font_list_hash:G,spoofing_hash:null,screen:s,touch_support:D,device_memory:P,hardware_concurrency:I,platform:x,vendor:k,timezone:T,timezone_offset:M,languages:E,do_not_track:R,gpc:U,font_list:i,plugin_list:d,plugin_count:d?d.length:null,plugin_hash:V,mime_types_hash:q,math_hash:W,system_colors_hash:K,webgl_vendor:l.vendor,webgl_renderer:l.renderer,locale:v,timezone_country:null,user_agent_data:b,media_devices:A,window_location:C,window_hostname:S,__persistent_id_present:O}}async function b(){const e=function(){const e=[],t=window,n=[["webdriver",()=>!0===navigator.webdriver],["cdc_props",()=>Object.keys(t).some(e=>/^[$_]?cdc_/.test(e)||/\$cdc_/.test(e))],["__webdriver_evaluate",()=>"__webdriver_evaluate"in t||"__driver_evaluate"in t],["__selenium",()=>"__selenium_unwrapped"in t||"__webdriver_script_fn"in t],["__nightmare",()=>"__nightmare"in t],["_phantom",()=>"_phantom"in t||"callPhantom"in t],["domAutomation",()=>"domAutomation"in t||"domAutomationController"in t],["puppeteer",()=>"__puppeteer_evaluation_script__"in t],["playwright",()=>"__playwright"in t||"__pw_manual"in t]];for(const[t,o]of n)c(o,!1)&&e.push(t);return e}(),t=await async function(){return await new Promise(e=>{try{const t=navigator.storage;if(t?.estimate)return t.estimate().then(t=>{"number"==typeof t.quota?e(t.quota<12e7):e(null)}).catch(()=>e(null)),void setTimeout(()=>e(null),500);e(null)}catch{e(null)}})}(),n=c(()=>!0===navigator.webdriver,!1),o=function(e){return c(()=>{if(e.length>0)return!0;const t=navigator.userAgent||"";if(/HeadlessChrome|PhantomJS|Electron/i.test(t))return!0;const n=0===(navigator.plugins?.length??0),o=!navigator.languages||0===navigator.languages.length;return n&&o},!1)}(e),l=c(()=>{const e=navigator.userAgent||"",t=/Mobi|Android|iPhone|iPad/i.test(e),n="ontouchstart"in window||navigator.maxTouchPoints>0;return!(!t||n)},!1),r=c(()=>{const e=[Function.prototype.bind,navigator.permissions?.query,HTMLCanvasElement.prototype.toDataURL,WebGLRenderingContext.prototype.getParameter].filter(Boolean);for(const t of e){const e=Function.prototype.toString.call(t);if(!/\{\s*\[native code\]\s*\}/.test(e))return!0}const t=Function.prototype.toString.toString();return!/\{\s*\[native code\]\s*\}/.test(t)},!1);return{is_webdriver:n,is_headless:o,is_emulator:l,function_tampered:r,ua_platform_mismatch:c(()=>{const e=(navigator.userAgent||"").toLowerCase(),t=(navigator.platform||"").toLowerCase();if(!t)return!1;const n=e.includes("windows"),o=e.includes("mac os")||e.includes("macintosh"),l=e.includes("linux")&&!e.includes("android"),r=t.includes("win"),a=t.includes("mac"),i=t.includes("linux")||t.includes("x11");return!(!n||r)||!(!o||a)||!(!l||i)},!1),private_mode:t,automation_flags:e,screen_mirrored:c(()=>!!(screen.colorDepth&&screen.colorDepth<=16)||screen.availWidth>screen.width||screen.availHeight>screen.height,null),remote_desktop_suspected:c(()=>{const e=window.devicePixelRatio;return(0===e||!e)&&screen.colorDepth<=16},null)}}const A=315576e5,x={chrome:{major:116,date:Date.UTC(2023,7,15),cadenceMs:24192e5},edge:{major:116,date:Date.UTC(2023,7,17),cadenceMs:24192e5},firefox:{major:116,date:Date.UTC(2023,7,1),cadenceMs:24192e5},safari:{major:16,date:Date.UTC(2022,8,12),cadenceMs:Math.round(A)},opera:{major:102,date:Date.UTC(2023,7,22),cadenceMs:24192e5}};function C(){return c(()=>{const e=navigator.userAgentData;if(!e?.brands?.length)return null;const t=e.brands.find(e=>!/Not.?A.?Brand|Chromium/i.test(e.brand))||e.brands.find(e=>/Chromium/i.test(e.brand));if(!t)return null;const n=/edge/i.test(t.brand)?"edge":/opera|opr/i.test(t.brand)?"opera":/chrome|chromium/i.test(t.brand)?"chrome":t.brand.toLowerCase(),o=parseInt(t.version,10);return{name:n,version:t.version,major:Number.isFinite(o)?o:null}},null)}async function S(){const e=c(()=>navigator.userAgent||null,null),t=e?function(e){const t=[["edge",/Edg(?:e|A|iOS)?\/([\d.]+)/],["opera",/OPR\/([\d.]+)/],["firefox",/Firefox\/([\d.]+)/],["chrome",/Chrome\/([\d.]+)/],["safari",/Version\/([\d.]+).*Safari/]];for(const[n,o]of t){const t=e.match(o);if(t){const e=t[1],o=parseInt(e.split(".")[0],10);return{name:n,version:e,major:Number.isFinite(o)?o:null}}}return{name:null,version:null,major:null}}(e):{name:null,version:null,major:null},n=C(),o=n?.name??t.name,l=n?.version??t.version,r=function(e,t){if(!e||null==t)return null;const n=x[e];if(!n)return null;const o=n.date+(t-n.major)*n.cadenceMs,l=Date.now()-o;return Math.max(0,l/A)}(o,n?.major??t.major),{blocked:a,dynamicBlocked:i}=await new Promise(e=>{try{if(!document.body)return e({blocked:null,dynamicBlocked:null});const t=document.createElement("div");t.className="pub_300x250 ad-banner adsbox sponsor-ad ad-placement",t.style.cssText="position:absolute;left:-9999px;top:-9999px;height:10px;width:10px;",t.innerHTML=" ",document.body.appendChild(t);const n=document.createElement("ins");n.className="adsbygoogle",n.style.cssText="position:absolute;left:-9999px;display:block;height:10px;width:10px;",document.body.appendChild(n),setTimeout(()=>{const o=c(()=>null===t.offsetParent||0===t.offsetHeight||0===t.clientHeight||"none"===getComputedStyle(t).display,null),l=c(()=>0===n.offsetHeight||"none"===getComputedStyle(n).display,null);try{document.body.removeChild(t),document.body.removeChild(n)}catch{}e({blocked:o,dynamicBlocked:l})},120)}catch{e({blocked:null,dynamicBlocked:null})}});return{user_agent:e,browser_name:o,browser_version:l,version_age_years:null==r?null:Math.round(10*r)/10,version_age_bucket:(u=r,null==u?null:u<1?"<1":u<2?"1-2":u<5?"2-5":">=5"),privacy_extension_detected:a,is_bot:c(()=>{if(!0===navigator.webdriver)return!0;const e=navigator.userAgent||"";return!!/bot|crawler|spider|crawling|HeadlessChrome|PhantomJS/i.test(e)||!e},!1),spoofing_detected:(s=t,c(()=>{const e=navigator.userAgent||"",t=window;if(("chrome"===s.name||"edge"===s.name)&&/Chrome\//.test(e)&&!("chrome"in t))return!0;if(Array.isArray(navigator.languages)&&0===navigator.languages.length&&navigator.language)return!0;const n=C();return!(!n?.name||!s.name||n.name===s.name||"chrome"===n.name&&"edge"===s.name)},!1)),dynamic_component_blocked:i};var s,u}async function k(e){const{quota:t,usage:n}=await async function(){return await new Promise(e=>{try{const t=navigator.storage;if(!t?.estimate)return e({quota:null,usage:null});t.estimate().then(t=>e({quota:t.quota??null,usage:t.usage??null})).catch(()=>e({quota:null,usage:null})),setTimeout(()=>e({quota:null,usage:null}),500)}catch{e({quota:null,usage:null})}})}();return{cookies_enabled:c(()=>navigator.cookieEnabled,null),local_storage:c(()=>{const e="__gs_t";return window.localStorage.setItem(e,"1"),window.localStorage.removeItem(e),!0},!1),session_storage:c(()=>{const e="__gs_t";return window.sessionStorage.setItem(e,"1"),window.sessionStorage.removeItem(e),!0},!1),indexed_db:c(()=>"undefined"!=typeof indexedDB&&null!==indexedDB,!1),cache_api:c(()=>"undefined"!=typeof caches&&null!==caches,!1),quota_bytes:t,usage_bytes:n,persistent_id_present:e}}function T(e=null){return{latitude:null,longitude:null,accuracy_m:null,permission_state:e}}function M(e){return"unavailable"===e?"prompt":e}async function E(e,t=3e3){const n=await async function(){try{const e=navigator.permissions;if(!e?.query)return"unavailable";const t=(await e.query({name:"geolocation"})).state;return"granted"===t||"denied"===t||"prompt"===t?t:"unavailable"}catch{return"unavailable"}}(),o=function(e,t){switch(e){case"off":default:return"skip";case"silent":return"granted"===t?"read-if-granted":"skip";case"prompt":case"required":return"denied"===t?"skip":"may-prompt"}}(e,n);return"skip"===o?T(M(n)):await function(e,t){return new Promise(n=>{if("undefined"==typeof navigator||!navigator.geolocation)return n(T(M(t)));let o=!1;const l=e=>{o||(o=!0,n(e))},r=setTimeout(()=>l(T("timeout")),e);try{navigator.geolocation.getCurrentPosition(e=>{clearTimeout(r),l({latitude:e.coords.latitude??null,longitude:e.coords.longitude??null,accuracy_m:"number"==typeof e.coords.accuracy?e.coords.accuracy:null,permission_state:"granted"})},e=>{clearTimeout(r);const n=e&&1===e.code;l(T(n?"denied":M(t)))},{enableHighAccuracy:!1,timeout:e,maximumAge:6e4})}catch{clearTimeout(r),l(T(M(t)))}})}(t,n)}const P=[{region:"us",url:"stun:stun.l.google.com:19302"},{region:"eu",url:"stun:stun.cloudflare.com:3478"},{region:"global",url:"stun:global.stun.twilio.com:3478"}],I=/^(10\.|127\.|169\.254\.|192\.168\.|172\.(1[6-9]|2\d|3[0-1])\.|::1|fe80:|fc00:|fd[0-9a-f]{2}:)/i,D=/([0-9]{1,3}(?:\.[0-9]{1,3}){3})|([a-f0-9]{1,4}(?::[a-f0-9]{0,4}){2,7})/i;function R(e){return new Promise(t=>{const n=window.RTCPeerConnection;if(!n)return t(null);let o=null,l=P.length;const r=[];let a=!1;const i=()=>{if(!a){a=!0;for(const e of r)try{e.close()}catch{}t(o?.region??null)}},s=setTimeout(i,e);for(const e of P)try{const t=new n({iceServers:[{urls:e.url}]});r.push(t);const a="undefined"!=typeof performance&&performance.now?performance.now():Date.now();t.createDataChannel("rg"),t.onicecandidate=t=>{if(!t.candidate)return;if(!/typ srflx/.test(t.candidate.candidate||""))return;const n=("undefined"!=typeof performance&&performance.now?performance.now():Date.now())-a;(!o||n<o.ms)&&(o={region:e.region,ms:n}),--l<=0&&(clearTimeout(s),i())},t.createOffer().then(e=>t.setLocalDescription(e)).catch(()=>{--l<=0&&(clearTimeout(s),i())})}catch{--l<=0&&(clearTimeout(s),i())}})}async function U(e=2500){const t={effective_type:null,rtt:null,downlink:null,save_data:null,webrtc_local_ips:[],public_ip_hint:null,dns_resolver_region:null};Object.assign(t,function(){const e={effective_type:null,rtt:null,downlink:null,save_data:null};try{const t=navigator.connection;t&&(e.effective_type=t.effectiveType??null,e.rtt="number"==typeof t.rtt?t.rtt:null,e.downlink="number"==typeof t.downlink?t.downlink:null,e.save_data="boolean"==typeof t.saveData?t.saveData:null)}catch{}return e}());const n=Math.min(e,2e3),o=Math.min(e,1500),[l,r]=await Promise.all([(a=n,new Promise(e=>{const t={localIps:[],publicIpHint:null,region:null},n=window.RTCPeerConnection;if(!n)return e(t);const o=new Set;let l=null,r=!1;const i=()=>{if(!r){r=!0;try{l?.close()}catch{}t.localIps=Array.from(o),e(t)}},s=setTimeout(i,a);try{l=new n({iceServers:P.map(e=>({urls:e.url}))}),l.createDataChannel("gs"),l.onicecandidate=e=>{if(!e.candidate)return clearTimeout(s),i();const n=e.candidate.candidate||"",l=n.match(D),r=l?l[0]:null;r&&(/typ host/.test(n)?(I.test(r)||r.includes("."),o.add(r)):!/typ srflx/.test(n)&&!/typ prflx/.test(n)||I.test(r)||(t.publicIpHint=t.publicIpHint??r,t.region)||n.match(/raddr ([0-9a-f.:]+)/i))},l.createOffer().then(e=>l.setLocalDescription(e)).catch(()=>{clearTimeout(s),i()})}catch{clearTimeout(s),i()}})).catch(()=>({localIps:[],publicIpHint:null,region:null})),R(o).catch(()=>null)]);var a;return t.webrtc_local_ips=l.localIps,t.public_ip_hint=l.publicIpHint,t.dns_resolver_region=r,t}function H(){return{buf:new Float64Array(256),len:0,idx:0}}function O(e,t){e.buf[e.idx]=t,e.idx=(e.idx+1)%256,e.len<256&&e.len++}function N(e){if(0===e.len)return null;let t=0;for(let n=0;n<e.len;n++)t+=e.buf[n];return t/e.len}let F=null;const L=[];function j(){return"undefined"!=typeof performance&&performance.now?performance.now():Date.now()}function B(e,t,n,o){try{e.addEventListener(t,n,{passive:!0,capture:!0,...o}),L.push(()=>e.removeEventListener(t,n,{capture:!0}))}catch{}}function G(e,t,n,o,l){const r=[];if(l>=10&&null!=e&&r.push(e<.2?.9:e<.4?.5:.1),null!=t&&r.push(t<12?.85:t<25?.4:.1),null!=n&&r.push(n<8?.85:n<20?.4:.1),null!=o&&r.push(o>25?.9:o>15?.5:.1),0===r.length)return null;const a=r.reduce((e,t)=>e+t,0)/r.length;return Math.round(100*a)/100}function V(){if(!F)return{mouse_path_entropy:null,keystroke_dwell_avg_ms:null,keystroke_flight_avg_ms:null,scroll_cadence_ms:null,form_fill_speed_cps:null,copy_paste_used:null,tab_switches:null,session_duration_ms:null,risk_score:null};const e=function(e){if(e.len<8)return null;const t=new Array(16).fill(0),n=function(e){const t=[];for(let n=0;n<e.len;n++)t.push(e.buf[n]);return t}(e);for(const e of n){let n=Math.floor((e+Math.PI)/(2*Math.PI)*16);n<0&&(n=0),n>15&&(n=15),t[n]++}const o=n.length;let l=0;for(const e of t){if(0===e)continue;const t=e/o;l-=t*Math.log2(t)}return Math.min(1,l/4)}(F.angles),t=N(F.dwell),n=N(F.flight),o=N(F.scrollGaps);let l=null;if(F.formCharCount>0&&null!=F.formFirstInputAt&&null!=F.formLastInputAt&&F.formLastInputAt>F.formFirstInputAt){const e=(F.formLastInputAt-F.formFirstInputAt)/1e3;e>0&&(l=Math.round(F.formCharCount/e*100)/100)}return{mouse_path_entropy:e,keystroke_dwell_avg_ms:null!=t?Math.round(t):null,keystroke_flight_avg_ms:null!=n?Math.round(n):null,scroll_cadence_ms:null!=o?Math.round(o):null,form_fill_speed_cps:l,copy_paste_used:F.copyPasteUsed,tab_switches:F.tabSwitches,session_duration_ms:Math.round(j()-F.startedAt),risk_score:G(e,t,n,l,F.moveCount)}}const q="prompt",W=!0,K=!0,$=4e3,X=!1;let Z={},Y=null;function J(...e){Z.debug&&console.info("[gs]",...e)}async function Q(){const e=Z.persistence??K,t=Z.max_wait_time??$,n=Z.gps??q,o="prompt"===n||"required"===n?Math.max(t,15e3):Math.min(t,3e3),l=d(),[a,i,s,c,m]=await Promise.all([u(v(e),z(),t),u(b(),{is_webdriver:null,is_headless:null,is_emulator:null,function_tampered:null,ua_platform_mismatch:null,private_mode:null,automation_flags:[],screen_mirrored:null,remote_desktop_suspected:null},t),u(S(),{user_agent:null,browser_name:null,browser_version:null,version_age_years:null,version_age_bucket:null,privacy_extension_detected:null,is_bot:null,spoofing_detected:null,dynamic_component_blocked:null},t),u(U(Math.min(t,2500)),{effective_type:null,rtt:null,downlink:null,save_data:null,webrtc_local_ips:[],public_ip_hint:null,dns_resolver_region:null},t),u(E(n,o),{latitude:null,longitude:null,accuracy_m:null,permission_state:null},o+250)]);Y=m,a.spoofing_hash=await u(async function(e){const t=[e.is_webdriver,e.is_headless,e.is_emulator,e.function_tampered,e.ua_platform_mismatch,e.private_mode,e.automation_flags.join(","),e.screen_mirrored,e.remote_desktop_suspected].join("~");return await r(t)}(i),null,500);const p=a.__persistent_id_present??null,_=await u(k(p),{cookies_enabled:null,local_storage:null,session_storage:null,indexed_db:null,cache_api:null,quota_bytes:null,usage_bytes:null,persistent_id_present:null},t);return delete a.__persistent_id_present,J(`signals collected in ${Math.round(d()-l)}ms`),{device:a,browser:s,tamper:i,gps:m,network:c,storage:_,behavior:Z.behavior??W?V():{mouse_path_entropy:null,keystroke_dwell_avg_ms:null,keystroke_flight_avg_ms:null,scroll_cadence_ms:null,form_fill_speed_cps:null,copy_paste_used:null,tab_switches:null,session_duration_ms:null,risk_score:null},email:(f=null,{address:f})};var f}function z(){return{device_hash:null,true_device_id:s(),browser_hash:null,cookie_hash:null,canvas_hash:null,webgl_hash:null,webgl2_hash:null,webgl_image_hash:null,webgl_params_hash:null,audio_hash:null,font_list_hash:null,spoofing_hash:null,screen:{width:null,height:null,color_depth:null,pixel_ratio:null,refresh_rate:null,is_extended:null},touch_support:null,device_memory:null,hardware_concurrency:null,platform:null,vendor:null,timezone:null,timezone_offset:null,languages:null,do_not_track:null,gpc:null}}function ee(e){Z={...Z,...e},J("configured",Z),Z.behavior??W?function(){if(F?.started)return;if("undefined"==typeof document||"undefined"==typeof window)return;F={started:!0,startedAt:j(),lastPoint:null,angles:H(),moveCount:0,keyDownAt:new Map,dwell:H(),flight:H(),lastKeyUpAt:null,formCharCount:0,formFirstInputAt:null,formLastInputAt:null,lastScrollAt:null,scrollGaps:H(),copyPasteUsed:!1,tabSwitches:0};const e=F;B(document,"pointermove",t=>{const n=t,o=j(),l={x:n.clientX,y:n.clientY,t:o};e.moveCount++;const r=e.lastPoint;if(r){const t=l.x-r.x,n=l.y-r.y;if(0!==t||0!==n){const o=Math.atan2(n,t);O(e.angles,o)}}e.lastPoint=l}),B(document,"keydown",t=>{const n=t;if(n.repeat)return;const o=j();if(e.keyDownAt.has(n.keyCode)||e.keyDownAt.set(n.keyCode,o),null!=e.lastKeyUpAt){const t=o-e.lastKeyUpAt;t>=0&&t<5e3&&O(e.flight,t)}}),B(document,"keyup",t=>{const n=t,o=j(),l=e.keyDownAt.get(n.keyCode);if(null!=l){const t=o-l;t>=0&&t<5e3&&O(e.dwell,t),e.keyDownAt.delete(n.keyCode)}e.lastKeyUpAt=o}),B(document,"input",t=>{if(!function(e){const t=e;if(!t||!t.tagName)return!1;const n=t.tagName.toUpperCase();if("TEXTAREA"===n)return!0;if("INPUT"===n){const e=(t.getAttribute("type")||"text").toLowerCase();return!["checkbox","radio","button","submit","range","file","color"].includes(e)}return!0===t.isContentEditable}(t.target))return;const n=j();null==e.formFirstInputAt&&(e.formFirstInputAt=n),e.formLastInputAt=n;const o=t;"insertText"===o.inputType||"insertCompositionText"===o.inputType?e.formCharCount+=o.data?o.data.length:1:"insertFromPaste"===o.inputType&&(e.copyPasteUsed=!0)}),B(window,"scroll",()=>{const t=j();if(null!=e.lastScrollAt){const n=t-e.lastScrollAt;n>=0&&n<6e4&&O(e.scrollGaps,n)}e.lastScrollAt=t}),B(document,"paste",()=>{e.copyPasteUsed=!0}),B(document,"visibilitychange",()=>{"hidden"===document.visibilityState&&e.tabSwitches++})}():function(){for(;L.length;)try{L.pop()()}catch{}F=null}()}async function te(){const e=Date.now(),t=await Q();if("required"===(Z.gps??q)&&(null==t.gps.latitude||null==t.gps.longitude)){const e=new Error("GPS location is required but was not captured. Allow location permission and retry.");throw e.name="GpsRequiredError",e.code="GPS_REQUIRED",e.gps_permission_state=t.gps.permission_state,e}const n={v:1,sid:Z.session_id??s(),iat:e,exp:e+3e5,nonce:i(16),collected_at:e,signals:t};return Z.sandbox??X?(J("sandbox mode — returning unencrypted payload"),`gsds_sandbox.${btoa(JSON.stringify(n)).replace(/=+$/,"")}`):await o(n,"v1")}async function ne(){return(await u(v(Z.persistence??K),z(),3e3)).true_device_id??s()}function oe(){return Y?{...Y}:null}const le={config:ee,getSession:te,getDeviceId:ne,getLastGps:oe};"undefined"!=typeof window&&(window.gs=le);export{ee as config,ne as getDeviceId,oe as getLastGps,te as getSession,le as gs};
|
|
2
|
+
//# sourceMappingURL=gs.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gs.esm.js","sources":["../src/keys.ts","../src/crypto/jwe.ts","../src/util/sha256.ts","../src/util/async.ts","../src/persistence/store.ts","../src/collectors/device.ts","../src/collectors/tamper.ts","../src/collectors/browser.ts","../src/collectors/storage.ts","../src/collectors/gps.ts","../src/util/consent.ts","../src/collectors/network.ts","../src/collectors/behavior.ts","../src/index.ts"],"sourcesContent":["/**\n * Bundled public keys, indexed by `kid`. The SDK encrypts the device token's\n * content-encryption key (CEK) with one of these RSA-OAEP-256 public keys.\n * The matching private key lives only in the GS edge environment\n * (`DEVICE_TOKEN_PRIVATE_KEY_<KID>` secret) and is never shipped to the browser.\n *\n * During a key rotation (performed manually, on-demand), add the new `kid`\n * here while keeping the previous one so in-flight tokens still decrypt. The SDK\n * always encrypts with `ACTIVE_KID`. Full procedure: `docs/gs-sdk-key-rotation.md`.\n */\nexport interface PublicJwk {\n kty: \"RSA\";\n n: string;\n e: string;\n}\n\nexport const ACTIVE_KID = \"v1\";\n\nexport const PUBLIC_KEYS: Record<string, PublicJwk> = {\n v1: {\n kty: \"RSA\",\n n: \"iudSshKiZs1hF1f3KvvoOU15MtR4iVuHHLQ1dx3wvWUbZYfSvbkRrV7WJe3lH3xcMWyiC2WIi7O9Remwr3qWI50RWBVEKNr9uLBCZUmZyPKCnGA6o3-Lm-BYjqpT8LO5QtS0G2jljX3DnOBHz0WDG56oE2g1u2nby_QyIpK_VdNLRq2xDx13_uYtIvQ5hZOu4-_5UQrXdU3IugmOVu7-YOpKoDwb3DjJyJ98iBNfqEi-WCfzw9CyURpY9i19sj0GHFQwxD7JqT7VpKJCIbGS0FTk7ZZe9XMPpABdVe3eR0FoOaEB5i7SHgtznJnkdjPiPb896rQS6CmCvubZ-iHrUGL4fky5Q5SIgYYkXofQN3qayei59h5clBSfkVM69fFQiK0swNAHF2FBAH-ZEardlF897c1uf8W8KsAbMKk5jgy_bZosZgf85GdOsRs-8uCqgO_fXELHj5Eb9-UtueE2LwDrOCwTsK1Ib_QlINotoXsClTk1MQiRGaEvV_fPyLLG5Ytrw82GIubqz2cZunr8h-yxVUv7Hq51Vnp3hUA3__mVt4TWsiImIdiCo6S6TS5T2FqBOOsNUdYmYpoutq_5aMm4byx0TNVDGf_5lKPN3ldllikM5TJ4Wr3yKLg1Oj_pbrMvIeCwnRIJwWUwmgzW2l1LOctK1kpf2hGJRkUF7pE\",\n e: \"AQAB\",\n },\n};\n","import { ACTIVE_KID, PUBLIC_KEYS, type PublicJwk } from \"../keys.ts\";\n\n/** base64url encode bytes (no padding). */\nfunction b64url(buf: Uint8Array): string {\n let bin = \"\";\n for (let i = 0; i < buf.length; i++) bin += String.fromCharCode(buf[i]);\n return btoa(bin).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n\nconst enc = new TextEncoder();\n\nasync function importPublicKey(jwk: PublicJwk): Promise<CryptoKey> {\n return crypto.subtle.importKey(\n \"jwk\",\n { ...jwk, alg: \"RSA-OAEP-256\", ext: true },\n { name: \"RSA-OAEP\", hash: \"SHA-256\" },\n false,\n [\"encrypt\"],\n );\n}\n\n/**\n * Seal a JSON payload into a compact JWE:\n * BASE64URL(header).BASE64URL(encKey).BASE64URL(iv).BASE64URL(ciphertext).BASE64URL(tag)\n * - CEK: random AES-256-GCM key, wrapped with RSA-OAEP-256.\n * - AAD: the ASCII bytes of the encoded protected header (per RFC 7516).\n */\nexport async function sealJwe(payload: unknown, kid: string = ACTIVE_KID): Promise<string> {\n const jwk = PUBLIC_KEYS[kid];\n if (!jwk) throw new Error(`unknown kid: ${kid}`);\n\n const header = { alg: \"RSA-OAEP-256\", enc: \"A256GCM\", kid, typ: \"GSDS\" };\n const encodedHeader = b64url(enc.encode(JSON.stringify(header)));\n const aad = enc.encode(encodedHeader);\n\n // Content-encryption key\n const cek = await crypto.subtle.generateKey({ name: \"AES-GCM\", length: 256 }, true, [\"encrypt\"]);\n const rawCek = new Uint8Array(await crypto.subtle.exportKey(\"raw\", cek));\n\n // Wrap CEK with RSA-OAEP-256\n const pub = await importPublicKey(jwk);\n const wrapped = new Uint8Array(await crypto.subtle.encrypt({ name: \"RSA-OAEP\" }, pub, rawCek as BufferSource));\n\n // Encrypt the plaintext\n const iv = crypto.getRandomValues(new Uint8Array(12));\n const plaintext = enc.encode(JSON.stringify(payload));\n const ctTag = new Uint8Array(\n await crypto.subtle.encrypt(\n { name: \"AES-GCM\", iv, additionalData: aad, tagLength: 128 },\n cek,\n plaintext as BufferSource,\n ),\n );\n const ciphertext = ctTag.slice(0, ctTag.length - 16);\n const tag = ctTag.slice(ctTag.length - 16);\n\n return [encodedHeader, b64url(wrapped), b64url(iv), b64url(ciphertext), b64url(tag)].join(\".\");\n}\n","/** SHA-256 helpers (WebCrypto) returning hex digests. */\n\nconst enc = new TextEncoder();\n\nexport async function sha256Hex(input: string | Uint8Array): Promise<string> {\n const data = typeof input === \"string\" ? enc.encode(input) : input;\n const digest = await crypto.subtle.digest(\"SHA-256\", data as BufferSource);\n return bufToHex(new Uint8Array(digest));\n}\n\n/** Short, stable hash (first 16 hex chars) for compact identity fields. */\nexport async function sha256Short(input: string | Uint8Array, len = 32): Promise<string> {\n const full = await sha256Hex(input);\n return full.slice(0, len);\n}\n\nexport function bufToHex(buf: Uint8Array): string {\n let out = \"\";\n for (let i = 0; i < buf.length; i++) out += buf[i].toString(16).padStart(2, \"0\");\n return out;\n}\n\nexport function randomHex(bytes: number): string {\n const b = new Uint8Array(bytes);\n crypto.getRandomValues(b);\n return bufToHex(b);\n}\n\nexport function uuid(): string {\n if (typeof crypto.randomUUID === \"function\") return crypto.randomUUID();\n // RFC4122 v4 fallback\n const b = new Uint8Array(16);\n crypto.getRandomValues(b);\n b[6] = (b[6] & 0x0f) | 0x40;\n b[8] = (b[8] & 0x3f) | 0x80;\n const h = bufToHex(b);\n return `${h.slice(0, 8)}-${h.slice(8, 12)}-${h.slice(12, 16)}-${h.slice(16, 20)}-${h.slice(20)}`;\n}\n","/** Small async helpers used by collectors. */\n\n/** Resolve a promise but never reject — returns `fallback` on error/timeout. */\nexport async function safe<T>(p: Promise<T> | T, fallback: T, timeoutMs = 0): Promise<T> {\n try {\n if (timeoutMs > 0) {\n return await Promise.race([\n Promise.resolve(p),\n new Promise<T>((resolve) => setTimeout(() => resolve(fallback), timeoutMs)),\n ]);\n }\n return await Promise.resolve(p);\n } catch {\n return fallback;\n }\n}\n\n/** Run a synchronous collector, swallowing any error. */\nexport function tryCatch<T>(fn: () => T, fallback: T): T {\n try {\n return fn();\n } catch {\n return fallback;\n }\n}\n\nexport const now = (): number =>\n typeof performance !== \"undefined\" && performance.now ? performance.now() : Date.now();\n","/**\n * Triple-store persistence for the long-lived `true_device_id`.\n *\n * SEON-parity: a single device should keep a stable identifier even when one\n * storage layer is cleared. We mirror the id across three independent layers\n * and reconcile on read:\n * 1. localStorage (fast, survives reloads)\n * 2. IndexedDB (survives some \"clear cookies\" flows)\n * 3. first-party cookie (survives localStorage clears; long max-age)\n *\n * On read we take the first id we find, then re-seed every empty layer so the\n * id \"heals\" across partial clears. If nothing exists, we mint a new one.\n *\n * Privacy: callers pass `persistence:false` (GDPR mode) to skip all of this and\n * fall back to an ephemeral, per-session id.\n */\n\nimport { uuid } from \"../util/sha256.ts\";\n\nconst KEY = \"gs_tdid\";\nconst COOKIE_MAX_AGE = 60 * 60 * 24 * 400; // ~400d, browser cap\nconst DB_NAME = \"gs_web_sdk\";\nconst STORE_NAME = \"kv\";\n\nfunction readLocalStorage(): string | null {\n try {\n return window.localStorage.getItem(KEY);\n } catch {\n return null;\n }\n}\n\nfunction writeLocalStorage(id: string): void {\n try {\n window.localStorage.setItem(KEY, id);\n } catch {\n /* private mode / disabled */\n }\n}\n\nfunction readCookie(): string | null {\n try {\n const match = document.cookie.match(\n new RegExp(\"(?:^|; )\" + KEY + \"=([^;]*)\"),\n );\n return match ? decodeURIComponent(match[1]) : null;\n } catch {\n return null;\n }\n}\n\nfunction writeCookie(id: string): void {\n try {\n const secure = location.protocol === \"https:\" ? \"; Secure\" : \"\";\n document.cookie =\n `${KEY}=${encodeURIComponent(id)}; Max-Age=${COOKIE_MAX_AGE}; Path=/; SameSite=Lax${secure}`;\n } catch {\n /* cookies disabled */\n }\n}\n\nfunction openDb(): Promise<IDBDatabase | null> {\n return new Promise((resolve) => {\n try {\n if (typeof indexedDB === \"undefined\") return resolve(null);\n const req = indexedDB.open(DB_NAME, 1);\n req.onupgradeneeded = () => {\n try {\n req.result.createObjectStore(STORE_NAME);\n } catch {\n /* already exists */\n }\n };\n req.onsuccess = () => resolve(req.result);\n req.onerror = () => resolve(null);\n // Safari can hang on private mode — guard with a timeout.\n setTimeout(() => resolve(null), 600);\n } catch {\n resolve(null);\n }\n });\n}\n\nasync function readIndexedDb(): Promise<string | null> {\n const db = await openDb();\n if (!db) return null;\n return new Promise((resolve) => {\n try {\n const tx = db.transaction(STORE_NAME, \"readonly\");\n const req = tx.objectStore(STORE_NAME).get(KEY);\n req.onsuccess = () => resolve(typeof req.result === \"string\" ? req.result : null);\n req.onerror = () => resolve(null);\n } catch {\n resolve(null);\n }\n });\n}\n\nasync function writeIndexedDb(id: string): Promise<void> {\n const db = await openDb();\n if (!db) return;\n await new Promise<void>((resolve) => {\n try {\n const tx = db.transaction(STORE_NAME, \"readwrite\");\n tx.objectStore(STORE_NAME).put(id, KEY);\n tx.oncomplete = () => resolve();\n tx.onerror = () => resolve();\n } catch {\n resolve();\n }\n });\n}\n\nexport interface PersistentIdResult {\n id: string;\n present_before: boolean;\n}\n\n/**\n * Resolve the stable `true_device_id`, healing any empty storage layer.\n * When `persist` is false, returns a fresh ephemeral id without writing.\n */\nexport async function resolveTrueDeviceId(persist: boolean): Promise<PersistentIdResult> {\n if (!persist) {\n return { id: uuid(), present_before: false };\n }\n\n const ls = readLocalStorage();\n const ck = readCookie();\n const idb = await readIndexedDb();\n\n const existing = ls || ck || idb || null;\n const id = existing ?? uuid();\n\n // Re-seed every layer that is missing or stale.\n if (ls !== id) writeLocalStorage(id);\n if (ck !== id) writeCookie(id);\n if (idb !== id) await writeIndexedDb(id);\n\n return { id, present_before: existing !== null };\n}\n","/**\n * Device identity + fingerprint collector (P2).\n *\n * Produces the stable hashes the GS engine keys on:\n * - true_device_id : long-lived, triple-stored id (see persistence/store.ts)\n * - device_hash : composite of stable hardware/render fingerprints\n * - browser_hash : composite of browser-volatile attributes\n * - cookie_hash : hash bound to the persistent id (cookie/storage layer)\n * plus the individual canvas/webgl/audio/font sub-hashes for granular scoring.\n *\n * Every probe is wrapped so a single failure (private mode, blocked API,\n * locked-down browser) degrades to `null` instead of throwing.\n */\n\nimport type { DeviceSignals } from \"../types.ts\";\nimport { sha256Short } from \"../util/sha256.ts\";\nimport { safe, tryCatch } from \"../util/async.ts\";\nimport { resolveTrueDeviceId } from \"../persistence/store.ts\";\n\n/* ---------------------------- canvas ---------------------------- */\nfunction canvasFingerprint(): string | null {\n return tryCatch(() => {\n const canvas = document.createElement(\"canvas\");\n canvas.width = 280;\n canvas.height = 60;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return null;\n ctx.textBaseline = \"top\";\n ctx.font = \"14px 'Arial'\";\n ctx.fillStyle = \"#f60\";\n ctx.fillRect(125, 1, 62, 20);\n ctx.fillStyle = \"#069\";\n ctx.fillText(\"GS\\u{1F512} fp.io 0123\", 2, 15);\n ctx.fillStyle = \"rgba(102, 204, 0, 0.7)\";\n ctx.fillText(\"GS\\u{1F512} fp.io 0123\", 4, 17);\n ctx.globalCompositeOperation = \"multiply\";\n ctx.fillStyle = \"rgb(255,0,255)\";\n ctx.beginPath();\n ctx.arc(50, 30, 20, 0, Math.PI * 2, true);\n ctx.fill();\n return canvas.toDataURL();\n }, null);\n}\n\n/* ---------------------------- webgl ---------------------------- */\ninterface WebglParts {\n imageHash: string | null;\n paramsHash: string | null;\n vendor: string | null;\n renderer: string | null;\n}\n\nfunction webglParts(version: \"webgl\" | \"webgl2\"): WebglParts {\n return tryCatch<WebglParts>(() => {\n const canvas = document.createElement(\"canvas\");\n const gl = canvas.getContext(version) as WebGLRenderingContext | null;\n if (!gl) return { imageHash: null, paramsHash: null, vendor: null, renderer: null };\n\n // Renderer + vendor + a curated set of stable params.\n const dbg = gl.getExtension(\"WEBGL_debug_renderer_info\");\n const params: string[] = [];\n const push = (label: string, v: unknown) => params.push(`${label}=${String(v)}`);\n let vendorStr: string | null = null;\n let rendererStr: string | null = null;\n if (dbg) {\n vendorStr = String(gl.getParameter((dbg as { UNMASKED_VENDOR_WEBGL: number }).UNMASKED_VENDOR_WEBGL) ?? \"\") || null;\n rendererStr = String(gl.getParameter((dbg as { UNMASKED_RENDERER_WEBGL: number }).UNMASKED_RENDERER_WEBGL) ?? \"\") || null;\n push(\"vendor\", vendorStr);\n push(\"renderer\", rendererStr);\n }\n const numeric = [\n gl.MAX_TEXTURE_SIZE, gl.MAX_RENDERBUFFER_SIZE, gl.MAX_VERTEX_ATTRIBS,\n gl.MAX_VARYING_VECTORS, gl.MAX_VERTEX_UNIFORM_VECTORS,\n gl.MAX_FRAGMENT_UNIFORM_VECTORS, gl.MAX_TEXTURE_IMAGE_UNITS,\n gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS, gl.ALIASED_LINE_WIDTH_RANGE,\n gl.ALIASED_POINT_SIZE_RANGE, gl.MAX_VIEWPORT_DIMS,\n ];\n for (const p of numeric) push(String(p), gl.getParameter(p));\n const exts = gl.getSupportedExtensions();\n if (exts) push(\"exts\", exts.slice().sort().join(\",\"));\n\n // Render a small scene and read pixels for an image hash.\n let imageData: string | null = null;\n try {\n const buf = gl.createBuffer();\n gl.bindBuffer(gl.ARRAY_BUFFER, buf);\n const verts = new Float32Array([-0.2, -0.9, 0, 0.4, -0.26, 0, 0, 0.7, 0]);\n gl.bufferData(gl.ARRAY_BUFFER, verts, gl.STATIC_DRAW);\n const pixels = new Uint8Array(canvas.width * canvas.height * 4);\n gl.readPixels(0, 0, canvas.width, canvas.height, gl.RGBA, gl.UNSIGNED_BYTE, pixels);\n imageData = Array.from(pixels.slice(0, 256)).join(\",\");\n } catch {\n imageData = null;\n }\n\n return {\n imageHash: imageData,\n paramsHash: params.join(\"|\") || null,\n vendor: vendorStr,\n renderer: rendererStr,\n };\n }, { imageHash: null, paramsHash: null, vendor: null, renderer: null });\n}\n\n/* ---------------------- plugins / mime / math / system colors ---------------------- */\nfunction collectPlugins(): string[] | null {\n return tryCatch<string[] | null>(() => {\n const list = navigator.plugins;\n if (!list || list.length === 0) return [];\n const names: string[] = [];\n for (let i = 0; i < list.length; i++) {\n const name = list[i]?.name;\n if (name) names.push(name);\n }\n return names;\n }, null);\n}\n\nfunction mimeTypesSeed(): string | null {\n return tryCatch<string | null>(() => {\n const mt = navigator.mimeTypes;\n if (!mt || mt.length === 0) return \"\";\n const types: string[] = [];\n for (let i = 0; i < mt.length; i++) {\n const t = mt[i]?.type;\n if (t) types.push(t);\n }\n return types.sort().join(\",\");\n }, null);\n}\n\n/** Deterministic Math-implementation fingerprint (varies subtly across engines). */\nfunction mathSeed(): string | null {\n return tryCatch<string | null>(() => {\n const ops: number[] = [\n Math.acos(0.123456789), Math.acosh(1.0000001), Math.asin(0.123456789),\n Math.asinh(0.123456789), Math.atan(0.123456789), Math.atanh(0.123456789),\n Math.cbrt(100), Math.cos(1e10), Math.cosh(10), Math.exp(10),\n Math.expm1(1), Math.log1p(10), Math.sin(1e10), Math.sinh(10),\n Math.tan(1e10), Math.tanh(0.123456789), Math.pow(Math.PI, -100),\n ];\n return ops.map((n) => n.toString()).join(\",\");\n }, null);\n}\n\n/** CSS system-color resolution fingerprint (theme/OS dependent). */\nfunction systemColorsSeed(): string | null {\n return tryCatch<string | null>(() => {\n const keywords = [\n \"ActiveText\", \"ButtonBorder\", \"ButtonFace\", \"ButtonText\", \"Canvas\",\n \"CanvasText\", \"Field\", \"FieldText\", \"GrayText\", \"Highlight\",\n \"HighlightText\", \"LinkText\", \"Mark\", \"MarkText\", \"VisitedText\",\n ];\n const el = document.createElement(\"div\");\n el.style.position = \"absolute\";\n el.style.left = \"-9999px\";\n document.body.appendChild(el);\n const out: string[] = [];\n for (const kw of keywords) {\n el.style.color = kw;\n out.push(`${kw}=${getComputedStyle(el).color}`);\n }\n document.body.removeChild(el);\n return out.join(\"|\");\n }, null);\n}\n\n/* ---------------------- UA Client Hints / media / locale ---------------------- */\nasync function collectUserAgentData(): Promise<DeviceSignals[\"user_agent_data\"]> {\n try {\n const uaData = (navigator as unknown as {\n userAgentData?: {\n mobile?: boolean;\n platform?: string;\n getHighEntropyValues?: (hints: string[]) => Promise<Record<string, unknown>>;\n };\n }).userAgentData;\n if (!uaData) return null;\n let high: Record<string, unknown> = {};\n if (typeof uaData.getHighEntropyValues === \"function\") {\n high = await uaData.getHighEntropyValues([\n \"architecture\", \"bitness\", \"model\", \"platformVersion\", \"uaFullVersion\",\n ]);\n }\n const str = (v: unknown): string | null => (typeof v === \"string\" && v.length > 0 ? v : null);\n return {\n platform: str(high.platform) ?? str(uaData.platform),\n platform_version: str(high.platformVersion),\n architecture: str(high.architecture),\n bitness: str(high.bitness),\n mobile: typeof uaData.mobile === \"boolean\" ? uaData.mobile : null,\n model: str(high.model),\n ua_full_version: str(high.uaFullVersion),\n };\n } catch {\n return null;\n }\n}\n\nasync function collectMediaDevices(): Promise<DeviceSignals[\"media_devices\"]> {\n try {\n const md = navigator.mediaDevices;\n if (!md || typeof md.enumerateDevices !== \"function\") return null;\n const devices = await md.enumerateDevices();\n const counts = { audio_input: 0, audio_output: 0, video_input: 0 };\n for (const d of devices) {\n if (d.kind === \"audioinput\") counts.audio_input++;\n else if (d.kind === \"audiooutput\") counts.audio_output++;\n else if (d.kind === \"videoinput\") counts.video_input++;\n }\n return counts;\n } catch {\n return null;\n }\n}\n\n/* ---------------------------- audio ---------------------------- */\nfunction audioFingerprint(): Promise<string | null> {\n return new Promise((resolve) => {\n try {\n const Ctx = (window as unknown as { OfflineAudioContext?: typeof OfflineAudioContext; webkitOfflineAudioContext?: typeof OfflineAudioContext })\n .OfflineAudioContext ||\n (window as unknown as { webkitOfflineAudioContext?: typeof OfflineAudioContext }).webkitOfflineAudioContext;\n if (!Ctx) return resolve(null);\n const ctx = new Ctx(1, 5000, 44100);\n const osc = ctx.createOscillator();\n osc.type = \"triangle\";\n osc.frequency.value = 10000;\n const comp = ctx.createDynamicsCompressor();\n comp.threshold.value = -50;\n comp.knee.value = 40;\n comp.ratio.value = 12;\n comp.attack.value = 0;\n comp.release.value = 0.25;\n osc.connect(comp);\n comp.connect(ctx.destination);\n osc.start(0);\n ctx.startRendering();\n const timer = setTimeout(() => resolve(null), 800);\n ctx.oncomplete = (e) => {\n clearTimeout(timer);\n try {\n const data = e.renderedBuffer.getChannelData(0).slice(4500, 4600);\n let sum = 0;\n for (let i = 0; i < data.length; i++) sum += Math.abs(data[i]);\n resolve(sum.toString());\n } catch {\n resolve(null);\n }\n };\n } catch {\n resolve(null);\n }\n });\n}\n\n/* ---------------------------- fonts ---------------------------- */\nconst FONT_PROBE = [\n \"Arial\", \"Arial Black\", \"Arial Narrow\", \"Calibri\", \"Cambria\", \"Comic Sans MS\",\n \"Consolas\", \"Courier New\", \"Georgia\", \"Helvetica\", \"Impact\", \"Lucida Console\",\n \"Palatino Linotype\", \"Segoe UI\", \"Tahoma\", \"Times New Roman\", \"Trebuchet MS\",\n \"Verdana\", \"Menlo\", \"Monaco\", \"Roboto\", \"Ubuntu\", \"Cantarell\", \"Noto Sans\",\n];\n\nfunction detectFonts(): string[] | null {\n return tryCatch<string[] | null>(() => {\n const base = [\"monospace\", \"sans-serif\", \"serif\"];\n const testString = \"mmmmmmmmmmlli\";\n const testSize = \"72px\";\n const span = document.createElement(\"span\");\n span.style.position = \"absolute\";\n span.style.left = \"-9999px\";\n span.style.fontSize = testSize;\n span.textContent = testString;\n document.body.appendChild(span);\n\n const baseline: Record<string, { w: number; h: number }> = {};\n for (const b of base) {\n span.style.fontFamily = b;\n baseline[b] = { w: span.offsetWidth, h: span.offsetHeight };\n }\n\n const available: string[] = [];\n for (const font of FONT_PROBE) {\n let detected = false;\n for (const b of base) {\n span.style.fontFamily = `'${font}',${b}`;\n if (span.offsetWidth !== baseline[b].w || span.offsetHeight !== baseline[b].h) {\n detected = true;\n break;\n }\n }\n if (detected) available.push(font);\n }\n document.body.removeChild(span);\n return available;\n }, null);\n}\n\n/* ---------------------------- screen ---------------------------- */\nfunction collectScreen(): DeviceSignals[\"screen\"] {\n return tryCatch<DeviceSignals[\"screen\"]>(() => {\n const orientation = (screen as unknown as {\n orientation?: { type?: string; angle?: number };\n }).orientation;\n return {\n width: screen.width ?? null,\n height: screen.height ?? null,\n color_depth: screen.colorDepth ?? null,\n pixel_ratio: window.devicePixelRatio ?? null,\n refresh_rate: null, // populated by tamper.ts (rAF sampling) when available\n is_extended: (screen as unknown as { isExtended?: boolean }).isExtended ?? null,\n available_width: screen.availWidth ?? null,\n available_height: screen.availHeight ?? null,\n pixel_depth: screen.pixelDepth ?? null,\n orientation_type: orientation?.type ?? null,\n orientation_angle: typeof orientation?.angle === \"number\" ? orientation.angle : null,\n };\n }, {\n width: null, height: null, color_depth: null, pixel_ratio: null,\n refresh_rate: null, is_extended: null,\n available_width: null, available_height: null, pixel_depth: null,\n orientation_type: null, orientation_angle: null,\n });\n}\n\n/* ---------------------------- orchestrator ---------------------------- */\nexport async function collectDevice(persist: boolean): Promise<DeviceSignals> {\n const nav = navigator;\n\n const [canvas, audio] = await Promise.all([\n Promise.resolve(canvasFingerprint()),\n safe(audioFingerprint(), null, 1000),\n ]);\n const wgl1 = webglParts(\"webgl\");\n const wgl2 = webglParts(\"webgl2\");\n const fonts = detectFonts();\n\n const screen_ = collectScreen();\n\n // Enriched safe-subset seeds + async probes (all fail open to null).\n const pluginList = collectPlugins();\n const mimeSeed = mimeTypesSeed();\n const mathS = mathSeed();\n const sysColorsSeed = systemColorsSeed();\n const locale = tryCatch(() => Intl.DateTimeFormat().resolvedOptions().locale || null, null);\n const [userAgentData, mediaDevices] = await Promise.all([\n safe(collectUserAgentData(), null, 800),\n safe(collectMediaDevices(), null, 800),\n ]);\n\n const platform = tryCatch(() => nav.platform || null, null);\n // Page context (drives device.window_location / device.window_hostname rules).\n const windowLocation = tryCatch(() => window.location?.href || null, null);\n const windowHostname = tryCatch(() => window.location?.hostname || null, null);\n const vendor = tryCatch(() => (nav as unknown as { vendor?: string }).vendor || null, null);\n const timezone = tryCatch(() => Intl.DateTimeFormat().resolvedOptions().timeZone || null, null);\n const tzOffset = tryCatch(() => new Date().getTimezoneOffset(), null);\n const languages = tryCatch(() => (nav.languages ? Array.from(nav.languages) : nav.language ? [nav.language] : null), null);\n const deviceMemory = tryCatch(() => (nav as unknown as { deviceMemory?: number }).deviceMemory ?? null, null);\n const concurrency = tryCatch(() => nav.hardwareConcurrency ?? null, null);\n const touch = tryCatch(() => \"ontouchstart\" in window || nav.maxTouchPoints > 0, null);\n const dnt = tryCatch(() => {\n const v = (nav as unknown as { doNotTrack?: string }).doNotTrack ??\n (window as unknown as { doNotTrack?: string }).doNotTrack;\n return v === \"1\" || v === \"yes\" ? true : v === \"0\" || v === \"no\" ? false : null;\n }, null);\n const gpc = tryCatch(() => (nav as unknown as { globalPrivacyControl?: boolean }).globalPrivacyControl ?? null, null);\n\n // Persistent id (triple-store).\n const { id: trueDeviceId, present_before } = await resolveTrueDeviceId(persist);\n\n // Individual sub-hashes.\n const [\n canvasHash, webglImageHash, webglParamsHash, webgl2ParamsHash,\n audioHash, fontHash, pluginHash, mimeTypesHash, mathHash, systemColorsHash,\n ] = await Promise.all([\n canvas ? sha256Short(canvas) : Promise.resolve(null),\n wgl1.imageHash ? sha256Short(wgl1.imageHash) : Promise.resolve(null),\n wgl1.paramsHash ? sha256Short(wgl1.paramsHash) : Promise.resolve(null),\n wgl2.paramsHash ? sha256Short(wgl2.paramsHash) : Promise.resolve(null),\n audio ? sha256Short(audio) : Promise.resolve(null),\n fonts && fonts.length ? sha256Short(fonts.join(\",\")) : Promise.resolve(null),\n pluginList && pluginList.length ? sha256Short(pluginList.join(\",\")) : Promise.resolve(null),\n mimeSeed ? sha256Short(mimeSeed) : Promise.resolve(null),\n mathS ? sha256Short(mathS) : Promise.resolve(null),\n sysColorsSeed ? sha256Short(sysColorsSeed) : Promise.resolve(null),\n ]);\n\n // Stable composite: hardware + render layers (survives most browser changes).\n const deviceSeed = [\n platform, vendor, deviceMemory, concurrency, touch,\n screen_.width, screen_.height, screen_.color_depth, screen_.pixel_ratio,\n canvasHash, webglImageHash, webglParamsHash, webgl2ParamsHash, audioHash, fontHash,\n ].join(\"~\");\n const deviceHash = await sha256Short(deviceSeed);\n\n // Browser-volatile composite (UA-ish layer).\n const browserSeed = [\n tryCatch(() => nav.userAgent, \"\"), languages?.join(\",\") ?? \"\",\n timezone, tzOffset, platform, vendor,\n ].join(\"~\");\n const browserHash = await sha256Short(browserSeed);\n\n // Cookie/storage-bound hash (combines persistent id with the device layer).\n const cookieHash = await sha256Short(`${trueDeviceId}~${deviceHash}`);\n\n return {\n device_hash: deviceHash,\n true_device_id: trueDeviceId,\n browser_hash: browserHash,\n cookie_hash: cookieHash,\n canvas_hash: canvasHash,\n webgl_hash: webglParamsHash,\n webgl2_hash: webgl2ParamsHash,\n webgl_image_hash: webglImageHash,\n webgl_params_hash: webglParamsHash,\n audio_hash: audioHash,\n font_list_hash: fontHash,\n spoofing_hash: null, // filled by tamper.ts merge\n screen: screen_,\n touch_support: touch,\n device_memory: deviceMemory,\n hardware_concurrency: concurrency,\n platform,\n vendor,\n timezone,\n timezone_offset: tzOffset,\n languages,\n do_not_track: dnt,\n gpc,\n // enriched safe-subset signals\n font_list: fonts,\n plugin_list: pluginList,\n plugin_count: pluginList ? pluginList.length : null,\n plugin_hash: pluginHash,\n mime_types_hash: mimeTypesHash,\n math_hash: mathHash,\n system_colors_hash: systemColorsHash,\n webgl_vendor: wgl1.vendor,\n webgl_renderer: wgl1.renderer,\n locale,\n timezone_country: null,\n user_agent_data: userAgentData,\n media_devices: mediaDevices,\n window_location: windowLocation,\n window_hostname: windowHostname,\n // non-typed extras consumed by the orchestrator before sealing\n ...( { __persistent_id_present: present_before } as Record<string, unknown> ),\n } as DeviceSignals;\n}\n","/**\n * Tamper / anti-spoof collector (P2).\n *\n * Heuristic detection of automation, headless runtimes, emulators, function\n * tampering, UA/platform mismatch, private mode, screen mirroring and remote\n * desktop. Each check is best-effort and degrades to `null`/`false` rather\n * than throwing. Results feed the server-side scoring engine — they are signals,\n * not verdicts.\n */\n\nimport type { TamperSignals } from \"../types.ts\";\nimport { sha256Short } from \"../util/sha256.ts\";\nimport { tryCatch } from \"../util/async.ts\";\n\n/* webdriver / automation flags */\nfunction detectWebdriver(): boolean {\n return tryCatch(() => navigator.webdriver === true, false);\n}\n\nfunction automationFlags(): string[] {\n const flags: string[] = [];\n const w = window as unknown as Record<string, unknown>;\n const probes: Array<[string, () => boolean]> = [\n [\"webdriver\", () => navigator.webdriver === true],\n [\"cdc_props\", () => Object.keys(w).some((k) => /^[$_]?cdc_/.test(k) || /\\$cdc_/.test(k))],\n [\"__webdriver_evaluate\", () => \"__webdriver_evaluate\" in w || \"__driver_evaluate\" in w],\n [\"__selenium\", () => \"__selenium_unwrapped\" in w || \"__webdriver_script_fn\" in w],\n [\"__nightmare\", () => \"__nightmare\" in w],\n [\"_phantom\", () => \"_phantom\" in w || \"callPhantom\" in w],\n [\"domAutomation\", () => \"domAutomation\" in w || \"domAutomationController\" in w],\n [\"puppeteer\", () => \"__puppeteer_evaluation_script__\" in w],\n [\"playwright\", () => \"__playwright\" in w || \"__pw_manual\" in w],\n ];\n for (const [name, fn] of probes) {\n if (tryCatch(fn, false)) flags.push(name);\n }\n return flags;\n}\n\n/* headless heuristics */\nfunction detectHeadless(flags: string[]): boolean {\n return tryCatch(() => {\n if (flags.length > 0) return true;\n const ua = navigator.userAgent || \"\";\n if (/HeadlessChrome|PhantomJS|Electron/i.test(ua)) return true;\n // Headless Chrome historically reports 0 plugins + missing languages.\n const noPlugins = (navigator.plugins?.length ?? 0) === 0;\n const noLanguages = !navigator.languages || navigator.languages.length === 0;\n // Permissions API quirk: notifications \"denied\" while Notification \"default\".\n return noPlugins && noLanguages;\n }, false);\n}\n\n/* emulator / touch-UA mismatch */\nfunction detectEmulator(): boolean {\n return tryCatch(() => {\n const ua = navigator.userAgent || \"\";\n const mobileUa = /Mobi|Android|iPhone|iPad/i.test(ua);\n const hasTouch = \"ontouchstart\" in window || navigator.maxTouchPoints > 0;\n // A \"mobile\" UA on a device reporting no touch + desktop concurrency.\n if (mobileUa && !hasTouch) return true;\n return false;\n }, false);\n}\n\n/* native function tampering (toString patched) */\nfunction detectFunctionTampering(): boolean {\n return tryCatch(() => {\n const natives = [\n Function.prototype.bind,\n navigator.permissions?.query,\n HTMLCanvasElement.prototype.toDataURL,\n WebGLRenderingContext.prototype.getParameter,\n ].filter(Boolean) as Array<(...a: unknown[]) => unknown>;\n for (const fn of natives) {\n const s = Function.prototype.toString.call(fn);\n if (!/\\{\\s*\\[native code\\]\\s*\\}/.test(s)) return true;\n }\n // toString itself patched?\n const ts = Function.prototype.toString.toString();\n if (!/\\{\\s*\\[native code\\]\\s*\\}/.test(ts)) return true;\n return false;\n }, false);\n}\n\n/* UA-platform mismatch (navigator.platform vs UA family) */\nfunction detectUaPlatformMismatch(): boolean {\n return tryCatch(() => {\n const ua = (navigator.userAgent || \"\").toLowerCase();\n const plat = (navigator.platform || \"\").toLowerCase();\n if (!plat) return false;\n const uaWin = ua.includes(\"windows\");\n const uaMac = ua.includes(\"mac os\") || ua.includes(\"macintosh\");\n const uaLinux = ua.includes(\"linux\") && !ua.includes(\"android\");\n const platWin = plat.includes(\"win\");\n const platMac = plat.includes(\"mac\");\n const platLinux = plat.includes(\"linux\") || plat.includes(\"x11\");\n if (uaWin && !platWin) return true;\n if (uaMac && !platMac) return true;\n if (uaLinux && !platLinux) return true;\n return false;\n }, false);\n}\n\n/* private / incognito (best-effort, storage-quota based) */\nasync function detectPrivateMode(): Promise<boolean | null> {\n return await new Promise<boolean | null>((resolve) => {\n try {\n const est = (navigator as unknown as { storage?: { estimate?: () => Promise<{ quota?: number }> } }).storage;\n if (est?.estimate) {\n est.estimate().then((e) => {\n // Incognito Chromium caps quota well under normal (~120MB) values.\n if (typeof e.quota === \"number\") resolve(e.quota < 120_000_000);\n else resolve(null);\n }).catch(() => resolve(null));\n setTimeout(() => resolve(null), 500);\n return;\n }\n resolve(null);\n } catch {\n resolve(null);\n }\n });\n}\n\n/* screen mirroring / remote desktop heuristics */\nfunction detectScreenMirrored(): boolean | null {\n return tryCatch<boolean | null>(() => {\n // Mismatched avail vs total or zero color depth often appears under\n // mirroring / RDP virtual displays.\n if (screen.colorDepth && screen.colorDepth <= 16) return true;\n if (screen.availWidth > screen.width || screen.availHeight > screen.height) return true;\n return false;\n }, null);\n}\n\nfunction detectRemoteDesktop(): boolean | null {\n return tryCatch<boolean | null>(() => {\n // RDP/virtual GPUs commonly report a 0/odd pixel ratio with low depth.\n const dpr = window.devicePixelRatio;\n if ((dpr === 0 || !dpr) && screen.colorDepth <= 16) return true;\n return false;\n }, null);\n}\n\nexport async function collectTamper(): Promise<TamperSignals> {\n const flags = automationFlags();\n const privateMode = await detectPrivateMode();\n\n const isWebdriver = detectWebdriver();\n const isHeadless = detectHeadless(flags);\n const isEmulator = detectEmulator();\n const functionTampered = detectFunctionTampering();\n const uaMismatch = detectUaPlatformMismatch();\n const screenMirrored = detectScreenMirrored();\n const remoteDesktop = detectRemoteDesktop();\n\n return {\n is_webdriver: isWebdriver,\n is_headless: isHeadless,\n is_emulator: isEmulator,\n function_tampered: functionTampered,\n ua_platform_mismatch: uaMismatch,\n private_mode: privateMode,\n automation_flags: flags,\n screen_mirrored: screenMirrored,\n remote_desktop_suspected: remoteDesktop,\n };\n}\n\n/** Stable hash summarising the tamper posture for the device's spoofing_hash. */\nexport async function spoofingHash(t: TamperSignals): Promise<string> {\n const seed = [\n t.is_webdriver, t.is_headless, t.is_emulator, t.function_tampered,\n t.ua_platform_mismatch, t.private_mode, t.automation_flags.join(\",\"),\n t.screen_mirrored, t.remote_desktop_suspected,\n ].join(\"~\");\n return await sha256Short(seed);\n}\n","/**\n * Browser profile collector (P3).\n *\n * Parses the UA / UA-Client-Hints into a normalised browser identity, estimates\n * the release age of the running version (stale browsers correlate with bots and\n * managed fraud farms), and runs a privacy-extension \"bait\" probe plus light\n * bot/spoof heuristics.\n *\n * All probes are best-effort and degrade to `null`/`false`.\n */\n\nimport type { BrowserSignals } from \"../types.ts\";\nimport { tryCatch } from \"../util/async.ts\";\n\nconst YEAR_MS = 365.25 * 24 * 60 * 60 * 1000;\n\n/* ---- engine release anchors (approx) + cadence per major version ---- */\ninterface Anchor {\n major: number;\n date: number; // ms epoch of that major's stable release\n cadenceMs: number; // approx time between major versions\n}\n\nconst ANCHORS: Record<string, Anchor> = {\n // Chromium 4-week cadence since 2021.\n chrome: { major: 116, date: Date.UTC(2023, 7, 15), cadenceMs: 28 * 24 * 60 * 60 * 1000 },\n edge: { major: 116, date: Date.UTC(2023, 7, 17), cadenceMs: 28 * 24 * 60 * 60 * 1000 },\n // Firefox ~4-week cadence.\n firefox: { major: 116, date: Date.UTC(2023, 7, 1), cadenceMs: 28 * 24 * 60 * 60 * 1000 },\n // Safari ships roughly yearly majors.\n safari: { major: 16, date: Date.UTC(2022, 8, 12), cadenceMs: Math.round(YEAR_MS) },\n opera: { major: 102, date: Date.UTC(2023, 7, 22), cadenceMs: 28 * 24 * 60 * 60 * 1000 },\n};\n\ninterface ParsedUa {\n name: string | null;\n version: string | null;\n major: number | null;\n}\n\nfunction parseUa(ua: string): ParsedUa {\n // Order matters: Edge/Opera spoof Chrome tokens, so test them first.\n const tests: Array<[string, RegExp]> = [\n [\"edge\", /Edg(?:e|A|iOS)?\\/([\\d.]+)/],\n [\"opera\", /OPR\\/([\\d.]+)/],\n [\"firefox\", /Firefox\\/([\\d.]+)/],\n [\"chrome\", /Chrome\\/([\\d.]+)/],\n [\"safari\", /Version\\/([\\d.]+).*Safari/],\n ];\n for (const [name, re] of tests) {\n const m = ua.match(re);\n if (m) {\n const version = m[1];\n const major = parseInt(version.split(\".\")[0], 10);\n return { name, version, major: Number.isFinite(major) ? major : null };\n }\n }\n return { name: null, version: null, major: null };\n}\n\nfunction uaClientHintsBrand(): ParsedUa | null {\n return tryCatch<ParsedUa | null>(() => {\n const uaData = (navigator as unknown as {\n userAgentData?: { brands?: Array<{ brand: string; version: string }> };\n }).userAgentData;\n if (!uaData?.brands?.length) return null;\n const pick = uaData.brands.find((b) =>\n !/Not.?A.?Brand|Chromium/i.test(b.brand)\n ) || uaData.brands.find((b) => /Chromium/i.test(b.brand));\n if (!pick) return null;\n const name = /edge/i.test(pick.brand)\n ? \"edge\"\n : /opera|opr/i.test(pick.brand)\n ? \"opera\"\n : /chrome|chromium/i.test(pick.brand)\n ? \"chrome\"\n : pick.brand.toLowerCase();\n const major = parseInt(pick.version, 10);\n return { name, version: pick.version, major: Number.isFinite(major) ? major : null };\n }, null);\n}\n\nfunction estimateVersionAge(name: string | null, major: number | null): number | null {\n if (!name || major == null) return null;\n const anchor = ANCHORS[name];\n if (!anchor) return null;\n const estRelease = anchor.date + (major - anchor.major) * anchor.cadenceMs;\n const ageMs = Date.now() - estRelease;\n return Math.max(0, ageMs / YEAR_MS);\n}\n\nfunction bucketAge(years: number | null): BrowserSignals[\"version_age_bucket\"] {\n if (years == null) return null;\n if (years < 1) return \"<1\";\n if (years < 2) return \"1-2\";\n if (years < 5) return \"2-5\";\n return \">=5\";\n}\n\n/* ---- privacy-extension bait: create commonly-blocked elements ---- */\nfunction privacyExtensionBait(): Promise<{ blocked: boolean | null; dynamicBlocked: boolean | null }> {\n return new Promise((resolve) => {\n try {\n if (!document.body) return resolve({ blocked: null, dynamicBlocked: null });\n const bait = document.createElement(\"div\");\n bait.className = \"pub_300x250 ad-banner adsbox sponsor-ad ad-placement\";\n bait.style.cssText = \"position:absolute;left:-9999px;top:-9999px;height:10px;width:10px;\";\n bait.innerHTML = \" \";\n document.body.appendChild(bait);\n\n // Second bait that mimics a dynamically-injected ad slot.\n const dyn = document.createElement(\"ins\");\n dyn.className = \"adsbygoogle\";\n dyn.style.cssText = \"position:absolute;left:-9999px;display:block;height:10px;width:10px;\";\n document.body.appendChild(dyn);\n\n setTimeout(() => {\n const blocked = tryCatch(() => {\n const hidden = bait.offsetParent === null ||\n bait.offsetHeight === 0 ||\n bait.clientHeight === 0 ||\n getComputedStyle(bait).display === \"none\";\n return hidden;\n }, null);\n const dynamicBlocked = tryCatch(() => {\n const hidden = dyn.offsetHeight === 0 ||\n getComputedStyle(dyn).display === \"none\";\n return hidden;\n }, null);\n try {\n document.body.removeChild(bait);\n document.body.removeChild(dyn);\n } catch { /* already removed */ }\n resolve({ blocked, dynamicBlocked });\n }, 120);\n } catch {\n resolve({ blocked: null, dynamicBlocked: null });\n }\n });\n}\n\n/* ---- bot + spoof heuristics ---- */\nfunction detectBot(): boolean {\n return tryCatch(() => {\n if (navigator.webdriver === true) return true;\n const ua = navigator.userAgent || \"\";\n if (/bot|crawler|spider|crawling|HeadlessChrome|PhantomJS/i.test(ua)) return true;\n if (!ua) return true;\n return false;\n }, false);\n}\n\nfunction detectSpoof(parsed: ParsedUa): boolean {\n return tryCatch(() => {\n const ua = navigator.userAgent || \"\";\n const w = window as unknown as Record<string, unknown>;\n // Chrome family UA but missing the `chrome` runtime object.\n if ((parsed.name === \"chrome\" || parsed.name === \"edge\") && /Chrome\\//.test(ua) && !(\"chrome\" in w)) {\n return true;\n }\n // languages claims but empty array.\n if (Array.isArray(navigator.languages) && navigator.languages.length === 0 && navigator.language) {\n return true;\n }\n // UA-CH brand disagrees with UA string family.\n const ch = uaClientHintsBrand();\n if (ch?.name && parsed.name && ch.name !== parsed.name &&\n !(ch.name === \"chrome\" && parsed.name === \"edge\")) {\n return true;\n }\n return false;\n }, false);\n}\n\nexport async function collectBrowser(): Promise<BrowserSignals> {\n const ua = tryCatch(() => navigator.userAgent || null, null);\n const parsedUa = ua ? parseUa(ua) : { name: null, version: null, major: null };\n const ch = uaClientHintsBrand();\n // Prefer client-hints brand/version when present (more spoof-resistant).\n const name = ch?.name ?? parsedUa.name;\n const version = ch?.version ?? parsedUa.version;\n const major = ch?.major ?? parsedUa.major;\n\n const ageYears = estimateVersionAge(name, major);\n const { blocked, dynamicBlocked } = await privacyExtensionBait();\n\n return {\n user_agent: ua,\n browser_name: name,\n browser_version: version,\n version_age_years: ageYears == null ? null : Math.round(ageYears * 10) / 10,\n version_age_bucket: bucketAge(ageYears),\n privacy_extension_detected: blocked,\n is_bot: detectBot(),\n spoofing_detected: detectSpoof(parsedUa),\n dynamic_component_blocked: dynamicBlocked,\n };\n}\n","/**\n * Storage capability collector (P3).\n *\n * Reports which client-side storage layers are usable plus quota/usage and\n * whether our persistent `true_device_id` was already present. Storage\n * availability is a strong incognito / hardened-browser signal.\n */\n\nimport type { StorageSignals } from \"../types.ts\";\nimport { tryCatch } from \"../util/async.ts\";\n\nfunction testLocalStorage(): boolean {\n return tryCatch(() => {\n const k = \"__gs_t\";\n window.localStorage.setItem(k, \"1\");\n window.localStorage.removeItem(k);\n return true;\n }, false);\n}\n\nfunction testSessionStorage(): boolean {\n return tryCatch(() => {\n const k = \"__gs_t\";\n window.sessionStorage.setItem(k, \"1\");\n window.sessionStorage.removeItem(k);\n return true;\n }, false);\n}\n\nfunction testIndexedDb(): boolean {\n return tryCatch(() => typeof indexedDB !== \"undefined\" && indexedDB !== null, false);\n}\n\nfunction testCacheApi(): boolean {\n return tryCatch(() => typeof caches !== \"undefined\" && caches !== null, false);\n}\n\nasync function estimateQuota(): Promise<{ quota: number | null; usage: number | null }> {\n return await new Promise((resolve) => {\n try {\n const storage = (navigator as unknown as {\n storage?: { estimate?: () => Promise<{ quota?: number; usage?: number }> };\n }).storage;\n if (!storage?.estimate) return resolve({ quota: null, usage: null });\n storage.estimate()\n .then((e) => resolve({ quota: e.quota ?? null, usage: e.usage ?? null }))\n .catch(() => resolve({ quota: null, usage: null }));\n setTimeout(() => resolve({ quota: null, usage: null }), 500);\n } catch {\n resolve({ quota: null, usage: null });\n }\n });\n}\n\n/** `persistentIdPresent` is supplied by the device collector (triple-store read). */\nexport async function collectStorage(persistentIdPresent: boolean | null): Promise<StorageSignals> {\n const { quota, usage } = await estimateQuota();\n return {\n cookies_enabled: tryCatch(() => navigator.cookieEnabled, null),\n local_storage: testLocalStorage(),\n session_storage: testSessionStorage(),\n indexed_db: testIndexedDb(),\n cache_api: testCacheApi(),\n quota_bytes: quota,\n usage_bytes: usage,\n persistent_id_present: persistentIdPresent,\n };\n}\n","/**\n * GPS collector (P4).\n *\n * Honors the configured `GpsMode` via the consent state machine in\n * `util/consent.ts`. Coordinates are only ever read with the user's awareness:\n * `silent` never prompts, `prompt`/`required` may surface the native browser\n * dialog, `off` is a hard no-op. Server-side enrichment (country/region/city,\n * impossible-travel, vpn-fallback) happens in the edge — the client only\n * reports raw lat/lon/accuracy + the resolved permission state.\n */\n\nimport type { GpsMode, GpsSignals } from \"../types.ts\";\nimport { queryGeoPermission, decideConsent, type PermissionStateValue } from \"../util/consent.ts\";\n\nfunction empty(permission: GpsSignals[\"permission_state\"] = null): GpsSignals {\n return { latitude: null, longitude: null, accuracy_m: null, permission_state: permission };\n}\n\nfunction mapPerm(p: PermissionStateValue): GpsSignals[\"permission_state\"] {\n return p === \"unavailable\" ? \"prompt\" : p;\n}\n\n/**\n * Read a single geolocation fix within `timeoutMs`. Resolves to an empty\n * (null-coordinate) result on denial/timeout/error so the orchestrator never\n * blocks on a hesitant user.\n */\nfunction readPosition(timeoutMs: number, startPerm: PermissionStateValue): Promise<GpsSignals> {\n return new Promise((resolve) => {\n if (typeof navigator === \"undefined\" || !navigator.geolocation) {\n return resolve(empty(mapPerm(startPerm)));\n }\n\n let settled = false;\n const finish = (v: GpsSignals) => {\n if (settled) return;\n settled = true;\n resolve(v);\n };\n\n const timer = setTimeout(() => finish(empty(\"timeout\")), timeoutMs);\n\n try {\n navigator.geolocation.getCurrentPosition(\n (pos) => {\n clearTimeout(timer);\n finish({\n latitude: pos.coords.latitude ?? null,\n longitude: pos.coords.longitude ?? null,\n accuracy_m: typeof pos.coords.accuracy === \"number\" ? pos.coords.accuracy : null,\n permission_state: \"granted\",\n });\n },\n (err) => {\n clearTimeout(timer);\n // code 1 = PERMISSION_DENIED\n const denied = err && err.code === 1;\n finish(empty(denied ? \"denied\" : mapPerm(startPerm)));\n },\n { enableHighAccuracy: false, timeout: timeoutMs, maximumAge: 60_000 },\n );\n } catch {\n clearTimeout(timer);\n finish(empty(mapPerm(startPerm)));\n }\n });\n}\n\n/** Collect GPS signals respecting the configured mode + consent state. */\nexport async function collectGps(mode: GpsMode, timeoutMs = 3000): Promise<GpsSignals> {\n const perm = await queryGeoPermission();\n const decision = decideConsent(mode, perm);\n\n if (decision === \"skip\") {\n return empty(mapPerm(perm));\n }\n\n // Both \"read-if-granted\" and \"may-prompt\" call getCurrentPosition; the\n // browser only shows a dialog when state is \"prompt\".\n return await readPosition(timeoutMs, perm);\n}\n","/**\n * GPS consent state machine (P4).\n *\n * Maps the four `GpsMode` values to a concrete consent decision without ever\n * surprising the user:\n * - `off` → never query, never prompt. Returns \"skip\".\n * - `silent` → only read if permission is ALREADY granted; never prompt.\n * - `prompt` → query, allowing the browser permission prompt (SEON-parity default).\n * - `required` → same as prompt, but the host page treats denial as fatal.\n *\n * The decision is derived from the Permissions API where available, falling\n * back to \"prompt\" semantics on browsers (Safari) that don't expose\n * geolocation permission state.\n */\n\nimport type { GpsMode } from \"../types.ts\";\n\nexport type ConsentDecision = \"skip\" | \"read-if-granted\" | \"may-prompt\";\n\nexport type PermissionStateValue = \"granted\" | \"denied\" | \"prompt\" | \"unavailable\";\n\n/** Query the current geolocation permission without triggering a prompt. */\nexport async function queryGeoPermission(): Promise<PermissionStateValue> {\n try {\n const perms = (navigator as unknown as {\n permissions?: { query?: (d: { name: string }) => Promise<{ state: string }> };\n }).permissions;\n if (!perms?.query) return \"unavailable\";\n const status = await perms.query({ name: \"geolocation\" });\n const s = status.state;\n if (s === \"granted\" || s === \"denied\" || s === \"prompt\") return s;\n return \"unavailable\";\n } catch {\n return \"unavailable\";\n }\n}\n\n/** Resolve the consent decision for a given mode + current permission state. */\nexport function decideConsent(mode: GpsMode, perm: PermissionStateValue): ConsentDecision {\n switch (mode) {\n case \"off\":\n return \"skip\";\n case \"silent\":\n // Only proceed if the user has already explicitly granted access.\n return perm === \"granted\" ? \"read-if-granted\" : \"skip\";\n case \"prompt\":\n case \"required\":\n // Denied means a prompt won't appear anyway — don't waste the budget.\n if (perm === \"denied\") return \"skip\";\n return \"may-prompt\";\n default:\n return \"skip\";\n }\n}\n","/**\n * Network collector (P4).\n *\n * Three independent, best-effort probes assembled under the caller's time\n * budget:\n * 1. Network Information API — effective_type / rtt / downlink / save_data.\n * 2. WebRTC STUN — local candidate IPs + a public-IP hint (srflx candidate).\n * Reveals VPN/NAT posture the server can cross-check against the HTTP IP.\n * 3. DNS-resolver region — coarse latency timing against a few regional STUN\n * endpoints; the fastest region hints at the resolver/egress location.\n *\n * Every probe fails open to null/empty — nothing here is allowed to throw or\n * hang the token assembly.\n */\n\nimport type { NetworkSignals } from \"../types.ts\";\n\n/** Public STUN endpoints used for candidate gathering + region timing. */\nconst STUN_ENDPOINTS: Array<{ region: string; url: string }> = [\n { region: \"us\", url: \"stun:stun.l.google.com:19302\" },\n { region: \"eu\", url: \"stun:stun.cloudflare.com:3478\" },\n { region: \"global\", url: \"stun:global.stun.twilio.com:3478\" },\n];\n\nconst PRIVATE_IP_RE =\n /^(10\\.|127\\.|169\\.254\\.|192\\.168\\.|172\\.(1[6-9]|2\\d|3[0-1])\\.|::1|fe80:|fc00:|fd[0-9a-f]{2}:)/i;\nconst IP_RE = /([0-9]{1,3}(?:\\.[0-9]{1,3}){3})|([a-f0-9]{1,4}(?::[a-f0-9]{0,4}){2,7})/i;\n\nfunction emptyNetwork(): NetworkSignals {\n return {\n effective_type: null,\n rtt: null,\n downlink: null,\n save_data: null,\n webrtc_local_ips: [],\n public_ip_hint: null,\n dns_resolver_region: null,\n };\n}\n\n/** Synchronous, cheap connection metadata read. */\nfunction readConnection(): Pick<NetworkSignals, \"effective_type\" | \"rtt\" | \"downlink\" | \"save_data\"> {\n const out = { effective_type: null, rtt: null, downlink: null, save_data: null } as Pick<\n NetworkSignals,\n \"effective_type\" | \"rtt\" | \"downlink\" | \"save_data\"\n >;\n try {\n const c = (navigator as unknown as {\n connection?: { effectiveType?: string; rtt?: number; downlink?: number; saveData?: boolean };\n }).connection;\n if (c) {\n out.effective_type = c.effectiveType ?? null;\n out.rtt = typeof c.rtt === \"number\" ? c.rtt : null;\n out.downlink = typeof c.downlink === \"number\" ? c.downlink : null;\n out.save_data = typeof c.saveData === \"boolean\" ? c.saveData : null;\n }\n } catch {\n /* unsupported */\n }\n return out;\n}\n\ninterface RtcResult {\n localIps: string[];\n publicIpHint: string | null;\n region: string | null;\n}\n\n/**\n * Gather ICE candidates via a throwaway RTCPeerConnection. Local (host)\n * candidates populate `webrtc_local_ips`; the first server-reflexive (srflx)\n * candidate gives a `public_ip_hint`. We also record which STUN region replied\n * first as a coarse egress hint.\n */\nfunction gatherWebrtc(timeoutMs: number): Promise<RtcResult> {\n return new Promise((resolve) => {\n const result: RtcResult = { localIps: [], publicIpHint: null, region: null };\n const RTC = (window as unknown as { RTCPeerConnection?: typeof RTCPeerConnection })\n .RTCPeerConnection;\n if (!RTC) return resolve(result);\n\n const seenLocal = new Set<string>();\n let pc: RTCPeerConnection | null = null;\n let settled = false;\n\n const finish = () => {\n if (settled) return;\n settled = true;\n try {\n pc?.close();\n } catch {\n /* ignore */\n }\n result.localIps = Array.from(seenLocal);\n resolve(result);\n };\n\n const timer = setTimeout(finish, timeoutMs);\n\n try {\n pc = new RTC({ iceServers: STUN_ENDPOINTS.map((e) => ({ urls: e.url })) });\n // A data channel is required for candidate gathering to begin.\n pc.createDataChannel(\"gs\");\n\n pc.onicecandidate = (ev) => {\n if (!ev.candidate) {\n // null candidate = gathering complete\n clearTimeout(timer);\n return finish();\n }\n const cand = ev.candidate.candidate || \"\";\n const m = cand.match(IP_RE);\n const ip = m ? m[0] : null;\n if (!ip) return;\n\n if (/typ host/.test(cand)) {\n if (PRIVATE_IP_RE.test(ip) || !ip.includes(\".\")) {\n // keep private/host candidates (mDNS-obfuscated ones won't match IP_RE)\n seenLocal.add(ip);\n } else {\n seenLocal.add(ip);\n }\n } else if (/typ srflx/.test(cand) || /typ prflx/.test(cand)) {\n if (!PRIVATE_IP_RE.test(ip)) {\n result.publicIpHint = result.publicIpHint ?? ip;\n if (!result.region) {\n const relAddr = cand.match(/raddr ([0-9a-f.:]+)/i);\n void relAddr; // reserved for future correlation\n }\n }\n }\n };\n\n pc.createOffer()\n .then((offer) => pc!.setLocalDescription(offer))\n .catch(() => {\n clearTimeout(timer);\n finish();\n });\n } catch {\n clearTimeout(timer);\n finish();\n }\n });\n}\n\n/**\n * Coarse DNS-resolver / egress region probe: time how fast each regional STUN\n * endpoint produces its first candidate; the fastest region is reported. This\n * is a hint only — the server's IpInfo lookup remains authoritative.\n */\nfunction probeRegion(timeoutMs: number): Promise<string | null> {\n return new Promise((resolve) => {\n const RTC = (window as unknown as { RTCPeerConnection?: typeof RTCPeerConnection })\n .RTCPeerConnection;\n if (!RTC) return resolve(null);\n\n let best: { region: string; ms: number } | null = null;\n let pending = STUN_ENDPOINTS.length;\n const conns: RTCPeerConnection[] = [];\n let settled = false;\n\n const finish = () => {\n if (settled) return;\n settled = true;\n for (const c of conns) {\n try {\n c.close();\n } catch {\n /* ignore */\n }\n }\n resolve(best?.region ?? null);\n };\n\n const timer = setTimeout(finish, timeoutMs);\n\n for (const ep of STUN_ENDPOINTS) {\n try {\n const pc = new RTC({ iceServers: [{ urls: ep.url }] });\n conns.push(pc);\n const start =\n typeof performance !== \"undefined\" && performance.now ? performance.now() : Date.now();\n pc.createDataChannel(\"rg\");\n pc.onicecandidate = (ev) => {\n if (!ev.candidate) return;\n if (!/typ srflx/.test(ev.candidate.candidate || \"\")) return;\n const ms =\n (typeof performance !== \"undefined\" && performance.now\n ? performance.now()\n : Date.now()) - start;\n if (!best || ms < best.ms) best = { region: ep.region, ms };\n if (--pending <= 0) {\n clearTimeout(timer);\n finish();\n }\n };\n pc.createOffer()\n .then((o) => pc.setLocalDescription(o))\n .catch(() => {\n if (--pending <= 0) {\n clearTimeout(timer);\n finish();\n }\n });\n } catch {\n if (--pending <= 0) {\n clearTimeout(timer);\n finish();\n }\n }\n }\n });\n}\n\n/** Collect all network signals within the given budget. */\nexport async function collectNetwork(budgetMs = 2500): Promise<NetworkSignals> {\n const out = emptyNetwork();\n Object.assign(out, readConnection());\n\n const webrtcBudget = Math.min(budgetMs, 2000);\n const regionBudget = Math.min(budgetMs, 1500);\n\n const [rtc, region] = await Promise.all([\n gatherWebrtc(webrtcBudget).catch(() => ({ localIps: [], publicIpHint: null, region: null })),\n probeRegion(regionBudget).catch(() => null),\n ]);\n\n out.webrtc_local_ips = rtc.localIps;\n out.public_ip_hint = rtc.publicIpHint;\n out.dns_resolver_region = region;\n return out;\n}\n","/**\n * Behavioral biometrics collector (P5) — always-on, timing only.\n *\n * PRIVACY CONTRACT: this module NEVER records input *values*. It only measures\n * timing and motion dynamics: when keys go down/up (not which keys), how the\n * pointer moves (not where it clicks), scroll rhythm, paste usage, tab focus\n * changes, and total session age. No DOM content, no field values, no key\n * identities ever enter a buffer.\n *\n * Capture is \"always-on\": `startBehaviorCapture()` is invoked from `gs.config`\n * so dynamics accumulate across the whole page lifetime. `snapshotBehavior()`\n * reads the current aggregates at token-seal time without stopping capture, so\n * a single page can mint multiple tokens (login, then checkout) each carrying\n * the dynamics observed up to that moment.\n */\n\nimport type { BehaviorSignals } from \"../types.ts\";\n\nconst RING = 256; // bounded buffers — fixed memory, no unbounded growth\n\ninterface Ring {\n buf: Float64Array;\n len: number;\n idx: number;\n}\n\nfunction makeRing(): Ring {\n return { buf: new Float64Array(RING), len: 0, idx: 0 };\n}\n\nfunction push(r: Ring, v: number): void {\n r.buf[r.idx] = v;\n r.idx = (r.idx + 1) % RING;\n if (r.len < RING) r.len++;\n}\n\nfunction values(r: Ring): number[] {\n const out: number[] = [];\n for (let i = 0; i < r.len; i++) out.push(r.buf[i]);\n return out;\n}\n\nfunction avg(r: Ring): number | null {\n if (r.len === 0) return null;\n let s = 0;\n for (let i = 0; i < r.len; i++) s += r.buf[i];\n return s / r.len;\n}\n\ninterface CaptureState {\n started: boolean;\n startedAt: number;\n\n // pointer\n lastPoint: { x: number; y: number; t: number } | null;\n angles: Ring; // turn angles between successive segments (entropy source)\n moveCount: number;\n\n // keystroke\n keyDownAt: Map<number, number>; // keyCode -> downTime (timing only; never logged)\n dwell: Ring; // keyup - keydown\n flight: Ring; // keydown(n) - keyup(n-1)\n lastKeyUpAt: number | null;\n\n // form fill — chars typed vs elapsed within editable fields\n formCharCount: number;\n formFirstInputAt: number | null;\n formLastInputAt: number | null;\n\n // scroll\n lastScrollAt: number | null;\n scrollGaps: Ring;\n\n // misc\n copyPasteUsed: boolean;\n tabSwitches: number;\n}\n\nlet s: CaptureState | null = null;\nconst cleanups: Array<() => void> = [];\n\nfunction nowMs(): number {\n return typeof performance !== \"undefined\" && performance.now ? performance.now() : Date.now();\n}\n\nfunction on<K extends keyof DocumentEventMap>(\n target: Document | Window,\n type: K,\n handler: (ev: DocumentEventMap[K]) => void,\n opts?: AddEventListenerOptions,\n): void {\n try {\n (target as Document).addEventListener(\n type,\n handler as EventListener,\n { passive: true, capture: true, ...opts },\n );\n cleanups.push(() =>\n (target as Document).removeEventListener(type, handler as EventListener, { capture: true }),\n );\n } catch {\n /* environment without DOM */\n }\n}\n\nfunction isEditable(el: EventTarget | null): boolean {\n const node = el as HTMLElement | null;\n if (!node || !node.tagName) return false;\n const tag = node.tagName.toUpperCase();\n if (tag === \"TEXTAREA\") return true;\n if (tag === \"INPUT\") {\n const type = (node.getAttribute(\"type\") || \"text\").toLowerCase();\n // Exclude non-text inputs; never inspect the value itself.\n return ![\"checkbox\", \"radio\", \"button\", \"submit\", \"range\", \"file\", \"color\"].includes(type);\n }\n return node.isContentEditable === true;\n}\n\n/** Begin always-on capture. Idempotent — safe to call from every `gs.config`. */\nexport function startBehaviorCapture(): void {\n if (s?.started) return;\n if (typeof document === \"undefined\" || typeof window === \"undefined\") return;\n\n s = {\n started: true,\n startedAt: nowMs(),\n lastPoint: null,\n angles: makeRing(),\n moveCount: 0,\n keyDownAt: new Map(),\n dwell: makeRing(),\n flight: makeRing(),\n lastKeyUpAt: null,\n formCharCount: 0,\n formFirstInputAt: null,\n formLastInputAt: null,\n lastScrollAt: null,\n scrollGaps: makeRing(),\n copyPasteUsed: false,\n tabSwitches: 0,\n };\n const st = s;\n\n // ---- pointer dynamics: record turn angles between movement segments ----\n on(document, \"pointermove\", (ev) => {\n const e = ev as PointerEvent;\n const t = nowMs();\n const p = { x: e.clientX, y: e.clientY, t };\n st.moveCount++;\n const prev = st.lastPoint;\n if (prev) {\n const dx = p.x - prev.x;\n const dy = p.y - prev.y;\n if (dx !== 0 || dy !== 0) {\n const angle = Math.atan2(dy, dx); // direction only — no coordinates stored\n push(st.angles, angle);\n }\n }\n st.lastPoint = p;\n });\n\n // ---- keystroke dynamics: dwell + flight, never key identity content ----\n on(document, \"keydown\", (ev) => {\n const e = ev as KeyboardEvent;\n if (e.repeat) return;\n const t = nowMs();\n // keyCode is used only as an ephemeral map key to pair with keyup timing.\n if (!st.keyDownAt.has(e.keyCode)) st.keyDownAt.set(e.keyCode, t);\n if (st.lastKeyUpAt != null) {\n const flight = t - st.lastKeyUpAt;\n if (flight >= 0 && flight < 5000) push(st.flight, flight);\n }\n });\n on(document, \"keyup\", (ev) => {\n const e = ev as KeyboardEvent;\n const t = nowMs();\n const down = st.keyDownAt.get(e.keyCode);\n if (down != null) {\n const dwell = t - down;\n if (dwell >= 0 && dwell < 5000) push(st.dwell, dwell);\n st.keyDownAt.delete(e.keyCode);\n }\n st.lastKeyUpAt = t;\n });\n\n // ---- form-fill speed: count chars typed into editable fields over time ----\n on(document, \"input\", (ev) => {\n if (!isEditable(ev.target)) return;\n const t = nowMs();\n if (st.formFirstInputAt == null) st.formFirstInputAt = t;\n st.formLastInputAt = t;\n // Approximate keystroke-driven characters without reading the value.\n const ie = ev as InputEvent;\n if (ie.inputType === \"insertText\" || ie.inputType === \"insertCompositionText\") {\n st.formCharCount += ie.data ? ie.data.length : 1;\n } else if (ie.inputType === \"insertFromPaste\") {\n st.copyPasteUsed = true;\n }\n });\n\n // ---- scroll rhythm ----\n on(window, \"scroll\", () => {\n const t = nowMs();\n if (st.lastScrollAt != null) {\n const gap = t - st.lastScrollAt;\n if (gap >= 0 && gap < 60_000) push(st.scrollGaps, gap);\n }\n st.lastScrollAt = t;\n });\n\n // ---- paste + tab visibility ----\n on(document, \"paste\", () => {\n st.copyPasteUsed = true;\n });\n on(document, \"visibilitychange\", () => {\n if (document.visibilityState === \"hidden\") st.tabSwitches++;\n });\n}\n\n/** Stop capture and detach all listeners (used by GDPR/teardown paths). */\nexport function stopBehaviorCapture(): void {\n while (cleanups.length) {\n try {\n cleanups.pop()!();\n } catch {\n /* ignore */\n }\n }\n s = null;\n}\n\n/** Shannon entropy (normalized 0..1) of pointer turn angles, binned into 16. */\nfunction angleEntropy(r: Ring): number | null {\n if (r.len < 8) return null;\n const bins = new Array(16).fill(0);\n const vals = values(r);\n for (const a of vals) {\n // map [-PI, PI] -> [0, 16)\n let bin = Math.floor(((a + Math.PI) / (2 * Math.PI)) * 16);\n if (bin < 0) bin = 0;\n if (bin > 15) bin = 15;\n bins[bin]++;\n }\n const n = vals.length;\n let h = 0;\n for (const c of bins) {\n if (c === 0) continue;\n const p = c / n;\n h -= p * Math.log2(p);\n }\n return Math.min(1, h / 4); // max entropy for 16 bins = log2(16) = 4\n}\n\n/**\n * Bot-likelihood heuristic in [0..1]. Higher = more automation-like:\n * near-zero pointer entropy, robotically uniform keystroke timing, and\n * machine-gun form-fill speed all push the score up. Returns null when there\n * is too little interaction to judge.\n */\nfunction riskScore(\n entropy: number | null,\n dwellAvg: number | null,\n flightAvg: number | null,\n formCps: number | null,\n moveCount: number,\n): number | null {\n const factors: number[] = [];\n\n if (moveCount >= 10 && entropy != null) {\n // Straight-line / no-jitter movement is suspicious.\n factors.push(entropy < 0.2 ? 0.9 : entropy < 0.4 ? 0.5 : 0.1);\n }\n if (dwellAvg != null) {\n // Sub-12ms or supernaturally uniform dwell -> automation.\n factors.push(dwellAvg < 12 ? 0.85 : dwellAvg < 25 ? 0.4 : 0.1);\n }\n if (flightAvg != null) {\n factors.push(flightAvg < 8 ? 0.85 : flightAvg < 20 ? 0.4 : 0.1);\n }\n if (formCps != null) {\n // >25 chars/sec sustained is faster than human typing.\n factors.push(formCps > 25 ? 0.9 : formCps > 15 ? 0.5 : 0.1);\n }\n\n if (factors.length === 0) return null;\n const score = factors.reduce((a, b) => a + b, 0) / factors.length;\n return Math.round(score * 100) / 100;\n}\n\nfunction empty(): BehaviorSignals {\n return {\n mouse_path_entropy: null, keystroke_dwell_avg_ms: null, keystroke_flight_avg_ms: null,\n scroll_cadence_ms: null, form_fill_speed_cps: null, copy_paste_used: null,\n tab_switches: null, session_duration_ms: null, risk_score: null,\n };\n}\n\n/** Read a snapshot of accumulated dynamics. Capture continues running. */\nexport function snapshotBehavior(): BehaviorSignals {\n if (!s) return empty();\n\n const entropy = angleEntropy(s.angles);\n const dwellAvg = avg(s.dwell);\n const flightAvg = avg(s.flight);\n const scrollCadence = avg(s.scrollGaps);\n\n let formCps: number | null = null;\n if (\n s.formCharCount > 0 &&\n s.formFirstInputAt != null &&\n s.formLastInputAt != null &&\n s.formLastInputAt > s.formFirstInputAt\n ) {\n const seconds = (s.formLastInputAt - s.formFirstInputAt) / 1000;\n if (seconds > 0) formCps = Math.round((s.formCharCount / seconds) * 100) / 100;\n }\n\n return {\n mouse_path_entropy: entropy,\n keystroke_dwell_avg_ms: dwellAvg != null ? Math.round(dwellAvg) : null,\n keystroke_flight_avg_ms: flightAvg != null ? Math.round(flightAvg) : null,\n scroll_cadence_ms: scrollCadence != null ? Math.round(scrollCadence) : null,\n form_fill_speed_cps: formCps,\n copy_paste_used: s.copyPasteUsed,\n tab_switches: s.tabSwitches,\n session_duration_ms: Math.round(nowMs() - s.startedAt),\n risk_score: riskScore(entropy, dwellAvg, flightAvg, formCps, s.moveCount),\n };\n}\n","/**\n * GS Web Intelligence SDK — public entry + session orchestrator.\n *\n * Usage (CDN / IIFE):\n * <script src=\"https://cdn.gammasweep.com/gs.js\"></script>\n * <script>\n * gs.config({ gps: \"prompt\" });\n * const token = await gs.getSession(); // opaque JWE, send to your backend\n * </script>\n *\n * The orchestrator runs every collector in parallel under a hard wall-clock\n * budget (`max_wait_time`), assembles the `Signals` envelope, and seals it into\n * a compact JWE. Nothing identifying ever leaves the page in cleartext (except\n * in explicit `sandbox` mode for `gk_test_` integration).\n */\n\nimport type {\n BehaviorSignals, EmailHintSignals, GpsSignals, NetworkSignals,\n GsConfig, Signals, TokenPayload, DeviceSignals,\n} from \"./types.ts\";\nimport { sealJwe } from \"./crypto/jwe.ts\";\nimport { ACTIVE_KID } from \"./keys.ts\";\nimport { uuid, randomHex } from \"./util/sha256.ts\";\nimport { safe, now } from \"./util/async.ts\";\nimport { collectDevice } from \"./collectors/device.ts\";\nimport { collectTamper, spoofingHash } from \"./collectors/tamper.ts\";\nimport { collectBrowser } from \"./collectors/browser.ts\";\nimport { collectStorage } from \"./collectors/storage.ts\";\nimport { collectGps } from \"./collectors/gps.ts\";\nimport { collectNetwork } from \"./collectors/network.ts\";\nimport { startBehaviorCapture, stopBehaviorCapture, snapshotBehavior } from \"./collectors/behavior.ts\";\n\nconst TTL_MS = 5 * 60 * 1000; // must mirror _shared/device-token.ts\nconst GPS_PROMPT_TIMEOUT_MS = 15_000;\n\nconst defaults: Required<Omit<GsConfig, \"session_id\">> = {\n gps: \"prompt\",\n behavior: true,\n persistence: true,\n max_wait_time: 4000,\n sandbox: false,\n debug: false,\n};\n\nlet cfg: GsConfig = {};\nlet lastGpsSignals: GpsSignals | null = null;\n\nfunction log(...args: unknown[]): void {\n if (cfg.debug) console.info(\"[gs]\", ...args);\n}\n\n/* ---- placeholder signal blocks (behavior + email filled in P5/host) ---- */\nfunction emptyGps(): GpsSignals {\n return { latitude: null, longitude: null, accuracy_m: null, permission_state: null };\n}\nfunction emptyNetwork(): NetworkSignals {\n return {\n effective_type: null, rtt: null, downlink: null, save_data: null,\n webrtc_local_ips: [], public_ip_hint: null, dns_resolver_region: null,\n };\n}\nfunction emptyBehavior(): BehaviorSignals {\n return {\n mouse_path_entropy: null, keystroke_dwell_avg_ms: null, keystroke_flight_avg_ms: null,\n scroll_cadence_ms: null, form_fill_speed_cps: null, copy_paste_used: null,\n tab_switches: null, session_duration_ms: null, risk_score: null,\n };\n}\nfunction emptyEmail(address: string | null): EmailHintSignals {\n return { address };\n}\n\n\n/** Collect every available signal under the configured time budget. */\nasync function collectSignals(): Promise<Signals> {\n const persist = cfg.persistence ?? defaults.persistence;\n const budget = cfg.max_wait_time ?? defaults.max_wait_time;\n const gpsMode = cfg.gps ?? defaults.gps;\n const gpsBudget = gpsMode === \"prompt\" || gpsMode === \"required\"\n ? Math.max(budget, GPS_PROMPT_TIMEOUT_MS)\n : Math.min(budget, 3000);\n\n // Network + GPS probes are I/O-bound (WebRTC, geolocation) and must share the\n // same wall-clock budget as the synchronous collectors — run everything flat.\n const t0 = now();\n const [device, tamper, browser, network, gps] = await Promise.all([\n safe(collectDevice(persist), fallbackDevice(), budget),\n safe(collectTamper(), fallbackTamper(), budget),\n safe(collectBrowser(), fallbackBrowser(), budget),\n safe(collectNetwork(Math.min(budget, 2500)), emptyNetwork(), budget),\n safe(collectGps(gpsMode, gpsBudget), emptyGps(), gpsBudget + 250),\n ]);\n lastGpsSignals = gps;\n\n // Merge tamper posture into the device's spoofing_hash.\n device.spoofing_hash = await safe(spoofingHash(tamper), null, 500);\n\n // Storage depends on whether the persistent id pre-existed (device read it).\n const idPresent =\n (device as unknown as { __persistent_id_present?: boolean }).__persistent_id_present ?? null;\n const storage = await safe(collectStorage(idPresent), fallbackStorage(), budget);\n delete (device as unknown as Record<string, unknown>).__persistent_id_present;\n\n log(`signals collected in ${Math.round(now() - t0)}ms`);\n\n return {\n device,\n browser,\n tamper,\n gps,\n network,\n storage,\n behavior: (cfg.behavior ?? defaults.behavior) ? snapshotBehavior() : emptyBehavior(),\n email: emptyEmail(null),\n };\n}\n\n\n/* ---- minimal fallbacks if a whole collector times out ---- */\nfunction fallbackDevice(): DeviceSignals {\n return {\n device_hash: null, true_device_id: uuid(), browser_hash: null, cookie_hash: null,\n canvas_hash: null, webgl_hash: null, webgl2_hash: null, webgl_image_hash: null,\n webgl_params_hash: null, audio_hash: null, font_list_hash: null, spoofing_hash: null,\n screen: { width: null, height: null, color_depth: null, pixel_ratio: null, refresh_rate: null, is_extended: null },\n touch_support: null, device_memory: null, hardware_concurrency: null,\n platform: null, vendor: null, timezone: null, timezone_offset: null,\n languages: null, do_not_track: null, gpc: null,\n };\n}\nfunction fallbackTamper() {\n return {\n is_webdriver: null, is_headless: null, is_emulator: null, function_tampered: null,\n ua_platform_mismatch: null, private_mode: null, automation_flags: [],\n screen_mirrored: null, remote_desktop_suspected: null,\n };\n}\nfunction fallbackBrowser() {\n return {\n user_agent: null, browser_name: null, browser_version: null, version_age_years: null,\n version_age_bucket: null, privacy_extension_detected: null, is_bot: null,\n spoofing_detected: null, dynamic_component_blocked: null,\n };\n}\nfunction fallbackStorage() {\n return {\n cookies_enabled: null, local_storage: null, session_storage: null, indexed_db: null,\n cache_api: null, quota_bytes: null, usage_bytes: null, persistent_id_present: null,\n };\n}\n\n/* ----------------------------- public API ----------------------------- */\nexport function config(opts: GsConfig): void {\n cfg = { ...cfg, ...opts };\n log(\"configured\", cfg);\n // Always-on behavioral capture begins at config time so dynamics accumulate\n // across the full page lifetime (and persist across multiple getSession calls).\n if (cfg.behavior ?? defaults.behavior) {\n startBehaviorCapture();\n } else {\n stopBehaviorCapture();\n }\n}\n\n\n/**\n * Build a fresh, single-use session token. Generate a new one per protected\n * user-action (login, checkout, signup) — tokens are one-time and expire in 5m.\n */\nexport async function getSession(): Promise<string> {\n const iat = Date.now();\n const signals = await collectSignals();\n if ((cfg.gps ?? defaults.gps) === \"required\" && (signals.gps.latitude == null || signals.gps.longitude == null)) {\n const err = new Error(\"GPS location is required but was not captured. Allow location permission and retry.\") as Error & {\n code?: string;\n gps_permission_state?: GpsSignals[\"permission_state\"];\n };\n err.name = \"GpsRequiredError\";\n err.code = \"GPS_REQUIRED\";\n err.gps_permission_state = signals.gps.permission_state;\n throw err;\n }\n const payload: TokenPayload = {\n v: 1,\n sid: cfg.session_id ?? uuid(),\n iat,\n exp: iat + TTL_MS,\n nonce: randomHex(16),\n collected_at: iat,\n signals,\n };\n\n if (cfg.sandbox ?? defaults.sandbox) {\n log(\"sandbox mode — returning unencrypted payload\");\n return `gsds_sandbox.${btoa(JSON.stringify(payload)).replace(/=+$/, \"\")}`;\n }\n\n return await sealJwe(payload, ACTIVE_KID);\n}\n\n/** Convenience: only the persistent device identity, without a full token. */\nexport async function getDeviceId(): Promise<string> {\n const device = await safe(collectDevice(cfg.persistence ?? defaults.persistence), fallbackDevice(), 3000);\n return device.true_device_id ?? uuid();\n}\n\n/** Last GPS block captured by getSession(); useful for integration diagnostics. */\nexport function getLastGps(): GpsSignals | null {\n return lastGpsSignals ? { ...lastGpsSignals } : null;\n}\n\nexport const gs = { config, getSession, getDeviceId, getLastGps };\n\n// Attach to window for the IIFE/CDN build.\nif (typeof window !== \"undefined\") {\n (window as unknown as { gs?: typeof gs }).gs = gs;\n}\n\nexport type { GsConfig, Signals, TokenPayload };\n"],"names":["PUBLIC_KEYS","v1","kty","n","e","b64url","buf","bin","i","length","String","fromCharCode","btoa","replace","enc","TextEncoder","async","sealJwe","payload","kid","jwk","Error","header","alg","typ","encodedHeader","encode","JSON","stringify","aad","cek","crypto","subtle","generateKey","name","rawCek","Uint8Array","exportKey","pub","importKey","ext","hash","importPublicKey","wrapped","encrypt","iv","getRandomValues","plaintext","ctTag","additionalData","tagLength","ciphertext","slice","tag","join","sha256Short","input","len","full","data","digest","bufToHex","sha256Hex","out","toString","padStart","randomHex","bytes","b","uuid","randomUUID","h","safe","p","fallback","timeoutMs","Promise","race","resolve","setTimeout","tryCatch","fn","now","performance","Date","KEY","STORE_NAME","openDb","indexedDB","req","open","onupgradeneeded","result","createObjectStore","onsuccess","onerror","resolveTrueDeviceId","persist","id","present_before","ls","window","localStorage","getItem","readLocalStorage","ck","match","document","cookie","RegExp","decodeURIComponent","readCookie","idb","db","transaction","objectStore","get","readIndexedDb","existing","setItem","writeLocalStorage","secure","location","protocol","encodeURIComponent","writeCookie","tx","put","oncomplete","writeIndexedDb","webglParts","version","canvas","createElement","gl","getContext","imageHash","paramsHash","vendor","renderer","dbg","getExtension","params","push","label","v","vendorStr","rendererStr","getParameter","UNMASKED_VENDOR_WEBGL","UNMASKED_RENDERER_WEBGL","numeric","MAX_TEXTURE_SIZE","MAX_RENDERBUFFER_SIZE","MAX_VERTEX_ATTRIBS","MAX_VARYING_VECTORS","MAX_VERTEX_UNIFORM_VECTORS","MAX_FRAGMENT_UNIFORM_VECTORS","MAX_TEXTURE_IMAGE_UNITS","MAX_COMBINED_TEXTURE_IMAGE_UNITS","ALIASED_LINE_WIDTH_RANGE","ALIASED_POINT_SIZE_RANGE","MAX_VIEWPORT_DIMS","exts","getSupportedExtensions","sort","imageData","createBuffer","bindBuffer","ARRAY_BUFFER","verts","Float32Array","bufferData","STATIC_DRAW","pixels","width","height","readPixels","RGBA","UNSIGNED_BYTE","Array","from","collectUserAgentData","uaData","navigator","userAgentData","high","getHighEntropyValues","str","platform","platform_version","platformVersion","architecture","bitness","mobile","model","ua_full_version","uaFullVersion","collectMediaDevices","md","mediaDevices","enumerateDevices","devices","counts","audio_input","audio_output","video_input","d","kind","FONT_PROBE","collectDevice","nav","audio","all","ctx","textBaseline","font","fillStyle","fillRect","fillText","globalCompositeOperation","beginPath","arc","Math","PI","fill","toDataURL","Ctx","OfflineAudioContext","webkitOfflineAudioContext","osc","createOscillator","type","frequency","value","comp","createDynamicsCompressor","threshold","knee","ratio","attack","release","connect","destination","start","startRendering","timer","clearTimeout","renderedBuffer","getChannelData","sum","abs","wgl1","wgl2","fonts","base","span","style","position","left","fontSize","textContent","body","appendChild","baseline","fontFamily","w","offsetWidth","offsetHeight","available","detected","removeChild","screen_","orientation","screen","color_depth","colorDepth","pixel_ratio","devicePixelRatio","refresh_rate","is_extended","isExtended","available_width","availWidth","available_height","availHeight","pixel_depth","pixelDepth","orientation_type","orientation_angle","angle","pluginList","list","plugins","names","mimeSeed","mt","mimeTypes","types","t","mathS","acos","acosh","asin","asinh","atan","atanh","cbrt","cos","cosh","exp","expm1","log1p","sin","sinh","tan","tanh","pow","map","sysColorsSeed","keywords","el","kw","color","getComputedStyle","locale","Intl","DateTimeFormat","resolvedOptions","windowLocation","href","windowHostname","hostname","timezone","timeZone","tzOffset","getTimezoneOffset","languages","language","deviceMemory","concurrency","hardwareConcurrency","touch","maxTouchPoints","dnt","doNotTrack","gpc","globalPrivacyControl","trueDeviceId","canvasHash","webglImageHash","webglParamsHash","webgl2ParamsHash","audioHash","fontHash","pluginHash","mimeTypesHash","mathHash","systemColorsHash","deviceSeed","deviceHash","browserSeed","userAgent","device_hash","true_device_id","browser_hash","cookie_hash","canvas_hash","webgl_hash","webgl2_hash","webgl_image_hash","webgl_params_hash","audio_hash","font_list_hash","spoofing_hash","touch_support","device_memory","hardware_concurrency","timezone_offset","do_not_track","font_list","plugin_list","plugin_count","plugin_hash","mime_types_hash","math_hash","system_colors_hash","webgl_vendor","webgl_renderer","timezone_country","user_agent_data","media_devices","window_location","window_hostname","__persistent_id_present","collectTamper","flags","probes","webdriver","Object","keys","some","k","test","automationFlags","privateMode","est","storage","estimate","then","quota","catch","detectPrivateMode","isWebdriver","isHeadless","ua","noPlugins","noLanguages","detectHeadless","isEmulator","mobileUa","hasTouch","functionTampered","natives","Function","prototype","bind","permissions","query","HTMLCanvasElement","WebGLRenderingContext","filter","Boolean","s","call","ts","is_webdriver","is_headless","is_emulator","function_tampered","ua_platform_mismatch","toLowerCase","plat","uaWin","includes","uaMac","uaLinux","platWin","platMac","platLinux","private_mode","automation_flags","screen_mirrored","remote_desktop_suspected","dpr","YEAR_MS","ANCHORS","chrome","major","date","UTC","cadenceMs","edge","firefox","safari","round","opera","uaClientHintsBrand","brands","pick","find","brand","parseInt","Number","isFinite","collectBrowser","parsedUa","tests","re","m","split","parseUa","ch","ageYears","anchor","estRelease","ageMs","max","estimateVersionAge","blocked","dynamicBlocked","bait","className","cssText","innerHTML","dyn","offsetParent","clientHeight","display","user_agent","browser_name","browser_version","version_age_years","version_age_bucket","years","privacy_extension_detected","is_bot","spoofing_detected","parsed","isArray","dynamic_component_blocked","collectStorage","persistentIdPresent","usage","estimateQuota","cookies_enabled","cookieEnabled","local_storage","removeItem","session_storage","sessionStorage","indexed_db","cache_api","caches","quota_bytes","usage_bytes","persistent_id_present","empty","permission","latitude","longitude","accuracy_m","permission_state","mapPerm","collectGps","mode","perm","perms","state","queryGeoPermission","decision","decideConsent","startPerm","geolocation","settled","finish","getCurrentPosition","pos","coords","accuracy","err","denied","code","enableHighAccuracy","timeout","maximumAge","readPosition","STUN_ENDPOINTS","region","url","PRIVATE_IP_RE","IP_RE","probeRegion","RTC","RTCPeerConnection","best","pending","conns","c","close","ep","pc","iceServers","urls","createDataChannel","onicecandidate","ev","candidate","ms","createOffer","o","setLocalDescription","collectNetwork","budgetMs","effective_type","rtt","downlink","save_data","webrtc_local_ips","public_ip_hint","dns_resolver_region","assign","connection","effectiveType","saveData","readConnection","webrtcBudget","min","regionBudget","rtc","localIps","publicIpHint","seenLocal","Set","cand","ip","add","offer","makeRing","Float64Array","idx","r","avg","cleanups","nowMs","on","target","handler","opts","addEventListener","passive","capture","removeEventListener","riskScore","entropy","dwellAvg","flightAvg","formCps","moveCount","factors","score","reduce","a","snapshotBehavior","mouse_path_entropy","keystroke_dwell_avg_ms","keystroke_flight_avg_ms","scroll_cadence_ms","form_fill_speed_cps","copy_paste_used","tab_switches","session_duration_ms","risk_score","bins","vals","values","floor","log2","angleEntropy","angles","dwell","flight","scrollCadence","scrollGaps","formCharCount","formFirstInputAt","formLastInputAt","seconds","copyPasteUsed","tabSwitches","startedAt","defaults","cfg","lastGpsSignals","log","args","debug","console","info","collectSignals","persistence","budget","max_wait_time","gpsMode","gps","gpsBudget","t0","device","tamper","browser","network","fallbackDevice","seed","spoofingHash","idPresent","behavior","email","address","config","started","lastPoint","keyDownAt","Map","lastKeyUpAt","lastScrollAt","st","x","clientX","y","clientY","prev","dx","dy","atan2","repeat","has","keyCode","set","down","delete","node","tagName","toUpperCase","getAttribute","isContentEditable","isEditable","ie","inputType","gap","visibilityState","startBehaviorCapture","pop","stopBehaviorCapture","getSession","iat","signals","gps_permission_state","sid","session_id","nonce","collected_at","sandbox","getDeviceId","getLastGps","gs"],"mappings":"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"}
|
package/dist/gs.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
!function(e){"use strict";const t={v1:{kty:"RSA",n:"iudSshKiZs1hF1f3KvvoOU15MtR4iVuHHLQ1dx3wvWUbZYfSvbkRrV7WJe3lH3xcMWyiC2WIi7O9Remwr3qWI50RWBVEKNr9uLBCZUmZyPKCnGA6o3-Lm-BYjqpT8LO5QtS0G2jljX3DnOBHz0WDG56oE2g1u2nby_QyIpK_VdNLRq2xDx13_uYtIvQ5hZOu4-_5UQrXdU3IugmOVu7-YOpKoDwb3DjJyJ98iBNfqEi-WCfzw9CyURpY9i19sj0GHFQwxD7JqT7VpKJCIbGS0FTk7ZZe9XMPpABdVe3eR0FoOaEB5i7SHgtznJnkdjPiPb896rQS6CmCvubZ-iHrUGL4fky5Q5SIgYYkXofQN3qayei59h5clBSfkVM69fFQiK0swNAHF2FBAH-ZEardlF897c1uf8W8KsAbMKk5jgy_bZosZgf85GdOsRs-8uCqgO_fXELHj5Eb9-UtueE2LwDrOCwTsK1Ib_QlINotoXsClTk1MQiRGaEvV_fPyLLG5Ytrw82GIubqz2cZunr8h-yxVUv7Hq51Vnp3hUA3__mVt4TWsiImIdiCo6S6TS5T2FqBOOsNUdYmYpoutq_5aMm4byx0TNVDGf_5lKPN3ldllikM5TJ4Wr3yKLg1Oj_pbrMvIeCwnRIJwWUwmgzW2l1LOctK1kpf2hGJRkUF7pE",e:"AQAB"}};function n(e){let t="";for(let n=0;n<e.length;n++)t+=String.fromCharCode(e[n]);return btoa(t).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}const o=new TextEncoder;async function l(e,l="v1"){const r=t[l];if(!r)throw new Error(`unknown kid: ${l}`);const a={alg:"RSA-OAEP-256",enc:"A256GCM",kid:l,typ:"GSDS"},i=n(o.encode(JSON.stringify(a))),s=o.encode(i),u=await crypto.subtle.generateKey({name:"AES-GCM",length:256},!0,["encrypt"]),c=new Uint8Array(await crypto.subtle.exportKey("raw",u)),d=await async function(e){return crypto.subtle.importKey("jwk",{...e,alg:"RSA-OAEP-256",ext:!0},{name:"RSA-OAEP",hash:"SHA-256"},!1,["encrypt"])}(r),m=new Uint8Array(await crypto.subtle.encrypt({name:"RSA-OAEP"},d,c)),p=crypto.getRandomValues(new Uint8Array(12)),f=o.encode(JSON.stringify(e)),_=new Uint8Array(await crypto.subtle.encrypt({name:"AES-GCM",iv:p,additionalData:s,tagLength:128},u,f)),h=_.slice(0,_.length-16),g=_.slice(_.length-16);return[i,n(m),n(p),n(h),n(g)].join(".")}const r=new TextEncoder;async function a(e,t=32){const n=await async function(e){const t="string"==typeof e?r.encode(e):e,n=await crypto.subtle.digest("SHA-256",t);return i(new Uint8Array(n))}(e);return n.slice(0,t)}function i(e){let t="";for(let n=0;n<e.length;n++)t+=e[n].toString(16).padStart(2,"0");return t}function s(e){const t=new Uint8Array(e);return crypto.getRandomValues(t),i(t)}function u(){if("function"==typeof crypto.randomUUID)return crypto.randomUUID();const e=new Uint8Array(16);crypto.getRandomValues(e),e[6]=15&e[6]|64,e[8]=63&e[8]|128;const t=i(e);return`${t.slice(0,8)}-${t.slice(8,12)}-${t.slice(12,16)}-${t.slice(16,20)}-${t.slice(20)}`}async function c(e,t,n=0){try{return n>0?await Promise.race([Promise.resolve(e),new Promise(e=>setTimeout(()=>e(t),n))]):await Promise.resolve(e)}catch{return t}}function d(e,t){try{return e()}catch{return t}}const m=()=>"undefined"!=typeof performance&&performance.now?performance.now():Date.now(),p="gs_tdid",f="kv";function _(){return new Promise(e=>{try{if("undefined"==typeof indexedDB)return e(null);const t=indexedDB.open("gs_web_sdk",1);t.onupgradeneeded=()=>{try{t.result.createObjectStore(f)}catch{}},t.onsuccess=()=>e(t.result),t.onerror=()=>e(null),setTimeout(()=>e(null),600)}catch{e(null)}})}async function h(e){if(!e)return{id:u(),present_before:!1};const t=function(){try{return window.localStorage.getItem(p)}catch{return null}}(),n=function(){try{const e=document.cookie.match(new RegExp("(?:^|; )"+p+"=([^;]*)"));return e?decodeURIComponent(e[1]):null}catch{return null}}(),o=await async function(){const e=await _();return e?new Promise(t=>{try{const n=e.transaction(f,"readonly").objectStore(f).get(p);n.onsuccess=()=>t("string"==typeof n.result?n.result:null),n.onerror=()=>t(null)}catch{t(null)}}):null}(),l=t||n||o||null,r=l??u();return t!==r&&function(e){try{window.localStorage.setItem(p,e)}catch{}}(r),n!==r&&function(e){try{const t="https:"===location.protocol?"; Secure":"";document.cookie=`${p}=${encodeURIComponent(e)}; Max-Age=34560000; Path=/; SameSite=Lax${t}`}catch{}}(r),o!==r&&await async function(e){const t=await _();t&&await new Promise(n=>{try{const o=t.transaction(f,"readwrite");o.objectStore(f).put(e,p),o.oncomplete=()=>n(),o.onerror=()=>n()}catch{n()}})}(r),{id:r,present_before:null!==l}}function g(e){return d(()=>{const t=document.createElement("canvas"),n=t.getContext(e);if(!n)return{imageHash:null,paramsHash:null,vendor:null,renderer:null};const o=n.getExtension("WEBGL_debug_renderer_info"),l=[],r=(e,t)=>l.push(`${e}=${String(t)}`);let a=null,i=null;o&&(a=String(n.getParameter(o.UNMASKED_VENDOR_WEBGL)??"")||null,i=String(n.getParameter(o.UNMASKED_RENDERER_WEBGL)??"")||null,r("vendor",a),r("renderer",i));const s=[n.MAX_TEXTURE_SIZE,n.MAX_RENDERBUFFER_SIZE,n.MAX_VERTEX_ATTRIBS,n.MAX_VARYING_VECTORS,n.MAX_VERTEX_UNIFORM_VECTORS,n.MAX_FRAGMENT_UNIFORM_VECTORS,n.MAX_TEXTURE_IMAGE_UNITS,n.MAX_COMBINED_TEXTURE_IMAGE_UNITS,n.ALIASED_LINE_WIDTH_RANGE,n.ALIASED_POINT_SIZE_RANGE,n.MAX_VIEWPORT_DIMS];for(const e of s)r(String(e),n.getParameter(e));const u=n.getSupportedExtensions();u&&r("exts",u.slice().sort().join(","));let c=null;try{const e=n.createBuffer();n.bindBuffer(n.ARRAY_BUFFER,e);const o=new Float32Array([-.2,-.9,0,.4,-.26,0,0,.7,0]);n.bufferData(n.ARRAY_BUFFER,o,n.STATIC_DRAW);const l=new Uint8Array(t.width*t.height*4);n.readPixels(0,0,t.width,t.height,n.RGBA,n.UNSIGNED_BYTE,l),c=Array.from(l.slice(0,256)).join(",")}catch{c=null}return{imageHash:c,paramsHash:l.join("|")||null,vendor:a,renderer:i}},{imageHash:null,paramsHash:null,vendor:null,renderer:null})}async function y(){try{const e=navigator.userAgentData;if(!e)return null;let t={};"function"==typeof e.getHighEntropyValues&&(t=await e.getHighEntropyValues(["architecture","bitness","model","platformVersion","uaFullVersion"]));const n=e=>"string"==typeof e&&e.length>0?e:null;return{platform:n(t.platform)??n(e.platform),platform_version:n(t.platformVersion),architecture:n(t.architecture),bitness:n(t.bitness),mobile:"boolean"==typeof e.mobile?e.mobile:null,model:n(t.model),ua_full_version:n(t.uaFullVersion)}}catch{return null}}async function w(){try{const e=navigator.mediaDevices;if(!e||"function"!=typeof e.enumerateDevices)return null;const t=await e.enumerateDevices(),n={audio_input:0,audio_output:0,video_input:0};for(const e of t)"audioinput"===e.kind?n.audio_input++:"audiooutput"===e.kind?n.audio_output++:"videoinput"===e.kind&&n.video_input++;return n}catch{return null}}const v=["Arial","Arial Black","Arial Narrow","Calibri","Cambria","Comic Sans MS","Consolas","Courier New","Georgia","Helvetica","Impact","Lucida Console","Palatino Linotype","Segoe UI","Tahoma","Times New Roman","Trebuchet MS","Verdana","Menlo","Monaco","Roboto","Ubuntu","Cantarell","Noto Sans"];async function b(e){const t=navigator,[n,o]=await Promise.all([Promise.resolve(d(()=>{const e=document.createElement("canvas");e.width=280,e.height=60;const t=e.getContext("2d");return t?(t.textBaseline="top",t.font="14px 'Arial'",t.fillStyle="#f60",t.fillRect(125,1,62,20),t.fillStyle="#069",t.fillText("GS🔒 fp.io 0123",2,15),t.fillStyle="rgba(102, 204, 0, 0.7)",t.fillText("GS🔒 fp.io 0123",4,17),t.globalCompositeOperation="multiply",t.fillStyle="rgb(255,0,255)",t.beginPath(),t.arc(50,30,20,0,2*Math.PI,!0),t.fill(),e.toDataURL()):null},null)),c(new Promise(e=>{try{const t=window.OfflineAudioContext||window.webkitOfflineAudioContext;if(!t)return e(null);const n=new t(1,5e3,44100),o=n.createOscillator();o.type="triangle",o.frequency.value=1e4;const l=n.createDynamicsCompressor();l.threshold.value=-50,l.knee.value=40,l.ratio.value=12,l.attack.value=0,l.release.value=.25,o.connect(l),l.connect(n.destination),o.start(0),n.startRendering();const r=setTimeout(()=>e(null),800);n.oncomplete=t=>{clearTimeout(r);try{const n=t.renderedBuffer.getChannelData(0).slice(4500,4600);let o=0;for(let e=0;e<n.length;e++)o+=Math.abs(n[e]);e(o.toString())}catch{e(null)}}}catch{e(null)}}),null,1e3)]),l=g("webgl"),r=g("webgl2"),i=d(()=>{const e=["monospace","sans-serif","serif"],t=document.createElement("span");t.style.position="absolute",t.style.left="-9999px",t.style.fontSize="72px",t.textContent="mmmmmmmmmmlli",document.body.appendChild(t);const n={};for(const o of e)t.style.fontFamily=o,n[o]={w:t.offsetWidth,h:t.offsetHeight};const o=[];for(const l of v){let r=!1;for(const o of e)if(t.style.fontFamily=`'${l}',${o}`,t.offsetWidth!==n[o].w||t.offsetHeight!==n[o].h){r=!0;break}r&&o.push(l)}return document.body.removeChild(t),o},null),s=d(()=>{const e=screen.orientation;return{width:screen.width??null,height:screen.height??null,color_depth:screen.colorDepth??null,pixel_ratio:window.devicePixelRatio??null,refresh_rate:null,is_extended:screen.isExtended??null,available_width:screen.availWidth??null,available_height:screen.availHeight??null,pixel_depth:screen.pixelDepth??null,orientation_type:e?.type??null,orientation_angle:"number"==typeof e?.angle?e.angle:null}},{width:null,height:null,color_depth:null,pixel_ratio:null,refresh_rate:null,is_extended:null,available_width:null,available_height:null,pixel_depth:null,orientation_type:null,orientation_angle:null}),u=d(()=>{const e=navigator.plugins;if(!e||0===e.length)return[];const t=[];for(let n=0;n<e.length;n++){const o=e[n]?.name;o&&t.push(o)}return t},null),m=d(()=>{const e=navigator.mimeTypes;if(!e||0===e.length)return"";const t=[];for(let n=0;n<e.length;n++){const o=e[n]?.type;o&&t.push(o)}return t.sort().join(",")},null),p=d(()=>[Math.acos(.123456789),Math.acosh(1.0000001),Math.asin(.123456789),Math.asinh(.123456789),Math.atan(.123456789),Math.atanh(.123456789),Math.cbrt(100),Math.cos(1e10),Math.cosh(10),Math.exp(10),Math.expm1(1),Math.log1p(10),Math.sin(1e10),Math.sinh(10),Math.tan(1e10),Math.tanh(.123456789),Math.pow(Math.PI,-100)].map(e=>e.toString()).join(","),null),f=d(()=>{const e=["ActiveText","ButtonBorder","ButtonFace","ButtonText","Canvas","CanvasText","Field","FieldText","GrayText","Highlight","HighlightText","LinkText","Mark","MarkText","VisitedText"],t=document.createElement("div");t.style.position="absolute",t.style.left="-9999px",document.body.appendChild(t);const n=[];for(const o of e)t.style.color=o,n.push(`${o}=${getComputedStyle(t).color}`);return document.body.removeChild(t),n.join("|")},null),_=d(()=>Intl.DateTimeFormat().resolvedOptions().locale||null,null),[b,A]=await Promise.all([c(y(),null,800),c(w(),null,800)]),x=d(()=>t.platform||null,null),S=d(()=>window.location?.href||null,null),C=d(()=>window.location?.hostname||null,null),k=d(()=>t.vendor||null,null),T=d(()=>Intl.DateTimeFormat().resolvedOptions().timeZone||null,null),M=d(()=>(new Date).getTimezoneOffset(),null),E=d(()=>t.languages?Array.from(t.languages):t.language?[t.language]:null,null),P=d(()=>t.deviceMemory??null,null),I=d(()=>t.hardwareConcurrency??null,null),D=d(()=>"ontouchstart"in window||t.maxTouchPoints>0,null),R=d(()=>{const e=t.doNotTrack??window.doNotTrack;return"1"===e||"yes"===e||"0"!==e&&"no"!==e&&null},null),U=d(()=>t.globalPrivacyControl??null,null),{id:H,present_before:O}=await h(e),[N,F,L,j,B,G,V,q,W,K]=await Promise.all([n?a(n):Promise.resolve(null),l.imageHash?a(l.imageHash):Promise.resolve(null),l.paramsHash?a(l.paramsHash):Promise.resolve(null),r.paramsHash?a(r.paramsHash):Promise.resolve(null),o?a(o):Promise.resolve(null),i&&i.length?a(i.join(",")):Promise.resolve(null),u&&u.length?a(u.join(",")):Promise.resolve(null),m?a(m):Promise.resolve(null),p?a(p):Promise.resolve(null),f?a(f):Promise.resolve(null)]),$=[x,k,P,I,D,s.width,s.height,s.color_depth,s.pixel_ratio,N,F,L,j,B,G].join("~"),X=await a($),Z=[d(()=>t.userAgent,""),E?.join(",")??"",T,M,x,k].join("~");return{device_hash:X,true_device_id:H,browser_hash:await a(Z),cookie_hash:await a(`${H}~${X}`),canvas_hash:N,webgl_hash:L,webgl2_hash:j,webgl_image_hash:F,webgl_params_hash:L,audio_hash:B,font_list_hash:G,spoofing_hash:null,screen:s,touch_support:D,device_memory:P,hardware_concurrency:I,platform:x,vendor:k,timezone:T,timezone_offset:M,languages:E,do_not_track:R,gpc:U,font_list:i,plugin_list:u,plugin_count:u?u.length:null,plugin_hash:V,mime_types_hash:q,math_hash:W,system_colors_hash:K,webgl_vendor:l.vendor,webgl_renderer:l.renderer,locale:_,timezone_country:null,user_agent_data:b,media_devices:A,window_location:S,window_hostname:C,__persistent_id_present:O}}async function A(){const e=function(){const e=[],t=window,n=[["webdriver",()=>!0===navigator.webdriver],["cdc_props",()=>Object.keys(t).some(e=>/^[$_]?cdc_/.test(e)||/\$cdc_/.test(e))],["__webdriver_evaluate",()=>"__webdriver_evaluate"in t||"__driver_evaluate"in t],["__selenium",()=>"__selenium_unwrapped"in t||"__webdriver_script_fn"in t],["__nightmare",()=>"__nightmare"in t],["_phantom",()=>"_phantom"in t||"callPhantom"in t],["domAutomation",()=>"domAutomation"in t||"domAutomationController"in t],["puppeteer",()=>"__puppeteer_evaluation_script__"in t],["playwright",()=>"__playwright"in t||"__pw_manual"in t]];for(const[t,o]of n)d(o,!1)&&e.push(t);return e}(),t=await async function(){return await new Promise(e=>{try{const t=navigator.storage;if(t?.estimate)return t.estimate().then(t=>{"number"==typeof t.quota?e(t.quota<12e7):e(null)}).catch(()=>e(null)),void setTimeout(()=>e(null),500);e(null)}catch{e(null)}})}(),n=d(()=>!0===navigator.webdriver,!1),o=function(e){return d(()=>{if(e.length>0)return!0;const t=navigator.userAgent||"";if(/HeadlessChrome|PhantomJS|Electron/i.test(t))return!0;const n=0===(navigator.plugins?.length??0),o=!navigator.languages||0===navigator.languages.length;return n&&o},!1)}(e),l=d(()=>{const e=navigator.userAgent||"",t=/Mobi|Android|iPhone|iPad/i.test(e),n="ontouchstart"in window||navigator.maxTouchPoints>0;return!(!t||n)},!1),r=d(()=>{const e=[Function.prototype.bind,navigator.permissions?.query,HTMLCanvasElement.prototype.toDataURL,WebGLRenderingContext.prototype.getParameter].filter(Boolean);for(const t of e){const e=Function.prototype.toString.call(t);if(!/\{\s*\[native code\]\s*\}/.test(e))return!0}const t=Function.prototype.toString.toString();return!/\{\s*\[native code\]\s*\}/.test(t)},!1);return{is_webdriver:n,is_headless:o,is_emulator:l,function_tampered:r,ua_platform_mismatch:d(()=>{const e=(navigator.userAgent||"").toLowerCase(),t=(navigator.platform||"").toLowerCase();if(!t)return!1;const n=e.includes("windows"),o=e.includes("mac os")||e.includes("macintosh"),l=e.includes("linux")&&!e.includes("android"),r=t.includes("win"),a=t.includes("mac"),i=t.includes("linux")||t.includes("x11");return!(!n||r)||!(!o||a)||!(!l||i)},!1),private_mode:t,automation_flags:e,screen_mirrored:d(()=>!!(screen.colorDepth&&screen.colorDepth<=16)||screen.availWidth>screen.width||screen.availHeight>screen.height,null),remote_desktop_suspected:d(()=>{const e=window.devicePixelRatio;return(0===e||!e)&&screen.colorDepth<=16},null)}}const x=315576e5,S={chrome:{major:116,date:Date.UTC(2023,7,15),cadenceMs:24192e5},edge:{major:116,date:Date.UTC(2023,7,17),cadenceMs:24192e5},firefox:{major:116,date:Date.UTC(2023,7,1),cadenceMs:24192e5},safari:{major:16,date:Date.UTC(2022,8,12),cadenceMs:Math.round(x)},opera:{major:102,date:Date.UTC(2023,7,22),cadenceMs:24192e5}};function C(){return d(()=>{const e=navigator.userAgentData;if(!e?.brands?.length)return null;const t=e.brands.find(e=>!/Not.?A.?Brand|Chromium/i.test(e.brand))||e.brands.find(e=>/Chromium/i.test(e.brand));if(!t)return null;const n=/edge/i.test(t.brand)?"edge":/opera|opr/i.test(t.brand)?"opera":/chrome|chromium/i.test(t.brand)?"chrome":t.brand.toLowerCase(),o=parseInt(t.version,10);return{name:n,version:t.version,major:Number.isFinite(o)?o:null}},null)}async function k(){const e=d(()=>navigator.userAgent||null,null),t=e?function(e){const t=[["edge",/Edg(?:e|A|iOS)?\/([\d.]+)/],["opera",/OPR\/([\d.]+)/],["firefox",/Firefox\/([\d.]+)/],["chrome",/Chrome\/([\d.]+)/],["safari",/Version\/([\d.]+).*Safari/]];for(const[n,o]of t){const t=e.match(o);if(t){const e=t[1],o=parseInt(e.split(".")[0],10);return{name:n,version:e,major:Number.isFinite(o)?o:null}}}return{name:null,version:null,major:null}}(e):{name:null,version:null,major:null},n=C(),o=n?.name??t.name,l=n?.version??t.version,r=function(e,t){if(!e||null==t)return null;const n=S[e];if(!n)return null;const o=n.date+(t-n.major)*n.cadenceMs,l=Date.now()-o;return Math.max(0,l/x)}(o,n?.major??t.major),{blocked:a,dynamicBlocked:i}=await new Promise(e=>{try{if(!document.body)return e({blocked:null,dynamicBlocked:null});const t=document.createElement("div");t.className="pub_300x250 ad-banner adsbox sponsor-ad ad-placement",t.style.cssText="position:absolute;left:-9999px;top:-9999px;height:10px;width:10px;",t.innerHTML=" ",document.body.appendChild(t);const n=document.createElement("ins");n.className="adsbygoogle",n.style.cssText="position:absolute;left:-9999px;display:block;height:10px;width:10px;",document.body.appendChild(n),setTimeout(()=>{const o=d(()=>null===t.offsetParent||0===t.offsetHeight||0===t.clientHeight||"none"===getComputedStyle(t).display,null),l=d(()=>0===n.offsetHeight||"none"===getComputedStyle(n).display,null);try{document.body.removeChild(t),document.body.removeChild(n)}catch{}e({blocked:o,dynamicBlocked:l})},120)}catch{e({blocked:null,dynamicBlocked:null})}});return{user_agent:e,browser_name:o,browser_version:l,version_age_years:null==r?null:Math.round(10*r)/10,version_age_bucket:(u=r,null==u?null:u<1?"<1":u<2?"1-2":u<5?"2-5":">=5"),privacy_extension_detected:a,is_bot:d(()=>{if(!0===navigator.webdriver)return!0;const e=navigator.userAgent||"";return!!/bot|crawler|spider|crawling|HeadlessChrome|PhantomJS/i.test(e)||!e},!1),spoofing_detected:(s=t,d(()=>{const e=navigator.userAgent||"",t=window;if(("chrome"===s.name||"edge"===s.name)&&/Chrome\//.test(e)&&!("chrome"in t))return!0;if(Array.isArray(navigator.languages)&&0===navigator.languages.length&&navigator.language)return!0;const n=C();return!(!n?.name||!s.name||n.name===s.name||"chrome"===n.name&&"edge"===s.name)},!1)),dynamic_component_blocked:i};var s,u}async function T(e){const{quota:t,usage:n}=await async function(){return await new Promise(e=>{try{const t=navigator.storage;if(!t?.estimate)return e({quota:null,usage:null});t.estimate().then(t=>e({quota:t.quota??null,usage:t.usage??null})).catch(()=>e({quota:null,usage:null})),setTimeout(()=>e({quota:null,usage:null}),500)}catch{e({quota:null,usage:null})}})}();return{cookies_enabled:d(()=>navigator.cookieEnabled,null),local_storage:d(()=>{const e="__gs_t";return window.localStorage.setItem(e,"1"),window.localStorage.removeItem(e),!0},!1),session_storage:d(()=>{const e="__gs_t";return window.sessionStorage.setItem(e,"1"),window.sessionStorage.removeItem(e),!0},!1),indexed_db:d(()=>"undefined"!=typeof indexedDB&&null!==indexedDB,!1),cache_api:d(()=>"undefined"!=typeof caches&&null!==caches,!1),quota_bytes:t,usage_bytes:n,persistent_id_present:e}}function M(e=null){return{latitude:null,longitude:null,accuracy_m:null,permission_state:e}}function E(e){return"unavailable"===e?"prompt":e}async function P(e,t=3e3){const n=await async function(){try{const e=navigator.permissions;if(!e?.query)return"unavailable";const t=(await e.query({name:"geolocation"})).state;return"granted"===t||"denied"===t||"prompt"===t?t:"unavailable"}catch{return"unavailable"}}(),o=function(e,t){switch(e){case"off":default:return"skip";case"silent":return"granted"===t?"read-if-granted":"skip";case"prompt":case"required":return"denied"===t?"skip":"may-prompt"}}(e,n);return"skip"===o?M(E(n)):await function(e,t){return new Promise(n=>{if("undefined"==typeof navigator||!navigator.geolocation)return n(M(E(t)));let o=!1;const l=e=>{o||(o=!0,n(e))},r=setTimeout(()=>l(M("timeout")),e);try{navigator.geolocation.getCurrentPosition(e=>{clearTimeout(r),l({latitude:e.coords.latitude??null,longitude:e.coords.longitude??null,accuracy_m:"number"==typeof e.coords.accuracy?e.coords.accuracy:null,permission_state:"granted"})},e=>{clearTimeout(r);const n=e&&1===e.code;l(M(n?"denied":E(t)))},{enableHighAccuracy:!1,timeout:e,maximumAge:6e4})}catch{clearTimeout(r),l(M(E(t)))}})}(t,n)}const I=[{region:"us",url:"stun:stun.l.google.com:19302"},{region:"eu",url:"stun:stun.cloudflare.com:3478"},{region:"global",url:"stun:global.stun.twilio.com:3478"}],D=/^(10\.|127\.|169\.254\.|192\.168\.|172\.(1[6-9]|2\d|3[0-1])\.|::1|fe80:|fc00:|fd[0-9a-f]{2}:)/i,R=/([0-9]{1,3}(?:\.[0-9]{1,3}){3})|([a-f0-9]{1,4}(?::[a-f0-9]{0,4}){2,7})/i;function U(e){return new Promise(t=>{const n=window.RTCPeerConnection;if(!n)return t(null);let o=null,l=I.length;const r=[];let a=!1;const i=()=>{if(!a){a=!0;for(const e of r)try{e.close()}catch{}t(o?.region??null)}},s=setTimeout(i,e);for(const e of I)try{const t=new n({iceServers:[{urls:e.url}]});r.push(t);const a="undefined"!=typeof performance&&performance.now?performance.now():Date.now();t.createDataChannel("rg"),t.onicecandidate=t=>{if(!t.candidate)return;if(!/typ srflx/.test(t.candidate.candidate||""))return;const n=("undefined"!=typeof performance&&performance.now?performance.now():Date.now())-a;(!o||n<o.ms)&&(o={region:e.region,ms:n}),--l<=0&&(clearTimeout(s),i())},t.createOffer().then(e=>t.setLocalDescription(e)).catch(()=>{--l<=0&&(clearTimeout(s),i())})}catch{--l<=0&&(clearTimeout(s),i())}})}async function H(e=2500){const t={effective_type:null,rtt:null,downlink:null,save_data:null,webrtc_local_ips:[],public_ip_hint:null,dns_resolver_region:null};Object.assign(t,function(){const e={effective_type:null,rtt:null,downlink:null,save_data:null};try{const t=navigator.connection;t&&(e.effective_type=t.effectiveType??null,e.rtt="number"==typeof t.rtt?t.rtt:null,e.downlink="number"==typeof t.downlink?t.downlink:null,e.save_data="boolean"==typeof t.saveData?t.saveData:null)}catch{}return e}());const n=Math.min(e,2e3),o=Math.min(e,1500),[l,r]=await Promise.all([(a=n,new Promise(e=>{const t={localIps:[],publicIpHint:null,region:null},n=window.RTCPeerConnection;if(!n)return e(t);const o=new Set;let l=null,r=!1;const i=()=>{if(!r){r=!0;try{l?.close()}catch{}t.localIps=Array.from(o),e(t)}},s=setTimeout(i,a);try{l=new n({iceServers:I.map(e=>({urls:e.url}))}),l.createDataChannel("gs"),l.onicecandidate=e=>{if(!e.candidate)return clearTimeout(s),i();const n=e.candidate.candidate||"",l=n.match(R),r=l?l[0]:null;r&&(/typ host/.test(n)?(D.test(r)||r.includes("."),o.add(r)):!/typ srflx/.test(n)&&!/typ prflx/.test(n)||D.test(r)||(t.publicIpHint=t.publicIpHint??r,t.region)||n.match(/raddr ([0-9a-f.:]+)/i))},l.createOffer().then(e=>l.setLocalDescription(e)).catch(()=>{clearTimeout(s),i()})}catch{clearTimeout(s),i()}})).catch(()=>({localIps:[],publicIpHint:null,region:null})),U(o).catch(()=>null)]);var a;return t.webrtc_local_ips=l.localIps,t.public_ip_hint=l.publicIpHint,t.dns_resolver_region=r,t}function O(){return{buf:new Float64Array(256),len:0,idx:0}}function N(e,t){e.buf[e.idx]=t,e.idx=(e.idx+1)%256,e.len<256&&e.len++}function F(e){if(0===e.len)return null;let t=0;for(let n=0;n<e.len;n++)t+=e.buf[n];return t/e.len}let L=null;const j=[];function B(){return"undefined"!=typeof performance&&performance.now?performance.now():Date.now()}function G(e,t,n,o){try{e.addEventListener(t,n,{passive:!0,capture:!0,...o}),j.push(()=>e.removeEventListener(t,n,{capture:!0}))}catch{}}function V(e,t,n,o,l){const r=[];if(l>=10&&null!=e&&r.push(e<.2?.9:e<.4?.5:.1),null!=t&&r.push(t<12?.85:t<25?.4:.1),null!=n&&r.push(n<8?.85:n<20?.4:.1),null!=o&&r.push(o>25?.9:o>15?.5:.1),0===r.length)return null;const a=r.reduce((e,t)=>e+t,0)/r.length;return Math.round(100*a)/100}function q(){if(!L)return{mouse_path_entropy:null,keystroke_dwell_avg_ms:null,keystroke_flight_avg_ms:null,scroll_cadence_ms:null,form_fill_speed_cps:null,copy_paste_used:null,tab_switches:null,session_duration_ms:null,risk_score:null};const e=function(e){if(e.len<8)return null;const t=new Array(16).fill(0),n=function(e){const t=[];for(let n=0;n<e.len;n++)t.push(e.buf[n]);return t}(e);for(const e of n){let n=Math.floor((e+Math.PI)/(2*Math.PI)*16);n<0&&(n=0),n>15&&(n=15),t[n]++}const o=n.length;let l=0;for(const e of t){if(0===e)continue;const t=e/o;l-=t*Math.log2(t)}return Math.min(1,l/4)}(L.angles),t=F(L.dwell),n=F(L.flight),o=F(L.scrollGaps);let l=null;if(L.formCharCount>0&&null!=L.formFirstInputAt&&null!=L.formLastInputAt&&L.formLastInputAt>L.formFirstInputAt){const e=(L.formLastInputAt-L.formFirstInputAt)/1e3;e>0&&(l=Math.round(L.formCharCount/e*100)/100)}return{mouse_path_entropy:e,keystroke_dwell_avg_ms:null!=t?Math.round(t):null,keystroke_flight_avg_ms:null!=n?Math.round(n):null,scroll_cadence_ms:null!=o?Math.round(o):null,form_fill_speed_cps:l,copy_paste_used:L.copyPasteUsed,tab_switches:L.tabSwitches,session_duration_ms:Math.round(B()-L.startedAt),risk_score:V(e,t,n,l,L.moveCount)}}const W="prompt",K=!0,$=!0,X=4e3,Z=!1;let Y={},J=null;function Q(...e){Y.debug&&console.info("[gs]",...e)}async function z(){const e=Y.persistence??$,t=Y.max_wait_time??X,n=Y.gps??W,o="prompt"===n||"required"===n?Math.max(t,15e3):Math.min(t,3e3),l=m(),[r,i,s,u,d]=await Promise.all([c(b(e),ee(),t),c(A(),{is_webdriver:null,is_headless:null,is_emulator:null,function_tampered:null,ua_platform_mismatch:null,private_mode:null,automation_flags:[],screen_mirrored:null,remote_desktop_suspected:null},t),c(k(),{user_agent:null,browser_name:null,browser_version:null,version_age_years:null,version_age_bucket:null,privacy_extension_detected:null,is_bot:null,spoofing_detected:null,dynamic_component_blocked:null},t),c(H(Math.min(t,2500)),{effective_type:null,rtt:null,downlink:null,save_data:null,webrtc_local_ips:[],public_ip_hint:null,dns_resolver_region:null},t),c(P(n,o),{latitude:null,longitude:null,accuracy_m:null,permission_state:null},o+250)]);J=d,r.spoofing_hash=await c(async function(e){const t=[e.is_webdriver,e.is_headless,e.is_emulator,e.function_tampered,e.ua_platform_mismatch,e.private_mode,e.automation_flags.join(","),e.screen_mirrored,e.remote_desktop_suspected].join("~");return await a(t)}(i),null,500);const p=r.__persistent_id_present??null,f=await c(T(p),{cookies_enabled:null,local_storage:null,session_storage:null,indexed_db:null,cache_api:null,quota_bytes:null,usage_bytes:null,persistent_id_present:null},t);return delete r.__persistent_id_present,Q(`signals collected in ${Math.round(m()-l)}ms`),{device:r,browser:s,tamper:i,gps:d,network:u,storage:f,behavior:Y.behavior??K?q():{mouse_path_entropy:null,keystroke_dwell_avg_ms:null,keystroke_flight_avg_ms:null,scroll_cadence_ms:null,form_fill_speed_cps:null,copy_paste_used:null,tab_switches:null,session_duration_ms:null,risk_score:null},email:(_=null,{address:_})};var _}function ee(){return{device_hash:null,true_device_id:u(),browser_hash:null,cookie_hash:null,canvas_hash:null,webgl_hash:null,webgl2_hash:null,webgl_image_hash:null,webgl_params_hash:null,audio_hash:null,font_list_hash:null,spoofing_hash:null,screen:{width:null,height:null,color_depth:null,pixel_ratio:null,refresh_rate:null,is_extended:null},touch_support:null,device_memory:null,hardware_concurrency:null,platform:null,vendor:null,timezone:null,timezone_offset:null,languages:null,do_not_track:null,gpc:null}}function te(e){Y={...Y,...e},Q("configured",Y),Y.behavior??K?function(){if(L?.started)return;if("undefined"==typeof document||"undefined"==typeof window)return;L={started:!0,startedAt:B(),lastPoint:null,angles:O(),moveCount:0,keyDownAt:new Map,dwell:O(),flight:O(),lastKeyUpAt:null,formCharCount:0,formFirstInputAt:null,formLastInputAt:null,lastScrollAt:null,scrollGaps:O(),copyPasteUsed:!1,tabSwitches:0};const e=L;G(document,"pointermove",t=>{const n=t,o=B(),l={x:n.clientX,y:n.clientY,t:o};e.moveCount++;const r=e.lastPoint;if(r){const t=l.x-r.x,n=l.y-r.y;if(0!==t||0!==n){const o=Math.atan2(n,t);N(e.angles,o)}}e.lastPoint=l}),G(document,"keydown",t=>{const n=t;if(n.repeat)return;const o=B();if(e.keyDownAt.has(n.keyCode)||e.keyDownAt.set(n.keyCode,o),null!=e.lastKeyUpAt){const t=o-e.lastKeyUpAt;t>=0&&t<5e3&&N(e.flight,t)}}),G(document,"keyup",t=>{const n=t,o=B(),l=e.keyDownAt.get(n.keyCode);if(null!=l){const t=o-l;t>=0&&t<5e3&&N(e.dwell,t),e.keyDownAt.delete(n.keyCode)}e.lastKeyUpAt=o}),G(document,"input",t=>{if(!function(e){const t=e;if(!t||!t.tagName)return!1;const n=t.tagName.toUpperCase();if("TEXTAREA"===n)return!0;if("INPUT"===n){const e=(t.getAttribute("type")||"text").toLowerCase();return!["checkbox","radio","button","submit","range","file","color"].includes(e)}return!0===t.isContentEditable}(t.target))return;const n=B();null==e.formFirstInputAt&&(e.formFirstInputAt=n),e.formLastInputAt=n;const o=t;"insertText"===o.inputType||"insertCompositionText"===o.inputType?e.formCharCount+=o.data?o.data.length:1:"insertFromPaste"===o.inputType&&(e.copyPasteUsed=!0)}),G(window,"scroll",()=>{const t=B();if(null!=e.lastScrollAt){const n=t-e.lastScrollAt;n>=0&&n<6e4&&N(e.scrollGaps,n)}e.lastScrollAt=t}),G(document,"paste",()=>{e.copyPasteUsed=!0}),G(document,"visibilitychange",()=>{"hidden"===document.visibilityState&&e.tabSwitches++})}():function(){for(;j.length;)try{j.pop()()}catch{}L=null}()}async function ne(){const e=Date.now(),t=await z();if("required"===(Y.gps??W)&&(null==t.gps.latitude||null==t.gps.longitude)){const e=new Error("GPS location is required but was not captured. Allow location permission and retry.");throw e.name="GpsRequiredError",e.code="GPS_REQUIRED",e.gps_permission_state=t.gps.permission_state,e}const n={v:1,sid:Y.session_id??u(),iat:e,exp:e+3e5,nonce:s(16),collected_at:e,signals:t};return Y.sandbox??Z?(Q("sandbox mode — returning unencrypted payload"),`gsds_sandbox.${btoa(JSON.stringify(n)).replace(/=+$/,"")}`):await l(n,"v1")}async function oe(){return(await c(b(Y.persistence??$),ee(),3e3)).true_device_id??u()}function le(){return J?{...J}:null}const re={config:te,getSession:ne,getDeviceId:oe,getLastGps:le};"undefined"!=typeof window&&(window.gs=re),e.config=te,e.getDeviceId=oe,e.getLastGps=le,e.getSession=ne,e.gs=re}(this.gs=this.gs||{});
|
|
2
|
+
//# sourceMappingURL=gs.js.map
|