@niksbanna/bot-detector 1.0.0 → 1.0.3

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.
@@ -1 +1,3 @@
1
- var BotDetectorLib=(()=>{var $=Object.defineProperty;var nt=Object.getOwnPropertyDescriptor;var st=Object.getOwnPropertyNames;var ot=Object.prototype.hasOwnProperty;var at=(g,e,t)=>e in g?$(g,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):g[e]=t;var rt=(g,e)=>{for(var t in e)$(g,t,{get:e[t],enumerable:!0})},ct=(g,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of st(e))!ot.call(g,i)&&i!==t&&$(g,i,{get:()=>e[i],enumerable:!(n=nt(e,i))||n.enumerable});return g};var ht=g=>ct($({},"__esModule",{value:!0}),g);var o=(g,e,t)=>at(g,typeof e!="symbol"?e+"":e,t);var dt={};rt(dt,{AudioContextSignal:()=>I,BotDetector:()=>j,CanvasSignal:()=>P,DOMContentTimingSignal:()=>O,HeadlessSignal:()=>C,InteractionTimingSignal:()=>b,KeyboardPatternSignal:()=>M,MouseMovementSignal:()=>y,NavigatorAnomalySignal:()=>R,PageLoadSignal:()=>A,PermissionsSignal:()=>D,PhantomJSSignal:()=>q,PlaywrightSignal:()=>V,PluginsSignal:()=>S,PuppeteerSignal:()=>B,ScoringEngine:()=>U,ScreenSignal:()=>H,ScrollBehaviorSignal:()=>T,SeleniumSignal:()=>K,Signal:()=>m,Signals:()=>tt,Verdict:()=>z,VerdictEngine:()=>N,WebDriverSignal:()=>E,WebGLSignal:()=>L,createDetector:()=>Z,default:()=>ut,defaultInstantSignals:()=>G,defaultInteractionSignals:()=>Q,defaultSignals:()=>lt,detect:()=>J,detectInstant:()=>et});var m=class{constructor(e={}){this.options=e,this._lastResult=null}get id(){return this.constructor.id}get category(){return this.constructor.category}get weight(){var e;return(e=this.options.weight)!=null?e:this.constructor.weight}get description(){return this.constructor.description}get requiresInteraction(){return this.constructor.requiresInteraction}get lastResult(){return this._lastResult}async detect(){throw new Error(`Signal.detect() must be implemented by ${this.constructor.name}`)}async run(){try{return this._lastResult=await this.detect(),this._lastResult}catch(e){return this._lastResult={triggered:!1,value:null,confidence:0,error:e.message},this._lastResult}}reset(){this._lastResult=null}createResult(e,t=null,n=1){return{triggered:!!e,value:t,confidence:Math.max(0,Math.min(1,n))}}};o(m,"id","base-signal"),o(m,"category","uncategorized"),o(m,"weight",.5),o(m,"description","Base signal class"),o(m,"requiresInteraction",!1);var U=class{constructor(e={}){this.weightOverrides=e.weightOverrides||{},this.maxScore=e.maxScore||100,this._results=new Map}getWeight(e,t){var n;return(n=this.weightOverrides[e])!=null?n:t}addResult(e,t,n){let i=this.getWeight(e,n);this._results.set(e,{...t,weight:i,contribution:t.triggered?i*t.confidence:0})}calculate(){if(this._results.size===0)return 0;let e=0,t=0;for(let[,i]of this._results)e+=i.weight,t+=i.contribution;if(e===0)return 0;let n=t/e*this.maxScore;return Math.round(n*100)/100}getBreakdown(){let e=[];for(let[t,n]of this._results)e.push({signalId:t,triggered:n.triggered,confidence:n.confidence,weight:n.weight,contribution:n.contribution,percentOfScore:this.calculate()>0?(n.contribution/this.calculate()*100).toFixed(1):"0.0"});return e.sort((t,n)=>n.contribution-t.contribution)}getTriggeredSignals(){let e=[];for(let[t,n]of this._results)n.triggered&&e.push(t);return e}getTriggeredCount(){let e=0;for(let[,t]of this._results)t.triggered&&e++;return e}reset(){this._results.clear()}};var z={HUMAN:"human",SUSPICIOUS:"suspicious",BOT:"bot"},F=class F{constructor(e={}){var t,n;this.humanThreshold=(t=e.humanThreshold)!=null?t:F.DEFAULT_THRESHOLDS.human,this.suspiciousThreshold=(n=e.suspiciousThreshold)!=null?n:F.DEFAULT_THRESHOLDS.suspicious,this.instantBotSignals=new Set(e.instantBotSignals||[])}getVerdict(e,t=[]){for(let c of t)if(this.instantBotSignals.has(c))return{verdict:z.BOT,score:e,confidence:"high",reason:`Instant bot signal triggered: ${c}`,triggeredCount:t.length};let n,i,s;return e<this.humanThreshold?(n=z.HUMAN,i=e<10?"high":"medium",s="Low bot score"):e<this.suspiciousThreshold?(n=z.SUSPICIOUS,i="medium",s="Moderate bot indicators detected"):(n=z.BOT,i=e>=75?"high":"medium",s="High accumulation of bot indicators"),{verdict:n,score:e,confidence:i,reason:s,triggeredCount:t.length}}isInstantBotSignal(e){return this.instantBotSignals.has(e)}addInstantBotSignal(e){this.instantBotSignals.add(e)}setThresholds(e){e.human!==void 0&&(this.humanThreshold=e.human),e.suspicious!==void 0&&(this.suspiciousThreshold=e.suspicious)}};o(F,"DEFAULT_THRESHOLDS",{human:20,suspicious:50});var N=F;var j=class g{constructor(e={}){if(this.options=e,this._signals=new Map,this._scoringEngine=new U({weightOverrides:e.weightOverrides}),this._verdictEngine=new N({humanThreshold:e.humanThreshold,suspiciousThreshold:e.suspiciousThreshold,instantBotSignals:e.instantBotSignals}),this._lastDetection=null,this._detectionTimeout=e.detectionTimeout||5e3,this._isRunning=!1,e.signals)for(let t of e.signals)this.registerSignal(t)}registerSignal(e){if(!(e instanceof m))throw new Error("Signal must be an instance of Signal class");let t=e.id;if(this._signals.has(t))throw new Error(`Signal with ID "${t}" is already registered`);return this._signals.set(t,e),this}registerSignals(e){for(let t of e)this.registerSignal(t);return this}unregisterSignal(e){return this._signals.delete(e)}getSignal(e){return this._signals.get(e)}getSignals(){return Array.from(this._signals.values())}getSignalsByCategory(e){return this.getSignals().filter(t=>t.category===e)}async detect(e={}){if(this._isRunning)throw new Error("Detection is already running");this._isRunning=!0,this._scoringEngine.reset();let t=performance.now(),n=new Map;try{let i=this.getSignals().filter(u=>!(e.skipInteractionSignals&&u.requiresInteraction)),s=i.map(async u=>{let l=await Promise.race([u.run(),new Promise(h=>setTimeout(()=>h({triggered:!1,value:null,confidence:0,error:"timeout"}),this._detectionTimeout))]);return{signal:u,result:l}}),c=await Promise.all(s);for(let{signal:u,result:l}of c)n.set(u.id,{...l,category:u.category,weight:u.weight,description:u.description}),this._scoringEngine.addResult(u.id,l,u.weight);let r=this._scoringEngine.calculate(),a=this._scoringEngine.getTriggeredSignals(),p=this._verdictEngine.getVerdict(r,a),d=performance.now()-t;return this._lastDetection={...p,signals:Object.fromEntries(n),breakdown:this._scoringEngine.getBreakdown(),timestamp:Date.now(),detectionTimeMs:Math.round(d),totalSignals:i.length,triggeredSignals:a},this._lastDetection}finally{this._isRunning=!1}}getLastDetection(){return this._lastDetection}getScore(){var e,t;return(t=(e=this._lastDetection)==null?void 0:e.score)!=null?t:0}getTriggeredSignals(){var e,t;return(t=(e=this._lastDetection)==null?void 0:e.triggeredSignals)!=null?t:[]}isRunning(){return this._isRunning}reset(){this._scoringEngine.reset(),this._lastDetection=null;for(let e of this._signals.values())e.reset()}configure(e){(e.humanThreshold!==void 0||e.suspiciousThreshold!==void 0)&&this._verdictEngine.setThresholds({human:e.humanThreshold,suspicious:e.suspiciousThreshold}),e.detectionTimeout!==void 0&&(this._detectionTimeout=e.detectionTimeout)}static withDefaults(e={}){return new g(e)}};var E=class extends m{async detect(){if(navigator.webdriver===!0)return this.createResult(!0,{webdriver:!0},1);let e=Object.getOwnPropertyDescriptor(navigator,"webdriver");if(e&&(e.get||!e.configurable))return this.createResult(!0,{webdriver:"modified",descriptor:{configurable:e.configurable,enumerable:e.enumerable,hasGetter:!!e.get}},.8);try{let t=Object.getPrototypeOf(navigator),n=Object.getOwnPropertyDescriptor(t,"webdriver");if(n&&n.get&&n.get.call(navigator)===!0)return this.createResult(!0,{webdriver:!0,source:"prototype"},1)}catch{}return this.createResult(!1)}};o(E,"id","webdriver"),o(E,"category","environment"),o(E,"weight",1),o(E,"description","Detects navigator.webdriver automation flag");var C=class extends m{async detect(){let e=[],t=0,n=navigator.userAgent||"";n.includes("HeadlessChrome")&&(e.push("headless-ua"),t=Math.max(t,1)),n.includes("Chrome")&&!n.includes("Chromium")&&(typeof window.chrome>"u"?(e.push("missing-chrome-object"),t=Math.max(t,.6)):window.chrome.runtime||(e.push("missing-chrome-runtime"),t=Math.max(t,.4))),navigator.plugins&&navigator.plugins.length===0&&(e.push("no-plugins"),t=Math.max(t,.5)),(!navigator.languages||navigator.languages.length===0)&&(e.push("no-languages"),t=Math.max(t,.6)),window.outerWidth===0&&window.outerHeight===0&&(e.push("zero-outer-dimensions"),t=Math.max(t,.7)),typeof navigator.connection>"u"&&n.includes("Chrome")&&(e.push("missing-connection-api"),t=Math.max(t,.3));try{typeof Notification<"u"&&Notification.permission==="denied"&&window.outerWidth===0&&(e.push("notification-headless-pattern"),t=Math.max(t,.5))}catch{}(window.callPhantom||window._phantom)&&(e.push("phantomjs"),t=Math.max(t,1)),window.__nightmare&&(e.push("nightmare"),t=Math.max(t,1));let i=e.length>0;return e.length>=3&&(t=Math.min(1,t+.2)),this.createResult(i,{indicators:e},t)}};o(C,"id","headless"),o(C,"category","environment"),o(C,"weight",.8),o(C,"description","Detects headless browser indicators");var R=class extends m{async detect(){let e=[],t=0,n=0,i=navigator.userAgent||"",s=navigator.platform||"";n++,s.includes("Win")&&!i.includes("Windows")?(e.push("platform-ua-mismatch-windows"),t+=1):s.includes("Mac")&&!i.includes("Mac")?(e.push("platform-ua-mismatch-mac"),t+=1):s.includes("Linux")&&!i.includes("Linux")&&!i.includes("Android")&&(e.push("platform-ua-mismatch-linux"),t+=1),n++,(!s||s===""||s==="undefined")&&(e.push("empty-platform"),t+=1),n++,navigator.language&&navigator.languages&&(navigator.languages.includes(navigator.language)||(e.push("language-mismatch"),t+=.5)),n++,i.includes("Chrome")&&navigator.vendor!=="Google Inc."?(e.push("vendor-mismatch-chrome"),t+=.5):i.includes("Firefox")&&navigator.vendor!==""?(e.push("vendor-mismatch-firefox"),t+=.5):i.includes("Safari")&&!i.includes("Chrome")&&navigator.vendor!=="Apple Computer, Inc."&&(e.push("vendor-mismatch-safari"),t+=.5),n++,typeof navigator.hardwareConcurrency<"u"&&(navigator.hardwareConcurrency===0||navigator.hardwareConcurrency>128)&&(e.push("suspicious-hardware-concurrency"),t+=.5),n++,typeof navigator.deviceMemory<"u"&&(navigator.deviceMemory===0||navigator.deviceMemory>512)&&(e.push("suspicious-device-memory"),t+=.5),n++;let c=/Android|iPhone|iPad|iPod|Mobile/i.test(i),r=navigator.maxTouchPoints>0;!c&&navigator.maxTouchPoints>5&&(e.push("desktop-high-touch-points"),t+=.3),n++;try{let d=Object.getOwnPropertyDescriptor(Navigator.prototype,"userAgent");d&&d.get&&d.get.toString().includes("native code")===!1&&(e.push("spoofed-user-agent"),t+=1)}catch{}let a=e.length>0,p=Math.min(1,t/Math.max(1,n));return this.createResult(a,{anomalies:e},p)}};o(R,"id","navigator-anomaly"),o(R,"category","environment"),o(R,"weight",.7),o(R,"description","Detects navigator property inconsistencies");var D=class extends m{async detect(){let e=[];if(!navigator.permissions)return this.createResult(!1,{supported:!1},0);try{let i=await navigator.permissions.query({name:"notifications"});if(typeof Notification<"u"){let s=Notification.permission;(s==="granted"&&i.state!=="granted"||s==="denied"&&i.state!=="denied"||s==="default"&&i.state!=="prompt")&&e.push("notification-permission-mismatch")}try{(await navigator.permissions.query({name:"geolocation"})).state==="denied"&&window.outerWidth===0&&e.push("geo-denied-headless")}catch{}try{await navigator.permissions.query({name:"camera"})}catch(s){s.name==="TypeError"&&e.push("camera-permission-error")}}catch(i){i.name!=="TypeError"&&e.push("permissions-query-error")}let t=e.length>0,n=Math.min(1,e.length*.4);return this.createResult(t,{anomalies:e},n)}};o(D,"id","permissions"),o(D,"category","environment"),o(D,"weight",.5),o(D,"description","Detects Permissions API anomalies");var y=class extends m{constructor(e={}){super(e),this._movements=[],this._isTracking=!1,this._trackingDuration=e.trackingDuration||3e3,this._minMovements=e.minMovements||5,this._boundHandler=null}startTracking(){this._isTracking||(this._movements=[],this._isTracking=!0,this._boundHandler=e=>{this._movements.push({x:e.clientX,y:e.clientY,t:performance.now()})},document.addEventListener("mousemove",this._boundHandler,{passive:!0}))}stopTracking(){this._isTracking&&(this._isTracking=!1,this._boundHandler&&(document.removeEventListener("mousemove",this._boundHandler),this._boundHandler=null))}async detect(){let e=[],t=0;this._movements.length===0&&(this.startTracking(),await new Promise(c=>setTimeout(c,this._trackingDuration)),this.stopTracking());let n=this._movements;if(n.length<this._minMovements)return e.push("no-mouse-movement"),t=Math.max(t,.6),this.createResult(!0,{anomalies:e,movements:n.length},t);let i=this._analyzeMovements(n);i.teleportCount>0&&(e.push("mouse-teleportation"),t=Math.max(t,.7)),i.linearPathRatio>.9&&(e.push("linear-path"),t=Math.max(t,.8)),i.velocityVariance<.01&&n.length>10&&(e.push("constant-velocity"),t=Math.max(t,.7)),i.accelerationChanges===0&&n.length>10&&(e.push("no-acceleration-variance"),t=Math.max(t,.6)),i.timingVariance<1&&n.length>10&&(e.push("robotic-timing"),t=Math.max(t,.8));let s=e.length>0;return this.createResult(s,{anomalies:e,movementCount:n.length,analysis:i},t)}_analyzeMovements(e){if(e.length<3)return{teleportCount:0,linearPathRatio:0,velocityVariance:0,accelerationChanges:0,timingVariance:0};let t=0,n=[],i=[],s=[];for(let l=1;l<e.length;l++){let h=e[l-1],f=e[l],x=f.x-h.x,_=f.y-h.y,w=f.t-h.t;if(w===0)continue;let k=Math.sqrt(x*x+_*_),v=k/w;n.push(v),i.push(Math.atan2(_,x)),s.push(w),k>300&&w<10&&t++}let c=n.reduce((l,h)=>l+h,0)/n.length,r=n.reduce((l,h)=>l+Math.pow(h-c,2),0)/n.length,a=0;if(i.length>1){let l=0;for(let h=1;h<i.length;h++)Math.abs(i[h]-i[h-1])<.1&&l++;a=l/(i.length-1)}let p=s.reduce((l,h)=>l+h,0)/s.length,d=s.reduce((l,h)=>l+Math.pow(h-p,2),0)/s.length,u=0;for(let l=1;l<n.length;l++)(n[l]-n[l-1])*(n[l-1]-(n[l-2]||0))<0&&u++;return{teleportCount:t,linearPathRatio:a,velocityVariance:r,accelerationChanges:u,timingVariance:d}}reset(){super.reset(),this.stopTracking(),this._movements=[]}};o(y,"id","mouse-movement"),o(y,"category","behavior"),o(y,"weight",.9),o(y,"description","Detects non-human mouse movement patterns"),o(y,"requiresInteraction",!0);var M=class extends m{constructor(e={}){super(e),this._keystrokes=[],this._isTracking=!1,this._trackingDuration=e.trackingDuration||5e3,this._minKeystrokes=e.minKeystrokes||10,this._boundKeydownHandler=null,this._boundKeyupHandler=null}startTracking(){this._isTracking||(this._keystrokes=[],this._isTracking=!0,this._boundKeydownHandler=e=>{this._keystrokes.push({type:"down",key:e.key,code:e.code,t:performance.now()})},this._boundKeyupHandler=e=>{this._keystrokes.push({type:"up",key:e.key,code:e.code,t:performance.now()})},document.addEventListener("keydown",this._boundKeydownHandler,{passive:!0}),document.addEventListener("keyup",this._boundKeyupHandler,{passive:!0}))}stopTracking(){this._isTracking&&(this._isTracking=!1,this._boundKeydownHandler&&(document.removeEventListener("keydown",this._boundKeydownHandler),this._boundKeydownHandler=null),this._boundKeyupHandler&&(document.removeEventListener("keyup",this._boundKeyupHandler),this._boundKeyupHandler=null))}async detect(){let e=[],t=0;this._keystrokes.length===0&&(this.startTracking(),await new Promise(r=>setTimeout(r,this._trackingDuration)),this.stopTracking());let n=this._keystrokes,i=n.filter(r=>r.type==="down");if(i.length<this._minKeystrokes)return this.createResult(!1,{reason:"insufficient-data",keystrokes:i.length},0);let s=this._analyzeKeystrokes(n);s.avgInterKeystrokeTime<50&&i.length>20&&(e.push("inhuman-speed"),t=Math.max(t,.9)),s.timingVariance<5&&i.length>15&&(e.push("robotic-timing"),t=Math.max(t,.8)),s.missingKeyups>i.length*.5&&(e.push("missing-keyups"),t=Math.max(t,.7)),s.holdTimeVariance<2&&s.holdTimes.length>10&&(e.push("constant-hold-time"),t=Math.max(t,.6)),s.sequentialKeys>i.length*.8&&i.length>10&&(e.push("sequential-input"),t=Math.max(t,.5)),s.rhythmScore<.1&&i.length>20&&(e.push("no-rhythm-variation"),t=Math.max(t,.6));let c=e.length>0;return this.createResult(c,{anomalies:e,keystrokeCount:i.length,analysis:s},t)}_analyzeKeystrokes(e){let t=e.filter(h=>h.type==="down"),n=e.filter(h=>h.type==="up");if(t.length<2)return{avgInterKeystrokeTime:1/0,timingVariance:1/0,missingKeyups:0,holdTimeVariance:1/0,holdTimes:[],sequentialKeys:0,rhythmScore:1};let i=[];for(let h=1;h<t.length;h++)i.push(t[h].t-t[h-1].t);let s=i.reduce((h,f)=>h+f,0)/i.length,c=i.reduce((h,f)=>h+Math.pow(f-s,2),0)/i.length,r=[];for(let h of t){let f=n.find(x=>x.key===h.key&&x.t>h.t);f&&r.push(f.t-h.t)}let a=r.length>0?r.reduce((h,f)=>h+f,0)/r.length:0,p=r.length>0?r.reduce((h,f)=>h+Math.pow(f-a,2),0)/r.length:1/0,d=t.length-r.length,u=0;for(let h=1;h<t.length;h++){let f=t[h-1].key.charCodeAt(0),x=t[h].key.charCodeAt(0);Math.abs(x-f)===1&&u++}let l=0;if(i.length>5){let h=[...i].sort((_,w)=>_-w),f=h[Math.floor(h.length/2)];l=i.filter(_=>Math.abs(_-f)>f*.3).length/i.length}return{avgInterKeystrokeTime:s,timingVariance:c,missingKeyups:d,holdTimeVariance:p,holdTimes:r,sequentialKeys:u,rhythmScore:l}}reset(){super.reset(),this.stopTracking(),this._keystrokes=[]}};o(M,"id","keyboard-pattern"),o(M,"category","behavior"),o(M,"weight",.8),o(M,"description","Detects non-human keystroke patterns"),o(M,"requiresInteraction",!0);var b=class extends m{constructor(e={}){super(e),this._pageLoadTime=performance.now(),this._firstInteractionTime=null,this._interactions=[],this._isTracking=!1,this._trackingDuration=e.trackingDuration||5e3,this._boundHandler=null}startTracking(){if(this._isTracking)return;this._interactions=[],this._isTracking=!0;let e=["click","mousedown","touchstart","keydown","scroll"];this._boundHandler=t=>{let n=performance.now();this._firstInteractionTime===null&&(this._firstInteractionTime=n),this._interactions.push({type:t.type,t:n,timeSinceLoad:n-this._pageLoadTime})};for(let t of e)document.addEventListener(t,this._boundHandler,{passive:!0,capture:!0})}stopTracking(){if(!this._isTracking)return;this._isTracking=!1;let e=["click","mousedown","touchstart","keydown","scroll"];if(this._boundHandler){for(let t of e)document.removeEventListener(t,this._boundHandler,{capture:!0});this._boundHandler=null}}async detect(){let e=[],t=0;!this._isTracking&&this._interactions.length===0&&(this.startTracking(),await new Promise(r=>setTimeout(r,this._trackingDuration)),this.stopTracking());let n=this._interactions;if(n.length===0)return this.createResult(!1,{reason:"no-interactions"},0);let i=n[0];if(i.timeSinceLoad<100?(e.push("instant-interaction"),t=Math.max(t,.9)):i.timeSinceLoad<300&&(e.push("very-fast-interaction"),t=Math.max(t,.6)),n.length>3){let r=[];for(let l=1;l<n.length;l++)r.push(n[l].t-n[l-1].t);let a=r.reduce((l,h)=>l+h,0)/r.length;r.reduce((l,h)=>l+Math.pow(h-a,2),0)/r.length<10&&n.length>5&&(e.push("robotic-intervals"),t=Math.max(t,.8));let d=50,u=0;for(let l of r)l<d&&u++;u>r.length*.7&&(e.push("burst-interactions"),t=Math.max(t,.7))}let s=n.map(r=>r.type).join(",");if(n.length>=6){let r=Math.floor(n.length/2),a=n.slice(0,r).map(d=>d.type).join(","),p=n.slice(r,r*2).map(d=>d.type).join(",");a===p&&a.length>0&&(e.push("repeated-sequence"),t=Math.max(t,.6))}let c=e.length>0;return this.createResult(c,{anomalies:e,interactionCount:n.length,timeToFirstInteraction:i.timeSinceLoad,firstInteractionType:i.type},t)}reset(){super.reset(),this.stopTracking(),this._pageLoadTime=performance.now(),this._firstInteractionTime=null,this._interactions=[]}};o(b,"id","interaction-timing"),o(b,"category","behavior"),o(b,"weight",.6),o(b,"description","Detects suspicious interaction timing"),o(b,"requiresInteraction",!0);var T=class extends m{constructor(e={}){super(e),this._scrollEvents=[],this._isTracking=!1,this._trackingDuration=e.trackingDuration||3e3,this._boundHandler=null}startTracking(){this._isTracking||(this._scrollEvents=[],this._isTracking=!0,this._boundHandler=()=>{this._scrollEvents.push({scrollY:window.scrollY,scrollX:window.scrollX,t:performance.now()})},window.addEventListener("scroll",this._boundHandler,{passive:!0}))}stopTracking(){this._isTracking&&(this._isTracking=!1,this._boundHandler&&(window.removeEventListener("scroll",this._boundHandler),this._boundHandler=null))}async detect(){let e=[],t=0;this._scrollEvents.length===0&&(this.startTracking(),await new Promise(c=>setTimeout(c,this._trackingDuration)),this.stopTracking());let n=this._scrollEvents;if(n.length<3)return this.createResult(!1,{reason:"insufficient-scroll-data",scrollEvents:n.length},0);let i=this._analyzeScrollPatterns(n);i.instantJumps>0&&(e.push("instant-scroll-jumps"),t=Math.max(t,.7)),i.velocityVariance<.1&&n.length>10&&(e.push("constant-scroll-velocity"),t=Math.max(t,.6)),i.momentumEvents===0&&n.length>5&&(e.push("no-scroll-momentum"),t=Math.max(t,.5)),i.intervalVariance<5&&n.length>10&&(e.push("robotic-scroll-timing"),t=Math.max(t,.7)),i.scrollDirections===1&&Math.abs(i.totalScrollY)>1e3&&i.velocityVariance<1&&(e.push("one-dimensional-scroll"),t=Math.max(t,.4)),i.exactPositionScrolls>2&&(e.push("exact-position-scrolls"),t=Math.max(t,.6));let s=e.length>0;return this.createResult(s,{anomalies:e,scrollEventCount:n.length,analysis:i},t)}_analyzeScrollPatterns(e){if(e.length<2)return{instantJumps:0,velocityVariance:0,momentumEvents:0,intervalVariance:0,scrollDirections:0,totalScrollY:0,exactPositionScrolls:0};let t=0,n=0,i=[],s=[],c=!1,r=!1,a=0,p=[0,100,200,300,400,500,600,800,1e3];for(let _=1;_<e.length;_++){let w=e[_-1],k=e[_],v=k.scrollY-w.scrollY,W=k.scrollX-w.scrollX,Y=k.t-w.t;if(s.push(Y),Math.abs(v)>0&&(c=!0),Math.abs(W)>0&&(r=!0),Y===0)continue;let X=Math.sqrt(v*v+W*W)/Y;if(i.push(X),Math.abs(v)+Math.abs(W)>200&&Y<20&&t++,_>1&&i.length>1){let it=i[i.length-2];X<it*.9&&X>0&&n++}p.includes(Math.round(k.scrollY))&&a++}let d=i.length>0?i.reduce((_,w)=>_+w,0)/i.length:0,u=i.length>0?i.reduce((_,w)=>_+Math.pow(w-d,2),0)/i.length:0,l=s.reduce((_,w)=>_+w,0)/s.length,h=s.reduce((_,w)=>_+Math.pow(w-l,2),0)/s.length,f=0;c&&f++,r&&f++;let x=e[e.length-1].scrollY-e[0].scrollY;return{instantJumps:t,velocityVariance:u,momentumEvents:n,intervalVariance:h,scrollDirections:f,totalScrollY:x,exactPositionScrolls:a}}reset(){super.reset(),this.stopTracking(),this._scrollEvents=[]}};o(T,"id","scroll-behavior"),o(T,"category","behavior"),o(T,"weight",.5),o(T,"description","Detects programmatic scroll patterns"),o(T,"requiresInteraction",!0);var S=class extends m{async detect(){let e=[],t=0,n=navigator.plugins,i=navigator.mimeTypes;if(!n)return e.push("no-plugins-object"),t=Math.max(t,.6),this.createResult(!0,{anomalies:e},t);n.length===0&&(e.push("empty-plugins"),t=Math.max(t,.5));let s=navigator.userAgent||"";if(s.includes("Chrome")&&!s.includes("Chromium")&&!Array.from(n).some(p=>p.name.includes("PDF")||p.name.includes("Chromium PDF"))&&n.length===0&&(e.push("chrome-missing-pdf-plugin"),t=Math.max(t,.4)),n.length>0&&i){let a=0;for(let p=0;p<n.length;p++)a+=n[p].length||0;i.length===0&&a>0&&(e.push("mimetypes-mismatch"),t=Math.max(t,.5))}if(n.length>1){let a=Array.from(n).map(d=>d.name);new Set(a).size<a.length&&(e.push("duplicate-plugins"),t=Math.max(t,.6))}try{let a=Object.getOwnPropertyDescriptor(Navigator.prototype,"plugins");a&&a.get&&(a.get.toString().includes("[native code]")||(e.push("plugins-getter-overridden"),t=Math.max(t,.7)))}catch{}!/Android|iPhone|iPad|iPod|Mobile/i.test(s)&&n.length===1&&(e.push("minimal-plugins"),t=Math.max(t,.3));let r=e.length>0;return this.createResult(r,{anomalies:e,pluginCount:n.length,mimeTypeCount:(i==null?void 0:i.length)||0},t)}};o(S,"id","plugins"),o(S,"category","fingerprint"),o(S,"weight",.6),o(S,"description","Detects browser plugin anomalies");var L=class extends m{async detect(){let e=[],t=0,n=document.createElement("canvas"),i=null;try{i=n.getContext("webgl")||n.getContext("experimental-webgl")}catch{e.push("webgl-error"),t=Math.max(t,.5)}if(!i)return e.push("webgl-unavailable"),t=Math.max(t,.4),this.createResult(!0,{anomalies:e},t);let s=i.getExtension("WEBGL_debug_renderer_info"),c="",r="";s&&(c=i.getParameter(s.UNMASKED_VENDOR_WEBGL)||"",r=i.getParameter(s.UNMASKED_RENDERER_WEBGL)||""),!c&&!r&&(e.push("no-webgl-renderer-info"),t=Math.max(t,.6));let a=["swiftshader","llvmpipe","software","mesa","google swiftshader","vmware","virtualbox"],p=r.toLowerCase();for(let x of a)if(p.includes(x)){e.push(`suspicious-renderer-${x.replace(/\s+/g,"-")}`),t=Math.max(t,.7);break}c&&r&&(p.includes("nvidia")&&!c.toLowerCase().includes("nvidia")&&(e.push("vendor-renderer-mismatch"),t=Math.max(t,.6)),(p.includes("amd")||p.includes("radeon"))&&!c.toLowerCase().includes("amd")&&!c.toLowerCase().includes("ati")&&(e.push("vendor-renderer-mismatch"),t=Math.max(t,.6)));let d=i.getSupportedExtensions()||[];d.length<5&&(e.push("few-webgl-extensions"),t=Math.max(t,.4));let u=i.getParameter(i.MAX_TEXTURE_SIZE),l=i.getParameter(i.MAX_VIEWPORT_DIMS);(u<1024||u>65536)&&(e.push("unrealistic-max-texture"),t=Math.max(t,.5));try{i.clearColor(0,0,0,1),i.clear(i.COLOR_BUFFER_BIT);let x=new Uint8Array(4);i.readPixels(0,0,1,1,i.RGBA,i.UNSIGNED_BYTE,x),x[3]!==255&&(e.push("webgl-render-failure"),t=Math.max(t,.6))}catch{e.push("webgl-render-error"),t=Math.max(t,.5)}let h=i.getExtension("WEBGL_lose_context");h&&h.loseContext();let f=e.length>0;return this.createResult(f,{anomalies:e,vendor:c,renderer:r,extensionCount:d.length,maxTextureSize:u},t)}};o(L,"id","webgl"),o(L,"category","fingerprint"),o(L,"weight",.7),o(L,"description","Detects WebGL rendering anomalies");var P=class extends m{async detect(){let e=[],t=0;try{let i=document.createElement("canvas");i.width=200,i.height=50;let s=i.getContext("2d");if(!s)return e.push("canvas-context-unavailable"),t=Math.max(t,.5),this.createResult(!0,{anomalies:e},t);s.textBaseline="alphabetic",s.font="14px Arial",s.fillStyle="#f60",s.fillRect(0,0,200,50),s.fillStyle="#069",s.fillText("Bot Detection Test \u{1F916}",2,15),s.fillStyle="rgba(102, 204, 0, 0.7)",s.fillText("Canvas Fingerprint",4,30),s.beginPath(),s.arc(100,25,10,0,Math.PI*2,!0),s.closePath(),s.fill();let c=i.toDataURL();s.clearRect(0,0,200,50),s.fillStyle="#f60",s.fillRect(0,0,200,50),s.fillStyle="#069",s.fillText("Bot Detection Test \u{1F916}",2,15),s.fillStyle="rgba(102, 204, 0, 0.7)",s.fillText("Canvas Fingerprint",4,30),s.beginPath(),s.arc(100,25,10,0,Math.PI*2,!0),s.closePath(),s.fill();let r=i.toDataURL();c!==r&&(e.push("canvas-randomized"),t=Math.max(t,.6)),c.length<1e3&&(e.push("canvas-possibly-blank"),t=Math.max(t,.4));let a=document.createElement("canvas");a.width=200,a.height=50;let p=a.toDataURL();c===p&&(e.push("canvas-rendering-blocked"),t=Math.max(t,.7));try{i.toDataURL.toString().includes("[native code]")||(e.push("toDataURL-overridden"),t=Math.max(t,.8))}catch{}let u=s.getImageData(0,0,200,50).data,l=!0,h=[u[0],u[1],u[2],u[3]];for(let f=4;f<u.length;f+=4)if(u[f]!==h[0]||u[f+1]!==h[1]||u[f+2]!==h[2]){l=!1;break}l&&(e.push("uniform-pixel-data"),t=Math.max(t,.6))}catch{e.push("canvas-error"),t=Math.max(t,.4)}let n=e.length>0;return this.createResult(n,{anomalies:e},t)}};o(P,"id","canvas"),o(P,"category","fingerprint"),o(P,"weight",.5),o(P,"description","Detects canvas fingerprint anomalies");var I=class extends m{async detect(){let e=[],t=0,n=window.AudioContext||window.webkitAudioContext;if(!n)return e.push("audio-context-unavailable"),t=Math.max(t,.4),this.createResult(!0,{anomalies:e},t);let i=null,s=null,c=null;try{i=new n;let a=i.sampleRate;if(a!==44100&&a!==48e3&&a!==96e3&&(e.push("unusual-sample-rate"),t=Math.max(t,.3)),s=i.createOscillator(),c=i.createAnalyser(),!s||!c)e.push("audio-nodes-unavailable"),t=Math.max(t,.5);else{let d=c.fftSize,u=i.destination;(!u||u.maxChannelCount===0)&&(e.push("no-audio-destination"),t=Math.max(t,.6)),u&&u.maxChannelCount<2&&(e.push("mono-audio-only"),t=Math.max(t,.3))}try{n.toString().includes("[native code]")||(e.push("audio-context-overridden"),t=Math.max(t,.7))}catch{}try{if(i.state==="suspended"&&await i.resume().catch(()=>{}),i.state==="running"){let d=i.createOscillator(),u=i.createGain(),l=i.createScriptProcessor?i.createScriptProcessor(4096,1,1):null;l&&(d.type="triangle",d.frequency.value=1e4,u.gain.value=0,d.connect(u),u.connect(l),l.connect(i.destination),d.start(0),await new Promise(h=>setTimeout(h,50)),d.stop(),d.disconnect(),u.disconnect(),l.disconnect())}}catch{e.push("audio-fingerprint-blocked"),t=Math.max(t,.4)}window.OfflineAudioContext||window.webkitOfflineAudioContext||(e.push("offline-audio-context-unavailable"),t=Math.max(t,.3))}catch{e.push("audio-context-error"),t=Math.max(t,.4)}finally{if(s)try{s.disconnect()}catch{}if(c)try{c.disconnect()}catch{}if(i)try{i.close()}catch{}}let r=e.length>0;return this.createResult(r,{anomalies:e},t)}};o(I,"id","audio-context"),o(I,"category","fingerprint"),o(I,"weight",.5),o(I,"description","Detects AudioContext anomalies");var H=class extends m{async detect(){let e=[],t=0,n=window.screen;if(!n)return e.push("no-screen-object"),t=Math.max(t,.6),this.createResult(!0,{anomalies:e},t);let i=n.width,s=n.height,c=n.availWidth,r=n.availHeight,a=n.colorDepth,p=n.pixelDepth,d=window.outerWidth,u=window.outerHeight,l=window.innerWidth,h=window.innerHeight;(d===0||u===0)&&(e.push("zero-outer-dimensions"),t=Math.max(t,.8)),(l===0||h===0)&&(e.push("zero-inner-dimensions"),t=Math.max(t,.7));let f=navigator.userAgent||"";!/Android|iPhone|iPad|iPod|Mobile/i.test(f)&&(i<640||s<480)&&(e.push("very-small-screen"),t=Math.max(t,.5)),(i>7680||s>4320)&&(e.push("unrealistic-screen-size"),t=Math.max(t,.4));let _=[{w:800,h:600},{w:1024,h:768},{w:1920,h:1080}];for(let v of _)if(i===v.w&&s===v.h&&d===v.w&&u===v.h){e.push("headless-default-dimensions"),t=Math.max(t,.5);break}(c>i||r>s)&&(e.push("available-exceeds-total"),t=Math.max(t,.7)),(d>i||u>s)&&(e.push("window-exceeds-screen"),t=Math.max(t,.6)),a!==24&&a!==32&&a!==30&&a!==48&&(e.push("unusual-color-depth"),t=Math.max(t,.3)),a!==p&&(e.push("depth-mismatch"),t=Math.max(t,.3));let w=window.devicePixelRatio;if(w===0||w===void 0?(e.push("missing-device-pixel-ratio"),t=Math.max(t,.5)):(w<.5||w>5)&&(e.push("unusual-device-pixel-ratio"),t=Math.max(t,.4)),n.orientation){let v=n.orientation.type,W=n.orientation.angle;v.includes("landscape")&&i<s&&(e.push("orientation-dimension-mismatch"),t=Math.max(t,.4)),v.includes("portrait")&&i>s&&(e.push("orientation-dimension-mismatch"),t=Math.max(t,.4))}l===d&&h===u&&d>0&&u>0&&(e.push("no-browser-chrome"),t=Math.max(t,.5));let k=e.length>0;return this.createResult(k,{anomalies:e,dimensions:{screen:{width:i,height:s},available:{width:c,height:r},window:{outer:{width:d,height:u},inner:{width:l,height:h}},colorDepth:a,devicePixelRatio:w}},t)}};o(H,"id","screen"),o(H,"category","fingerprint"),o(H,"weight",.4),o(H,"description","Detects unusual screen dimensions");var A=class extends m{async detect(){let e=[],t=0;if(!window.performance||!performance.timing){if(performance.getEntriesByType){let v=performance.getEntriesByType("navigation");if(v.length>0)return this._analyzeNavigationTiming(v[0])}return e.push("no-performance-api"),t=Math.max(t,.3),this.createResult(!0,{anomalies:e},t)}let n=performance.timing,i=n.navigationStart,s=n.domContentLoadedEventEnd-i,c=n.domComplete-i,r=n.loadEventEnd-i,a=n.domainLookupEnd-n.domainLookupStart,p=n.connectEnd-n.connectStart,d=n.responseEnd-n.requestStart,u=n.domComplete-n.domLoading;s>0&&s<10&&(e.push("instant-dom-content-loaded"),t=Math.max(t,.7)),a===0&&p===0&&d<5&&(e.push("zero-network-timing"),t=Math.max(t,.4)),(s<0||c<0||r<0)&&(e.push("negative-timing"),t=Math.max(t,.8)),n.domContentLoadedEventEnd>0&&n.loadEventEnd>0&&n.domContentLoadedEventEnd>n.loadEventEnd&&(e.push("timing-order-violation"),t=Math.max(t,.7)),u>3e4&&(e.push("excessive-dom-processing"),t=Math.max(t,.3));let l=n.domContentLoadedEventStart-n.responseEnd;l>0&&l<5&&(e.push("instant-script-execution"),t=Math.max(t,.4));let h=performance.now(),f=performance.now();h===f&&h>0&&(e.push("frozen-performance-now"),t=Math.max(t,.6));let x=Date.now(),_=performance.now(),w=Date.now();Math.abs(w-x-(performance.now()-_))>100&&(e.push("timing-inconsistency"),t=Math.max(t,.5));let k=e.length>0;return this.createResult(k,{anomalies:e,timings:{domContentLoaded:s,domComplete:c,loadComplete:r,dnsLookup:a,tcpConnection:p,serverResponse:d,domProcessing:u}},t)}_analyzeNavigationTiming(e){let t=[],n=0,i=e.domContentLoadedEventEnd,s=e.loadEventEnd,c=e.domainLookupEnd-e.domainLookupStart,r=e.responseEnd-e.requestStart;i>0&&i<10&&(t.push("instant-dom-content-loaded"),n=Math.max(n,.7)),c===0&&r===0&&(t.push("zero-network-timing"),n=Math.max(n,.4));let a=t.length>0;return this.createResult(a,{anomalies:t,timings:{domContentLoaded:i,loadComplete:s,dnsLookup:c,serverResponse:r}},n)}};o(A,"id","page-load"),o(A,"category","timing"),o(A,"weight",.5),o(A,"description","Detects suspicious page load timing");var O=class extends m{constructor(e={}){super(e),this._domContentLoadedTime=null,this._documentReadyState=document.readyState,this._captureTime=performance.now(),document.readyState==="loading"&&document.addEventListener("DOMContentLoaded",()=>{this._domContentLoadedTime=performance.now()})}async detect(){let e=[],t=0,n=performance.now(),i=document.readyState,s=0,c=0,r=0;if(performance.getEntriesByType){let p=performance.getEntriesByType("resource");s=p.length;for(let d of p)c+=d.duration,d.initiatorType==="script"&&d.name.startsWith("http")&&r++}s===0&&i==="complete"&&(e.push("no-resources-loaded"),t=Math.max(t,.4)),this._domContentLoadedTime&&this._domContentLoadedTime<50&&s===0&&(e.push("instant-ready-no-resources"),t=Math.max(t,.6)),document.hidden&&this._documentReadyState==="loading"&&(e.push("hidden-at-load"),t=Math.max(t,.3)),typeof document.visibilityState>"u"&&(e.push("no-visibility-api"),t=Math.max(t,.4));try{let p=performance.now(),d=document.createElement("div");d.id="__bot_detection_test__",document.body.appendChild(d);let u=performance.now();document.body.removeChild(d);let l=performance.now(),h=u-p,f=l-u;h===0&&f===0&&(e.push("instant-dom-operations"),t=Math.max(t,.5))}catch{document.body||(e.push("no-document-body"),t=Math.max(t,.4))}if(typeof MutationObserver>"u"&&(e.push("no-mutation-observer"),t=Math.max(t,.5)),typeof requestAnimationFrame>"u"&&(e.push("no-request-animation-frame"),t=Math.max(t,.5)),performance.getEntriesByType){let p=performance.getEntriesByType("paint");!p.find(l=>l.name==="first-paint")&&i==="complete"&&n>1e3&&(e.push("no-first-paint"),t=Math.max(t,.4)),!p.find(l=>l.name==="first-contentful-paint")&&i==="complete"&&n>1e3&&(e.push("no-first-contentful-paint"),t=Math.max(t,.4))}typeof IntersectionObserver>"u"&&(e.push("no-intersection-observer"),t=Math.max(t,.4));let a=e.length>0;return this.createResult(a,{anomalies:e,metrics:{readyState:i,resourceCount:s,externalScriptCount:r,domContentLoadedTime:this._domContentLoadedTime,documentHidden:document.hidden}},t)}};o(O,"id","dom-content-timing"),o(O,"category","timing"),o(O,"weight",.4),o(O,"description","Analyzes DOM content loaded timing patterns");var B=class extends m{async detect(){let e=[],t=0;window.__puppeteer_evaluation_script__&&(e.push("puppeteer-evaluation-script"),t=Math.max(t,1));let n=["__puppeteer_evaluation_script__","__puppeteer","puppeteer"];for(let r of n)r in window&&(e.push(`global-${r}`),t=Math.max(t,1));(navigator.userAgent||"").includes("HeadlessChrome")&&(e.push("headless-chrome-ua"),t=Math.max(t,.9)),(window.cdc_adoQpoasnfa76pfcZLmcfl_Array||window.cdc_adoQpoasnfa76pfcZLmcfl_Promise||window.cdc_adoQpoasnfa76pfcZLmcfl_Symbol)&&(e.push("cdp-artifacts"),t=Math.max(t,1));try{window.eval.toString().includes("puppeteer")&&(e.push("eval-puppeteer"),t=Math.max(t,.9))}catch{}try{throw new Error("stack trace test")}catch(r){let a=r.stack||"";(a.includes("puppeteer")||a.includes("pptr"))&&(e.push("stack-trace-puppeteer"),t=Math.max(t,.8))}window.innerWidth===800&&window.innerHeight===600&&(e.push("default-viewport"),t=Math.max(t,.3)),navigator.webdriver===!0&&(e.push("webdriver-flag"),t=Math.max(t,.9)),Object.keys(window).filter(r=>r.startsWith("__")&&typeof window[r]=="function").length>3&&(e.push("suspicious-bindings"),t=Math.max(t,.5)),typeof window.chrome<"u"&&(!window.chrome.runtime||!window.chrome.runtime.id)&&(e.push("incomplete-chrome-object"),t=Math.max(t,.4));let c=e.length>0;return this.createResult(c,{indicators:e},t)}};o(B,"id","puppeteer"),o(B,"category","automation"),o(B,"weight",1),o(B,"description","Detects Puppeteer automation artifacts");var V=class extends m{async detect(){let e=[],t=0;window.__playwright&&(e.push("playwright-namespace"),t=Math.max(t,1));let n=["__playwright","__pw_manual","__pwInitScripts","playwright"];for(let c of n)c in window&&(e.push(`global-${c}`),t=Math.max(t,1));window.__playwright__binding__&&(e.push("playwright-binding"),t=Math.max(t,1));let i=navigator.userAgent||"";(i.includes("Playwright")||i.includes("HeadlessChrome"))&&(e.push("playwright-ua-marker"),t=Math.max(t,i.includes("Playwright")?1:.7)),navigator.webdriver===!0&&(e.push("webdriver-flag"),t=Math.max(t,.8));try{Object.keys(window).filter(a=>a.startsWith("__pw")).length>0&&(e.push("pw-bindings"),t=Math.max(t,1))}catch{}typeof window.__pw_date_intercepted<"u"&&(e.push("date-interception"),t=Math.max(t,.9)),window.__pw_geolocation__&&(e.push("geolocation-mock"),t=Math.max(t,.9)),window.__pw_permissions__&&(e.push("permissions-override"),t=Math.max(t,.9)),window.__cdpSession__&&(e.push("cdp-session"),t=Math.max(t,.8));try{throw new Error("stack trace test")}catch(c){let r=c.stack||"";(r.includes("playwright")||r.includes("__pw"))&&(e.push("stack-trace-playwright"),t=Math.max(t,.8))}try{let r=new Date().toLocaleString();window.__pwTimezone__&&(e.push("timezone-mock"),t=Math.max(t,.8))}catch{}let s=e.length>0;return this.createResult(s,{indicators:e},t)}};o(V,"id","playwright"),o(V,"category","automation"),o(V,"weight",1),o(V,"description","Detects Playwright automation artifacts");var K=class extends m{async detect(){let e=[],t=0;navigator.webdriver===!0&&(e.push("webdriver-flag"),t=Math.max(t,1));let n=["_selenium","callSelenium","_Selenium_IDE_Recorder","__selenium_evaluate","__selenium_unwrap","__webdriver_evaluate","__webdriver_unwrap","__webdriver_script_function","__webdriver_script_func","__fxdriver_evaluate","__fxdriver_unwrap","webdriver"];for(let a of n)a in window&&(e.push(`global-${a}`),t=Math.max(t,1));let i=["__webdriver_script_fn","__driver_evaluate","__webdriver_evaluate","__selenium_evaluate","__fxdriver_evaluate","__driver_unwrap","__webdriver_unwrap","__selenium_unwrap","__fxdriver_unwrap"];for(let a of i)a in document&&(e.push(`document-${a}`),t=Math.max(t,1));Object.keys(window).filter(a=>a.startsWith("$cdc_")||a.startsWith("$wdc_")||a.startsWith("$chrome_asyncScriptInfo")).length>0&&(e.push("chromedriver-variables"),t=Math.max(t,1)),(window.webdriverCallback||document.documentElement.getAttribute("webdriver"))&&(e.push("geckodriver-artifacts"),t=Math.max(t,1));try{let a=document.documentElement;(a.hasAttribute("webdriver")||a.getAttribute("selenium")||a.getAttribute("driver"))&&(e.push("document-webdriver-attr"),t=Math.max(t,1))}catch{}(window.selenium||window.sideex)&&(e.push("selenium-ide"),t=Math.max(t,1));try{let a=Object.getOwnPropertyDescriptor(Navigator.prototype,"webdriver");a&&a.get&&(a.get.toString().includes("[native code]")||(e.push("webdriver-getter-modified"),t=Math.max(t,.7)))}catch{}(window.domAutomation||window.domAutomationController)&&(e.push("dom-automation"),t=Math.max(t,1)),window.awesomium&&(e.push("awesomium"),t=Math.max(t,.9)),window.external&&window.external.toString().includes("Selenium")&&(e.push("external-selenium"),t=Math.max(t,1));let r=e.length>0;return this.createResult(r,{indicators:e},t)}};o(K,"id","selenium"),o(K,"category","automation"),o(K,"weight",1),o(K,"description","Detects Selenium WebDriver artifacts");var q=class extends m{async detect(){let e=[],t=0;window.callPhantom&&(e.push("callPhantom"),t=Math.max(t,1)),window._phantom&&(e.push("_phantom"),t=Math.max(t,1)),window.phantom&&(e.push("phantom"),t=Math.max(t,1));let n=navigator.userAgent||"";n.includes("PhantomJS")&&(e.push("phantomjs-ua"),t=Math.max(t,1)),window.__phantomas&&(e.push("phantomas"),t=Math.max(t,1)),window.__casper&&(e.push("casperjs"),t=Math.max(t,1)),window.casper&&(e.push("casper-global"),t=Math.max(t,1)),window.slimer&&(e.push("slimerjs"),t=Math.max(t,1)),window.__nightmare&&(e.push("nightmare"),t=Math.max(t,1)),window.nightmare&&(e.push("nightmare-global"),t=Math.max(t,1));try{let c=Function.prototype.toString.call(Function);(c.includes("phantom")||c.includes("Phantom"))&&(e.push("function-prototype-phantom"),t=Math.max(t,.8))}catch{}try{throw new Error("test")}catch(c){(c.stack||"").includes("phantom")&&(e.push("stack-trace-phantom"),t=Math.max(t,.9))}navigator.plugins&&navigator.plugins.length===0&&e.length>0&&(e.push("no-plugins-phantom"),t=Math.max(t,.5));let i=["__PHANTOM__","PHANTOM","Buffer","process"];for(let c of i)c in window&&c!=="Buffer"&&c!=="process"&&(e.push(`phantom-prop-${c.toLowerCase()}`),t=Math.max(t,.9));n.includes("QtWebKit")&&(e.push("qtwebkit"),t=Math.max(t,.7));let s=e.length>0;return this.createResult(s,{indicators:e},t)}};o(q,"id","phantomjs"),o(q,"category","automation"),o(q,"weight",1),o(q,"description","Detects PhantomJS automation artifacts");var tt={WebDriverSignal:E,HeadlessSignal:C,NavigatorAnomalySignal:R,PermissionsSignal:D,MouseMovementSignal:y,KeyboardPatternSignal:M,InteractionTimingSignal:b,ScrollBehaviorSignal:T,PluginsSignal:S,WebGLSignal:L,CanvasSignal:P,AudioContextSignal:I,ScreenSignal:H,PageLoadSignal:A,DOMContentTimingSignal:O,PuppeteerSignal:B,PlaywrightSignal:V,SeleniumSignal:K,PhantomJSSignal:q},G=[new E,new C,new R,new D,new S,new L,new P,new I,new H,new A,new O,new B,new V,new K,new q],Q=[new y,new M,new b,new T],lt=[...G,...Q];function Z(g={}){let{includeInteractionSignals:e=!0,instantBotSignals:t=["webdriver","puppeteer","playwright","selenium","phantomjs"],...n}=g,i=e?[...G,...Q.map(s=>{let c=s.constructor;return new c(s.options)})]:G.map(s=>{let c=s.constructor;return new c(s.options)});return new j({signals:i,instantBotSignals:t,...n})}async function J(g={}){return Z({includeInteractionSignals:!g.skipInteractionSignals}).detect(g)}async function et(){return J({skipInteractionSignals:!0})}var ut={BotDetector:j,createDetector:Z,detect:J,detectInstant:et,Signal:m,Signals:tt,Verdict:z};return ht(dt);})();
1
+ var BotDetectorLib=(()=>{var G=Object.defineProperty;var it=Object.getOwnPropertyDescriptor;var st=Object.getOwnPropertyNames;var ot=Object.prototype.hasOwnProperty;var at=(f,e,t)=>e in f?G(f,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):f[e]=t;var rt=(f,e)=>{for(var t in e)G(f,t,{get:e[t],enumerable:!0})},ct=(f,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of st(e))!ot.call(f,n)&&n!==t&&G(f,n,{get:()=>e[n],enumerable:!(i=it(e,n))||i.enumerable});return f};var ht=f=>ct(G({},"__esModule",{value:!0}),f);var o=(f,e,t)=>at(f,typeof e!="symbol"?e+"":e,t);var dt={};rt(dt,{AudioContextSignal:()=>P,BotDetector:()=>$,CanvasSignal:()=>L,DOMContentTimingSignal:()=>A,HeadlessSignal:()=>M,InteractionTimingSignal:()=>C,KeyboardPatternSignal:()=>E,MouseMovementSignal:()=>k,NavigatorAnomalySignal:()=>b,PageLoadSignal:()=>H,PermissionsSignal:()=>T,PhantomJSSignal:()=>K,PlaywrightSignal:()=>B,PluginsSignal:()=>R,PuppeteerSignal:()=>O,ScoringEngine:()=>N,ScreenSignal:()=>I,ScrollBehaviorSignal:()=>D,SeleniumSignal:()=>V,Signal:()=>m,Signals:()=>Z,Verdict:()=>U,VerdictEngine:()=>F,WebDriverSignal:()=>v,WebGLSignal:()=>S,createDetector:()=>X,default:()=>ut,defaultInstantSignals:()=>J,defaultInteractionSignals:()=>tt,defaultSignals:()=>lt,detect:()=>Q,detectInstant:()=>et});var m=class{constructor(e={}){this.options=e,this._lastResult=null}get id(){return this.constructor.id}get category(){return this.constructor.category}get weight(){var e;return(e=this.options.weight)!=null?e:this.constructor.weight}get description(){return this.constructor.description}get requiresInteraction(){return this.constructor.requiresInteraction}get lastResult(){return this._lastResult}async detect(){throw new Error(`Signal.detect() must be implemented by ${this.constructor.name}`)}async run(){try{return this._lastResult=await this.detect(),this._lastResult}catch(e){return this._lastResult={triggered:!1,value:null,confidence:0,error:e.message},this._lastResult}}reset(){this._lastResult=null}createResult(e,t=null,i=1){return{triggered:!!e,value:t,confidence:Math.max(0,Math.min(1,i))}}};o(m,"id","base-signal"),o(m,"category","uncategorized"),o(m,"weight",.5),o(m,"description","Base signal class"),o(m,"requiresInteraction",!1);var N=class{constructor(e={}){this.weightOverrides=e.weightOverrides||{},this.maxScore=e.maxScore||100,this._results=new Map}getWeight(e,t){var i;return(i=this.weightOverrides[e])!=null?i:t}addResult(e,t,i){let n=this.getWeight(e,i);this._results.set(e,{...t,weight:n,contribution:t.triggered?n*t.confidence:0})}calculate(){if(this._results.size===0)return 0;let e=0,t=0;for(let[,n]of this._results)e+=n.weight,t+=n.contribution;if(e===0)return 0;let i=t/e*this.maxScore;return Math.round(i*100)/100}getBreakdown(){let e=[],t=this.calculate();for(let[i,n]of this._results)e.push({signalId:i,triggered:n.triggered,confidence:n.confidence,weight:n.weight,contribution:n.contribution,percentOfScore:t>0?(n.contribution/t*100).toFixed(1):"0.0"});return e.sort((i,n)=>n.contribution-i.contribution)}getTriggeredSignals(){let e=[];for(let[t,i]of this._results)i.triggered&&e.push(t);return e}getTriggeredCount(){let e=0;for(let[,t]of this._results)t.triggered&&e++;return e}reset(){this._results.clear()}};var U={HUMAN:"human",SUSPICIOUS:"suspicious",BOT:"bot"},Y=class Y{constructor(e={}){var t,i;this.humanThreshold=(t=e.humanThreshold)!=null?t:Y.DEFAULT_THRESHOLDS.human,this.suspiciousThreshold=(i=e.suspiciousThreshold)!=null?i:Y.DEFAULT_THRESHOLDS.suspicious,this.instantBotSignals=new Set(e.instantBotSignals||[])}getVerdict(e,t=[]){for(let r of t)if(this.instantBotSignals.has(r))return{verdict:U.BOT,score:e,confidence:"high",reason:`Instant bot signal triggered: ${r}`,triggeredCount:t.length};let i,n,s;return e<this.humanThreshold?(i=U.HUMAN,n=e<10?"high":"medium",s="Low bot score"):e<this.suspiciousThreshold?(i=U.SUSPICIOUS,n="medium",s="Moderate bot indicators detected"):(i=U.BOT,n=e>=75?"high":"medium",s="High accumulation of bot indicators"),{verdict:i,score:e,confidence:n,reason:s,triggeredCount:t.length}}isInstantBotSignal(e){return this.instantBotSignals.has(e)}addInstantBotSignal(e){this.instantBotSignals.add(e)}setThresholds(e){e.human!==void 0&&(this.humanThreshold=e.human),e.suspicious!==void 0&&(this.suspiciousThreshold=e.suspicious)}};o(Y,"DEFAULT_THRESHOLDS",{human:20,suspicious:50});var F=Y;var $=class{constructor(e={}){if(this.options=e,this._signals=new Map,this._scoringEngine=new N({weightOverrides:e.weightOverrides}),this._verdictEngine=new F({humanThreshold:e.humanThreshold,suspiciousThreshold:e.suspiciousThreshold,instantBotSignals:e.instantBotSignals}),this._lastDetection=null,this._detectionTimeout=e.detectionTimeout||5e3,this._isRunning=!1,e.signals)for(let t of e.signals)this.registerSignal(t)}registerSignal(e){if(!(e instanceof m))throw new Error("Signal must be an instance of Signal class");let t=e.id;if(this._signals.has(t))throw new Error(`Signal with ID "${t}" is already registered`);return this._signals.set(t,e),this}registerSignals(e){for(let t of e)this.registerSignal(t);return this}unregisterSignal(e){return this._signals.delete(e)}getSignal(e){return this._signals.get(e)}getSignals(){return Array.from(this._signals.values())}getSignalsByCategory(e){return this.getSignals().filter(t=>t.category===e)}async detect(e={}){if(this._isRunning)throw new Error("Detection is already running");this._isRunning=!0,this._scoringEngine.reset();let t=performance.now(),i=new Map;try{let n=this.getSignals().filter(u=>!(e.skipInteractionSignals&&u.requiresInteraction)),s=n.map(async u=>{let l,h=new Promise(w=>{l=setTimeout(()=>w({triggered:!1,value:null,confidence:0,error:"timeout"}),this._detectionTimeout)}),g=await Promise.race([u.run(),h]);return clearTimeout(l),{signal:u,result:g}}),r=await Promise.all(s);for(let{signal:u,result:l}of r)i.set(u.id,{...l,category:u.category,weight:u.weight,description:u.description}),this._scoringEngine.addResult(u.id,l,u.weight);let c=this._scoringEngine.calculate(),a=this._scoringEngine.getTriggeredSignals(),p=this._verdictEngine.getVerdict(c,a),d=performance.now()-t;return this._lastDetection={...p,signals:Object.fromEntries(i),breakdown:this._scoringEngine.getBreakdown(),timestamp:Date.now(),detectionTimeMs:Math.round(d),totalSignals:n.length,triggeredSignals:a},this._lastDetection}finally{this._isRunning=!1}}getLastDetection(){return this._lastDetection}getScore(){var e,t;return(t=(e=this._lastDetection)==null?void 0:e.score)!=null?t:0}getTriggeredSignals(){var e,t;return(t=(e=this._lastDetection)==null?void 0:e.triggeredSignals)!=null?t:[]}isRunning(){return this._isRunning}reset(){this._scoringEngine.reset(),this._lastDetection=null;for(let e of this._signals.values())e.reset()}configure(e){(e.humanThreshold!==void 0||e.suspiciousThreshold!==void 0)&&this._verdictEngine.setThresholds({human:e.humanThreshold,suspicious:e.suspiciousThreshold}),e.detectionTimeout!==void 0&&(this._detectionTimeout=e.detectionTimeout)}static withDefaults(){throw new Error(`BotDetector.withDefaults() is not supported. Use createDetector() from '@niksbanna/bot-detector' instead:
2
+ import { createDetector } from '@niksbanna/bot-detector';
3
+ const detector = createDetector();`)}};var v=class extends m{async detect(){if(navigator.webdriver===!0)return this.createResult(!0,{webdriver:!0},1);let e=Object.getOwnPropertyDescriptor(navigator,"webdriver");if(e&&(e.get||!e.configurable))return this.createResult(!0,{webdriver:"modified",descriptor:{configurable:e.configurable,enumerable:e.enumerable,hasGetter:!!e.get}},.8);try{let t=Object.getPrototypeOf(navigator),i=Object.getOwnPropertyDescriptor(t,"webdriver");if(i&&i.get&&i.get.call(navigator)===!0)return this.createResult(!0,{webdriver:!0,source:"prototype"},1)}catch{}return this.createResult(!1)}};o(v,"id","webdriver"),o(v,"category","environment"),o(v,"weight",1),o(v,"description","Detects navigator.webdriver automation flag");var M=class extends m{async detect(){let e=[],t=0,i=navigator.userAgent||"";i.includes("HeadlessChrome")&&(e.push("headless-ua"),t=Math.max(t,1)),i.includes("Chrome")&&!i.includes("Chromium")&&typeof window.chrome>"u"&&(e.push("missing-chrome-object"),t=Math.max(t,.6)),navigator.plugins&&navigator.plugins.length===0&&(e.push("no-plugins"),t=Math.max(t,.5)),(!navigator.languages||navigator.languages.length===0)&&(e.push("no-languages"),t=Math.max(t,.6)),window.outerWidth===0&&window.outerHeight===0&&(e.push("zero-outer-dimensions"),t=Math.max(t,.7)),typeof navigator.connection>"u"&&i.includes("Chrome")&&(e.push("missing-connection-api"),t=Math.max(t,.3));try{typeof Notification<"u"&&Notification.permission==="denied"&&window.outerWidth===0&&(e.push("notification-headless-pattern"),t=Math.max(t,.5))}catch{}(window.callPhantom||window._phantom)&&(e.push("phantomjs"),t=Math.max(t,1)),window.__nightmare&&(e.push("nightmare"),t=Math.max(t,1));let n=e.length>0;return e.length>=3&&(t=Math.min(1,t+.2)),this.createResult(n,{indicators:e},t)}};o(M,"id","headless"),o(M,"category","environment"),o(M,"weight",.8),o(M,"description","Detects headless browser indicators");var b=class extends m{async detect(){let e=[],t=0,i=0,n=navigator.userAgent||"",s=navigator.platform||"";i++,s.includes("Win")&&!n.includes("Windows")?(e.push("platform-ua-mismatch-windows"),t+=1):s.includes("Mac")&&!n.includes("Mac")?(e.push("platform-ua-mismatch-mac"),t+=1):s.includes("Linux")&&!n.includes("Linux")&&!n.includes("Android")&&(e.push("platform-ua-mismatch-linux"),t+=1),i++,!(n.includes("Chrome")&&!n.includes("Chromium"))&&(!s||s===""||s==="undefined")&&(e.push("empty-platform"),t+=1),i++,navigator.language&&navigator.languages&&(navigator.languages.includes(navigator.language)||(e.push("language-mismatch"),t+=.5)),i++,n.includes("Chrome")&&navigator.vendor!=="Google Inc."?(e.push("vendor-mismatch-chrome"),t+=.5):n.includes("Firefox")&&navigator.vendor!==""?(e.push("vendor-mismatch-firefox"),t+=.5):n.includes("Safari")&&!n.includes("Chrome")&&navigator.vendor!=="Apple Computer, Inc."&&(e.push("vendor-mismatch-safari"),t+=.5),i++,typeof navigator.hardwareConcurrency<"u"&&(navigator.hardwareConcurrency===0||navigator.hardwareConcurrency>128)&&(e.push("suspicious-hardware-concurrency"),t+=.5),i++,typeof navigator.deviceMemory<"u"&&(navigator.deviceMemory===0||navigator.deviceMemory>512)&&(e.push("suspicious-device-memory"),t+=.5),i++;let c=/Android|iPhone|iPad|iPod|Mobile/i.test(n),a=navigator.maxTouchPoints>0;!c&&navigator.maxTouchPoints>5&&(e.push("desktop-high-touch-points"),t+=.3),i++;try{let u=Object.getOwnPropertyDescriptor(Navigator.prototype,"userAgent");u&&u.get&&u.get.toString().includes("native code")===!1&&(e.push("spoofed-user-agent"),t+=1)}catch{}let p=e.length>0,d=Math.min(1,t/Math.max(1,i));return this.createResult(p,{anomalies:e},d)}};o(b,"id","navigator-anomaly"),o(b,"category","environment"),o(b,"weight",.7),o(b,"description","Detects navigator property inconsistencies");var T=class extends m{async detect(){let e=[];if(!navigator.permissions)return this.createResult(!1,{supported:!1},0);try{let n=await navigator.permissions.query({name:"notifications"});if(typeof Notification<"u"){let s=Notification.permission;(s==="granted"&&n.state!=="granted"||s==="denied"&&n.state!=="denied"||s==="default"&&n.state!=="prompt")&&e.push("notification-permission-mismatch")}try{(await navigator.permissions.query({name:"geolocation"})).state==="denied"&&window.outerWidth===0&&e.push("geo-denied-headless")}catch{}try{await navigator.permissions.query({name:"camera"})}catch(s){s.name==="TypeError"&&e.push("camera-permission-error")}}catch(n){n.name!=="TypeError"&&e.push("permissions-query-error")}let t=e.length>0,i=Math.min(1,e.length*.4);return this.createResult(t,{anomalies:e},i)}};o(T,"id","permissions"),o(T,"category","environment"),o(T,"weight",.5),o(T,"description","Detects Permissions API anomalies");var k=class extends m{constructor(e={}){super(e),this._movements=[],this._isTracking=!1,this._trackingDuration=Math.min(e.trackingDuration||2500,2500),this._minMovements=e.minMovements||5,this._boundHandler=null}startTracking(){this._isTracking||(this._movements=[],this._isTracking=!0,this._boundHandler=e=>{this._movements.push({x:e.clientX,y:e.clientY,t:performance.now()})},document.addEventListener("mousemove",this._boundHandler,{passive:!0}))}stopTracking(){this._isTracking&&(this._isTracking=!1,this._boundHandler&&(document.removeEventListener("mousemove",this._boundHandler),this._boundHandler=null))}async detect(){let e=[],t=0;this._movements.length===0&&(this.startTracking(),await new Promise(r=>setTimeout(r,this._trackingDuration)),this.stopTracking());let i=this._movements;if(i.length<this._minMovements)return e.push("no-mouse-movement"),t=Math.max(t,.6),this.createResult(!0,{anomalies:e,movements:i.length},t);let n=this._analyzeMovements(i);n.teleportCount>0&&(e.push("mouse-teleportation"),t=Math.max(t,.7)),n.linearPathRatio>.9&&(e.push("linear-path"),t=Math.max(t,.8)),n.velocityVariance<.01&&i.length>10&&(e.push("constant-velocity"),t=Math.max(t,.7)),n.accelerationChanges===0&&i.length>10&&(e.push("no-acceleration-variance"),t=Math.max(t,.6)),n.timingVariance<1&&i.length>10&&(e.push("robotic-timing"),t=Math.max(t,.8));let s=e.length>0;return this.createResult(s,{anomalies:e,movementCount:i.length,analysis:n},t)}_analyzeMovements(e){if(e.length<3)return{teleportCount:0,linearPathRatio:0,velocityVariance:0,accelerationChanges:0,timingVariance:0};let t=0,i=[],n=[],s=[];for(let l=1;l<e.length;l++){let h=e[l-1],g=e[l],w=g.x-h.x,x=g.y-h.y,_=g.t-h.t;if(_===0)continue;let q=Math.sqrt(w*w+x*x),y=q/_;i.push(y),n.push(Math.atan2(x,w)),s.push(_),q>300&&_<10&&t++}let r=i.reduce((l,h)=>l+h,0)/i.length,c=i.reduce((l,h)=>l+Math.pow(h-r,2),0)/i.length,a=0;if(n.length>1){let l=0;for(let h=1;h<n.length;h++)Math.abs(n[h]-n[h-1])<.1&&l++;a=l/(n.length-1)}let p=s.reduce((l,h)=>l+h,0)/s.length,d=s.reduce((l,h)=>l+Math.pow(h-p,2),0)/s.length,u=0;for(let l=1;l<i.length;l++)(i[l]-i[l-1])*(i[l-1]-(i[l-2]||0))<0&&u++;return{teleportCount:t,linearPathRatio:a,velocityVariance:c,accelerationChanges:u,timingVariance:d}}reset(){super.reset(),this.stopTracking(),this._movements=[]}};o(k,"id","mouse-movement"),o(k,"category","behavior"),o(k,"weight",.9),o(k,"description","Detects non-human mouse movement patterns"),o(k,"requiresInteraction",!0);var E=class extends m{constructor(e={}){super(e),this._keystrokes=[],this._isTracking=!1,this._trackingDuration=Math.min(e.trackingDuration||2500,2500),this._minKeystrokes=e.minKeystrokes||10,this._boundKeydownHandler=null,this._boundKeyupHandler=null}startTracking(){this._isTracking||(this._keystrokes=[],this._isTracking=!0,this._boundKeydownHandler=e=>{this._keystrokes.push({type:"down",key:e.key,code:e.code,t:performance.now()})},this._boundKeyupHandler=e=>{this._keystrokes.push({type:"up",key:e.key,code:e.code,t:performance.now()})},document.addEventListener("keydown",this._boundKeydownHandler,{passive:!0}),document.addEventListener("keyup",this._boundKeyupHandler,{passive:!0}))}stopTracking(){this._isTracking&&(this._isTracking=!1,this._boundKeydownHandler&&(document.removeEventListener("keydown",this._boundKeydownHandler),this._boundKeydownHandler=null),this._boundKeyupHandler&&(document.removeEventListener("keyup",this._boundKeyupHandler),this._boundKeyupHandler=null))}async detect(){let e=[],t=0;this._keystrokes.length===0&&(this.startTracking(),await new Promise(c=>setTimeout(c,this._trackingDuration)),this.stopTracking());let i=this._keystrokes,n=i.filter(c=>c.type==="down");if(n.length<this._minKeystrokes)return this.createResult(!1,{reason:"insufficient-data",keystrokes:n.length},0);let s=this._analyzeKeystrokes(i);s.avgInterKeystrokeTime<50&&n.length>20&&(e.push("inhuman-speed"),t=Math.max(t,.9)),s.timingVariance<5&&n.length>15&&(e.push("robotic-timing"),t=Math.max(t,.8)),s.missingKeyups>n.length*.5&&(e.push("missing-keyups"),t=Math.max(t,.7)),s.holdTimeVariance<2&&s.holdTimes.length>10&&(e.push("constant-hold-time"),t=Math.max(t,.6)),s.sequentialKeys>n.length*.8&&n.length>10&&(e.push("sequential-input"),t=Math.max(t,.5)),s.rhythmScore<.1&&n.length>20&&(e.push("no-rhythm-variation"),t=Math.max(t,.6));let r=e.length>0;return this.createResult(r,{anomalies:e,keystrokeCount:n.length,analysis:s},t)}_analyzeKeystrokes(e){let t=e.filter(h=>h.type==="down"),i=e.filter(h=>h.type==="up");if(t.length<2)return{avgInterKeystrokeTime:1/0,timingVariance:1/0,missingKeyups:0,holdTimeVariance:1/0,holdTimes:[],sequentialKeys:0,rhythmScore:1};let n=[];for(let h=1;h<t.length;h++)n.push(t[h].t-t[h-1].t);let s=n.reduce((h,g)=>h+g,0)/n.length,r=n.reduce((h,g)=>h+Math.pow(g-s,2),0)/n.length,c=[];for(let h of t){let g=i.find(w=>w.key===h.key&&w.t>h.t);g&&c.push(g.t-h.t)}let a=c.length>0?c.reduce((h,g)=>h+g,0)/c.length:0,p=c.length>0?c.reduce((h,g)=>h+Math.pow(g-a,2),0)/c.length:1/0,d=t.length-c.length,u=0;for(let h=1;h<t.length;h++){let g=t[h-1].key.charCodeAt(0),w=t[h].key.charCodeAt(0);Math.abs(w-g)===1&&u++}let l=0;if(n.length>5){let h=[...n].sort((x,_)=>x-_),g=h[Math.floor(h.length/2)];l=n.filter(x=>Math.abs(x-g)>g*.3).length/n.length}return{avgInterKeystrokeTime:s,timingVariance:r,missingKeyups:d,holdTimeVariance:p,holdTimes:c,sequentialKeys:u,rhythmScore:l}}reset(){super.reset(),this.stopTracking(),this._keystrokes=[]}};o(E,"id","keyboard-pattern"),o(E,"category","behavior"),o(E,"weight",.8),o(E,"description","Detects non-human keystroke patterns"),o(E,"requiresInteraction",!0);var C=class extends m{constructor(e={}){super(e),this._pageLoadTime=performance.now(),this._firstInteractionTime=null,this._interactions=[],this._isTracking=!1,this._trackingDuration=e.trackingDuration||5e3,this._boundHandler=null}startTracking(){if(this._isTracking)return;this._interactions=[],this._isTracking=!0;let e=["click","mousedown","touchstart","keydown","scroll"];this._boundHandler=t=>{let i=performance.now();this._firstInteractionTime===null&&(this._firstInteractionTime=i),this._interactions.push({type:t.type,t:i,timeSinceLoad:i-this._pageLoadTime})};for(let t of e)document.addEventListener(t,this._boundHandler,{passive:!0,capture:!0})}stopTracking(){if(!this._isTracking)return;this._isTracking=!1;let e=["click","mousedown","touchstart","keydown","scroll"];if(this._boundHandler){for(let t of e)document.removeEventListener(t,this._boundHandler,{capture:!0});this._boundHandler=null}}async detect(){let e=[],t=0;!this._isTracking&&this._interactions.length===0&&(this.startTracking(),await new Promise(c=>setTimeout(c,this._trackingDuration)),this.stopTracking());let i=this._interactions;if(i.length===0)return this.createResult(!1,{reason:"no-interactions"},0);let n=i[0];if(n.timeSinceLoad<100?(e.push("instant-interaction"),t=Math.max(t,.9)):n.timeSinceLoad<300&&(e.push("very-fast-interaction"),t=Math.max(t,.6)),i.length>3){let c=[];for(let l=1;l<i.length;l++)c.push(i[l].t-i[l-1].t);let a=c.reduce((l,h)=>l+h,0)/c.length;c.reduce((l,h)=>l+Math.pow(h-a,2),0)/c.length<10&&i.length>5&&(e.push("robotic-intervals"),t=Math.max(t,.8));let d=50,u=0;for(let l of c)l<d&&u++;u>c.length*.7&&(e.push("burst-interactions"),t=Math.max(t,.7))}let s=i.map(c=>c.type).join(",");if(i.length>=6){let c=Math.floor(i.length/2),a=i.slice(0,c).map(d=>d.type).join(","),p=i.slice(c,c*2).map(d=>d.type).join(",");a===p&&a.length>0&&(e.push("repeated-sequence"),t=Math.max(t,.6))}let r=e.length>0;return this.createResult(r,{anomalies:e,interactionCount:i.length,timeToFirstInteraction:n.timeSinceLoad,firstInteractionType:n.type},t)}reset(){super.reset(),this.stopTracking(),this._pageLoadTime=performance.now(),this._firstInteractionTime=null,this._interactions=[]}};o(C,"id","interaction-timing"),o(C,"category","behavior"),o(C,"weight",.6),o(C,"description","Detects suspicious interaction timing"),o(C,"requiresInteraction",!0);var D=class extends m{constructor(e={}){super(e),this._scrollEvents=[],this._isTracking=!1,this._trackingDuration=Math.min(e.trackingDuration||2500,2500),this._boundHandler=null}startTracking(){this._isTracking||(this._scrollEvents=[],this._isTracking=!0,this._boundHandler=()=>{this._scrollEvents.push({scrollY:window.scrollY,scrollX:window.scrollX,t:performance.now()})},window.addEventListener("scroll",this._boundHandler,{passive:!0}))}stopTracking(){this._isTracking&&(this._isTracking=!1,this._boundHandler&&(window.removeEventListener("scroll",this._boundHandler),this._boundHandler=null))}async detect(){let e=[],t=0;this._scrollEvents.length===0&&(this.startTracking(),await new Promise(r=>setTimeout(r,this._trackingDuration)),this.stopTracking());let i=this._scrollEvents;if(i.length<3)return this.createResult(!1,{reason:"insufficient-scroll-data",scrollEvents:i.length},0);let n=this._analyzeScrollPatterns(i);n.instantJumps>0&&(e.push("instant-scroll-jumps"),t=Math.max(t,.7)),n.velocityVariance<.1&&i.length>10&&(e.push("constant-scroll-velocity"),t=Math.max(t,.6)),n.momentumEvents===0&&i.length>5&&(e.push("no-scroll-momentum"),t=Math.max(t,.5)),n.intervalVariance<5&&i.length>10&&(e.push("robotic-scroll-timing"),t=Math.max(t,.7)),n.scrollDirections===1&&Math.abs(n.totalScrollY)>1e3&&n.velocityVariance<1&&(e.push("one-dimensional-scroll"),t=Math.max(t,.4)),n.exactPositionScrolls>2&&(e.push("exact-position-scrolls"),t=Math.max(t,.6));let s=e.length>0;return this.createResult(s,{anomalies:e,scrollEventCount:i.length,analysis:n},t)}_analyzeScrollPatterns(e){if(e.length<2)return{instantJumps:0,velocityVariance:0,momentumEvents:0,intervalVariance:0,scrollDirections:0,totalScrollY:0,exactPositionScrolls:0};let t=0,i=0,n=[],s=[],r=!1,c=!1,a=0,p=[0,100,200,300,400,500,600,800,1e3];for(let x=1;x<e.length;x++){let _=e[x-1],q=e[x],y=q.scrollY-_.scrollY,W=q.scrollX-_.scrollX,z=q.t-_.t;if(s.push(z),Math.abs(y)>0&&(r=!0),Math.abs(W)>0&&(c=!0),z===0)continue;let j=Math.sqrt(y*y+W*W)/z;if(n.push(j),Math.abs(y)+Math.abs(W)>200&&z<20&&t++,x>1&&n.length>1){let nt=n[n.length-2];j<nt*.9&&j>0&&i++}p.includes(Math.round(q.scrollY))&&a++}let d=n.length>0?n.reduce((x,_)=>x+_,0)/n.length:0,u=n.length>0?n.reduce((x,_)=>x+Math.pow(_-d,2),0)/n.length:0,l=s.reduce((x,_)=>x+_,0)/s.length,h=s.reduce((x,_)=>x+Math.pow(_-l,2),0)/s.length,g=0;r&&g++,c&&g++;let w=e[e.length-1].scrollY-e[0].scrollY;return{instantJumps:t,velocityVariance:u,momentumEvents:i,intervalVariance:h,scrollDirections:g,totalScrollY:w,exactPositionScrolls:a}}reset(){super.reset(),this.stopTracking(),this._scrollEvents=[]}};o(D,"id","scroll-behavior"),o(D,"category","behavior"),o(D,"weight",.5),o(D,"description","Detects programmatic scroll patterns"),o(D,"requiresInteraction",!0);var R=class extends m{async detect(){let e=[],t=0,i=navigator.plugins,n=navigator.mimeTypes;if(!i)return e.push("no-plugins-object"),t=Math.max(t,.6),this.createResult(!0,{anomalies:e},t);i.length===0&&(e.push("empty-plugins"),t=Math.max(t,.5));let s=navigator.userAgent||"";if(s.includes("Chrome")&&!s.includes("Chromium")&&!Array.from(i).some(p=>p.name.includes("PDF")||p.name.includes("Chromium PDF"))&&i.length===0&&(e.push("chrome-missing-pdf-plugin"),t=Math.max(t,.4)),i.length>0&&n){let a=0;for(let p=0;p<i.length;p++)a+=i[p].length||0;n.length===0&&a>0&&(e.push("mimetypes-mismatch"),t=Math.max(t,.5))}if(i.length>1){let a=Array.from(i).map(d=>d.name);new Set(a).size<a.length&&(e.push("duplicate-plugins"),t=Math.max(t,.6))}try{let a=Object.getOwnPropertyDescriptor(Navigator.prototype,"plugins");a&&a.get&&(a.get.toString().includes("[native code]")||(e.push("plugins-getter-overridden"),t=Math.max(t,.7)))}catch{}!/Android|iPhone|iPad|iPod|Mobile/i.test(s)&&i.length===1&&(e.push("minimal-plugins"),t=Math.max(t,.3));let c=e.length>0;return this.createResult(c,{anomalies:e,pluginCount:i.length,mimeTypeCount:(n==null?void 0:n.length)||0},t)}};o(R,"id","plugins"),o(R,"category","fingerprint"),o(R,"weight",.6),o(R,"description","Detects browser plugin anomalies");var S=class extends m{async detect(){let e=[],t=0,i=document.createElement("canvas"),n=null;try{n=i.getContext("webgl")||i.getContext("experimental-webgl")}catch{e.push("webgl-error"),t=Math.max(t,.5)}if(!n)return e.push("webgl-unavailable"),t=Math.max(t,.4),this.createResult(!0,{anomalies:e},t);let s=n.getExtension("WEBGL_debug_renderer_info"),r="",c="";s&&(r=n.getParameter(s.UNMASKED_VENDOR_WEBGL)||"",c=n.getParameter(s.UNMASKED_RENDERER_WEBGL)||""),!r&&!c&&(e.push("no-webgl-renderer-info"),t=Math.max(t,.6));let a=["swiftshader","llvmpipe","software","mesa","google swiftshader","vmware","virtualbox"],p=c.toLowerCase();for(let w of a)if(p.includes(w)){e.push(`suspicious-renderer-${w.replace(/\s+/g,"-")}`),t=Math.max(t,.7);break}r&&c&&(p.includes("nvidia")&&!r.toLowerCase().includes("nvidia")&&(e.push("vendor-renderer-mismatch"),t=Math.max(t,.6)),(p.includes("amd")||p.includes("radeon"))&&!r.toLowerCase().includes("amd")&&!r.toLowerCase().includes("ati")&&(e.push("vendor-renderer-mismatch"),t=Math.max(t,.6)));let d=n.getSupportedExtensions()||[];d.length<5&&(e.push("few-webgl-extensions"),t=Math.max(t,.4));let u=n.getParameter(n.MAX_TEXTURE_SIZE),l=n.getParameter(n.MAX_VIEWPORT_DIMS);(u<1024||u>65536)&&(e.push("unrealistic-max-texture"),t=Math.max(t,.5));try{n.clearColor(0,0,0,1),n.clear(n.COLOR_BUFFER_BIT);let w=new Uint8Array(4);n.readPixels(0,0,1,1,n.RGBA,n.UNSIGNED_BYTE,w),w[3]!==255&&(e.push("webgl-render-failure"),t=Math.max(t,.6))}catch{e.push("webgl-render-error"),t=Math.max(t,.5)}let h=n.getExtension("WEBGL_lose_context");h&&h.loseContext();let g=e.length>0;return this.createResult(g,{anomalies:e,vendor:r,renderer:c,extensionCount:d.length,maxTextureSize:u},t)}};o(S,"id","webgl"),o(S,"category","fingerprint"),o(S,"weight",.7),o(S,"description","Detects WebGL rendering anomalies");var L=class extends m{async detect(){let e=[],t=0;try{let n=document.createElement("canvas");n.width=200,n.height=50;let s=n.getContext("2d");if(!s)return e.push("canvas-context-unavailable"),t=Math.max(t,.5),this.createResult(!0,{anomalies:e},t);s.textBaseline="alphabetic",s.font="14px Arial",s.fillStyle="#f60",s.fillRect(0,0,200,50),s.fillStyle="#069",s.fillText("Bot Detection Test \u{1F916}",2,15),s.fillStyle="rgba(102, 204, 0, 0.7)",s.fillText("Canvas Fingerprint",4,30),s.beginPath(),s.arc(100,25,10,0,Math.PI*2,!0),s.closePath(),s.fill();let r=n.toDataURL();s.clearRect(0,0,200,50),s.fillStyle="#f60",s.fillRect(0,0,200,50),s.fillStyle="#069",s.fillText("Bot Detection Test \u{1F916}",2,15),s.fillStyle="rgba(102, 204, 0, 0.7)",s.fillText("Canvas Fingerprint",4,30),s.beginPath(),s.arc(100,25,10,0,Math.PI*2,!0),s.closePath(),s.fill();let c=n.toDataURL();r!==c&&(e.push("canvas-randomized"),t=Math.max(t,.6)),r.length<1e3&&(e.push("canvas-possibly-blank"),t=Math.max(t,.4));let a=document.createElement("canvas");a.width=200,a.height=50;let p=a.toDataURL();r===p&&(e.push("canvas-rendering-blocked"),t=Math.max(t,.7));try{n.toDataURL.toString().includes("[native code]")||(e.push("toDataURL-overridden"),t=Math.max(t,.8))}catch{}let u=s.getImageData(0,0,200,50).data,l=!0,h=[u[0],u[1],u[2],u[3]];for(let g=4;g<u.length;g+=4)if(u[g]!==h[0]||u[g+1]!==h[1]||u[g+2]!==h[2]){l=!1;break}l&&(e.push("uniform-pixel-data"),t=Math.max(t,.6))}catch{e.push("canvas-error"),t=Math.max(t,.4)}let i=e.length>0;return this.createResult(i,{anomalies:e},t)}};o(L,"id","canvas"),o(L,"category","fingerprint"),o(L,"weight",.5),o(L,"description","Detects canvas fingerprint anomalies");var P=class extends m{async detect(){let e=[],t=0,i=window.AudioContext||window.webkitAudioContext;if(!i)return e.push("audio-context-unavailable"),t=Math.max(t,.4),this.createResult(!0,{anomalies:e},t);let n=null,s=null,r=null;try{n=new i;let a=n.sampleRate;if(a!==44100&&a!==48e3&&a!==96e3&&(e.push("unusual-sample-rate"),t=Math.max(t,.3)),s=n.createOscillator(),r=n.createAnalyser(),!s||!r)e.push("audio-nodes-unavailable"),t=Math.max(t,.5);else{let d=r.fftSize,u=n.destination;(!u||u.maxChannelCount===0)&&(e.push("no-audio-destination"),t=Math.max(t,.6)),u&&u.maxChannelCount<2&&(e.push("mono-audio-only"),t=Math.max(t,.3))}try{i.toString().includes("[native code]")||(e.push("audio-context-overridden"),t=Math.max(t,.7))}catch{}try{if(n.state==="suspended"&&await n.resume().catch(()=>{}),n.state==="running"){let d=n.createOscillator(),u=n.createGain(),l=n.createScriptProcessor?n.createScriptProcessor(4096,1,1):null;l&&(d.type="triangle",d.frequency.value=1e4,u.gain.value=0,d.connect(u),u.connect(l),l.connect(n.destination),d.start(0),await new Promise(h=>setTimeout(h,50)),d.stop(),d.disconnect(),u.disconnect(),l.disconnect())}}catch{e.push("audio-fingerprint-blocked"),t=Math.max(t,.4)}window.OfflineAudioContext||window.webkitOfflineAudioContext||(e.push("offline-audio-context-unavailable"),t=Math.max(t,.3))}catch{e.push("audio-context-error"),t=Math.max(t,.4)}finally{if(s)try{s.disconnect()}catch{}if(r)try{r.disconnect()}catch{}if(n)try{n.close()}catch{}}let c=e.length>0;return this.createResult(c,{anomalies:e},t)}};o(P,"id","audio-context"),o(P,"category","fingerprint"),o(P,"weight",.5),o(P,"description","Detects AudioContext anomalies");var I=class extends m{async detect(){let e=[],t=0,i=window.screen;if(!i)return e.push("no-screen-object"),t=Math.max(t,.6),this.createResult(!0,{anomalies:e},t);let n=i.width,s=i.height,r=i.availWidth,c=i.availHeight,a=i.colorDepth,p=i.pixelDepth,d=window.outerWidth,u=window.outerHeight,l=window.innerWidth,h=window.innerHeight;(d===0||u===0)&&(e.push("zero-outer-dimensions"),t=Math.max(t,.8)),(l===0||h===0)&&(e.push("zero-inner-dimensions"),t=Math.max(t,.7));let g=navigator.userAgent||"";!/Android|iPhone|iPad|iPod|Mobile/i.test(g)&&(n<640||s<480)&&(e.push("very-small-screen"),t=Math.max(t,.5)),(n>7680||s>4320)&&(e.push("unrealistic-screen-size"),t=Math.max(t,.4));let x=[{w:800,h:600},{w:1024,h:768},{w:1920,h:1080}];for(let y of x)if(n===y.w&&s===y.h&&d===y.w&&u===y.h){e.push("headless-default-dimensions"),t=Math.max(t,.5);break}(r>n||c>s)&&(e.push("available-exceeds-total"),t=Math.max(t,.7)),(d>n||u>s)&&(e.push("window-exceeds-screen"),t=Math.max(t,.6)),a!==24&&a!==32&&a!==30&&a!==48&&(e.push("unusual-color-depth"),t=Math.max(t,.3)),a!==p&&(e.push("depth-mismatch"),t=Math.max(t,.3));let _=window.devicePixelRatio;if(_===0||_===void 0?(e.push("missing-device-pixel-ratio"),t=Math.max(t,.5)):(_<.5||_>5)&&(e.push("unusual-device-pixel-ratio"),t=Math.max(t,.4)),i.orientation){let y=i.orientation.type,W=i.orientation.angle;y.includes("landscape")&&n<s&&(e.push("orientation-dimension-mismatch"),t=Math.max(t,.4)),y.includes("portrait")&&n>s&&(e.push("orientation-dimension-mismatch"),t=Math.max(t,.4))}l===d&&h===u&&d>0&&u>0&&(e.push("no-browser-chrome"),t=Math.max(t,.5));let q=e.length>0;return this.createResult(q,{anomalies:e,dimensions:{screen:{width:n,height:s},available:{width:r,height:c},window:{outer:{width:d,height:u},inner:{width:l,height:h}},colorDepth:a,devicePixelRatio:_}},t)}};o(I,"id","screen"),o(I,"category","fingerprint"),o(I,"weight",.4),o(I,"description","Detects unusual screen dimensions");var H=class extends m{async detect(){let e=[],t=0;if(!window.performance||!performance.timing){if(performance.getEntriesByType){let z=performance.getEntriesByType("navigation");if(z.length>0)return this._analyzeNavigationTiming(z[0])}return e.push("no-performance-api"),t=Math.max(t,.3),this.createResult(!0,{anomalies:e},t)}let i=performance.timing,n=i.navigationStart,s=(z,j)=>z===0||j===0?null:z-j,r=s(i.domContentLoadedEventEnd,n),c=s(i.domComplete,n),a=s(i.loadEventEnd,n),p=s(i.domainLookupEnd,i.domainLookupStart),d=s(i.connectEnd,i.connectStart),u=s(i.responseEnd,i.requestStart),l=s(i.domComplete,i.domLoading);r!==null&&r>0&&r<10&&(e.push("instant-dom-content-loaded"),t=Math.max(t,.7)),p===0&&d===0&&u!==null&&u<5&&(e.push("zero-network-timing"),t=Math.max(t,.4)),(r!==null&&r<0||c!==null&&c<0||a!==null&&a<0)&&(e.push("negative-timing"),t=Math.max(t,.8)),i.domContentLoadedEventEnd>0&&i.loadEventEnd>0&&i.domContentLoadedEventEnd>i.loadEventEnd&&(e.push("timing-order-violation"),t=Math.max(t,.7)),l!==null&&l>3e4&&(e.push("excessive-dom-processing"),t=Math.max(t,.3));let h=i.domContentLoadedEventStart-i.responseEnd;i.responseEnd>0&&i.domContentLoadedEventStart>0&&h>0&&h<5&&(e.push("instant-script-execution"),t=Math.max(t,.4));let g=performance.now(),w=g+2;for(;performance.now()<w;);performance.now()===g&&(e.push("frozen-performance-now"),t=Math.max(t,.6));let _=Date.now(),q=performance.now(),y=Date.now();Math.abs(y-_-(performance.now()-q))>100&&(e.push("timing-inconsistency"),t=Math.max(t,.5));let W=e.length>0;return this.createResult(W,{anomalies:e,timings:{domContentLoaded:r,domComplete:c,loadComplete:a,dnsLookup:p,tcpConnection:d,serverResponse:u,domProcessing:l}},t)}_analyzeNavigationTiming(e){let t=[],i=0,n=e.domContentLoadedEventEnd,s=e.loadEventEnd,r=e.domainLookupEnd-e.domainLookupStart,c=e.responseEnd-e.requestStart;n>0&&n<10&&(t.push("instant-dom-content-loaded"),i=Math.max(i,.7)),r===0&&c===0&&(t.push("zero-network-timing"),i=Math.max(i,.4));let a=t.length>0;return this.createResult(a,{anomalies:t,timings:{domContentLoaded:n,loadComplete:s,dnsLookup:r,serverResponse:c}},i)}};o(H,"id","page-load"),o(H,"category","timing"),o(H,"weight",.5),o(H,"description","Detects suspicious page load timing");var A=class extends m{constructor(e={}){super(e),this._domContentLoadedTime=null,this._documentReadyState=document.readyState,this._captureTime=performance.now(),document.readyState==="loading"&&document.addEventListener("DOMContentLoaded",()=>{this._domContentLoadedTime=performance.now()})}async detect(){let e=[],t=0,i=performance.now(),n=document.readyState,s=0,r=0,c=0;if(performance.getEntriesByType){let p=performance.getEntriesByType("resource");s=p.length;for(let d of p)r+=d.duration,d.initiatorType==="script"&&d.name.startsWith("http")&&c++}s===0&&n==="complete"&&(e.push("no-resources-loaded"),t=Math.max(t,.4)),this._domContentLoadedTime&&this._domContentLoadedTime<50&&s===0&&(e.push("instant-ready-no-resources"),t=Math.max(t,.6)),document.hidden&&this._documentReadyState==="loading"&&(e.push("hidden-at-load"),t=Math.max(t,.3)),typeof document.visibilityState>"u"&&(e.push("no-visibility-api"),t=Math.max(t,.4));try{let p=`__bdt_${Math.random().toString(36).slice(2)}`,d=performance.now(),u=document.createElement("div");u.id=p,document.body.appendChild(u);let l=performance.now();document.body.removeChild(u);let h=performance.now(),g=l-d,w=h-l;g===0&&w===0&&(e.push("instant-dom-operations"),t=Math.max(t,.5))}catch{document.body||(e.push("no-document-body"),t=Math.max(t,.4))}if(typeof MutationObserver>"u"&&(e.push("no-mutation-observer"),t=Math.max(t,.5)),typeof requestAnimationFrame>"u"&&(e.push("no-request-animation-frame"),t=Math.max(t,.5)),performance.getEntriesByType){let p=performance.getEntriesByType("paint");!p.find(l=>l.name==="first-paint")&&n==="complete"&&i>1e3&&(e.push("no-first-paint"),t=Math.max(t,.4)),!p.find(l=>l.name==="first-contentful-paint")&&n==="complete"&&i>1e3&&(e.push("no-first-contentful-paint"),t=Math.max(t,.4))}typeof IntersectionObserver>"u"&&(e.push("no-intersection-observer"),t=Math.max(t,.4));let a=e.length>0;return this.createResult(a,{anomalies:e,metrics:{readyState:n,resourceCount:s,externalScriptCount:c,domContentLoadedTime:this._domContentLoadedTime,documentHidden:document.hidden}},t)}};o(A,"id","dom-content-timing"),o(A,"category","timing"),o(A,"weight",.4),o(A,"description","Analyzes DOM content loaded timing patterns");var O=class extends m{async detect(){let e=[],t=0;window.__puppeteer_evaluation_script__&&(e.push("puppeteer-evaluation-script"),t=Math.max(t,1));let i=["__puppeteer_evaluation_script__","__puppeteer","puppeteer"];for(let a of i)a in window&&(e.push(`global-${a}`),t=Math.max(t,1));(navigator.userAgent||"").includes("HeadlessChrome")&&(e.push("headless-chrome-ua"),t=Math.max(t,.9)),(window.cdc_adoQpoasnfa76pfcZLmcfl_Array||window.cdc_adoQpoasnfa76pfcZLmcfl_Promise||window.cdc_adoQpoasnfa76pfcZLmcfl_Symbol)&&(e.push("cdp-artifacts"),t=Math.max(t,1));try{window.eval.toString().includes("puppeteer")&&(e.push("eval-puppeteer"),t=Math.max(t,.9))}catch{}try{throw new Error("stack trace test")}catch(a){let p=a.stack||"";(p.includes("puppeteer")||p.includes("pptr"))&&(e.push("stack-trace-puppeteer"),t=Math.max(t,.8))}window.innerWidth===800&&window.innerHeight===600&&(e.push("default-viewport"),t=Math.max(t,.3));let s=["__zone_symbol__","__next","__webpack","__react","__REACT","__vite","__nuxt"];Object.keys(window).filter(a=>!a.startsWith("__")||typeof window[a]!="function"?!1:!s.some(p=>a.startsWith(p))).length>10&&(e.push("suspicious-bindings"),t=Math.max(t,.5));let c=e.length>0;return this.createResult(c,{indicators:e},t)}};o(O,"id","puppeteer"),o(O,"category","automation"),o(O,"weight",1),o(O,"description","Detects Puppeteer automation artifacts");var B=class extends m{async detect(){let e=[],t=0;window.__playwright&&(e.push("playwright-namespace"),t=Math.max(t,1));let i=["__playwright","__pw_manual","__pwInitScripts","playwright"];for(let r of i)r in window&&(e.push(`global-${r}`),t=Math.max(t,1));window.__playwright__binding__&&(e.push("playwright-binding"),t=Math.max(t,1));let n=navigator.userAgent||"";(n.includes("Playwright")||n.includes("HeadlessChrome"))&&(e.push("playwright-ua-marker"),t=Math.max(t,n.includes("Playwright")?1:.7));try{Object.keys(window).filter(a=>a.startsWith("__pw")).length>0&&(e.push("pw-bindings"),t=Math.max(t,1))}catch{}typeof window.__pw_date_intercepted<"u"&&(e.push("date-interception"),t=Math.max(t,.9)),window.__pw_geolocation__&&(e.push("geolocation-mock"),t=Math.max(t,.9)),window.__pw_permissions__&&(e.push("permissions-override"),t=Math.max(t,.9)),window.__cdpSession__&&(e.push("cdp-session"),t=Math.max(t,.8));try{throw new Error("stack trace test")}catch(r){let c=r.stack||"";(c.includes("playwright")||c.includes("__pw"))&&(e.push("stack-trace-playwright"),t=Math.max(t,.8))}try{let c=new Date().toLocaleString();window.__pwTimezone__&&(e.push("timezone-mock"),t=Math.max(t,.8))}catch{}let s=e.length>0;return this.createResult(s,{indicators:e},t)}};o(B,"id","playwright"),o(B,"category","automation"),o(B,"weight",1),o(B,"description","Detects Playwright automation artifacts");var V=class extends m{async detect(){let e=[],t=0,i=["_selenium","callSelenium","_Selenium_IDE_Recorder","__selenium_evaluate","__selenium_unwrap","__webdriver_evaluate","__webdriver_unwrap","__webdriver_script_function","__webdriver_script_func","__fxdriver_evaluate","__fxdriver_unwrap","webdriver"];for(let a of i)a in window&&(e.push(`global-${a}`),t=Math.max(t,1));let n=["__webdriver_script_fn","__driver_evaluate","__webdriver_evaluate","__selenium_evaluate","__fxdriver_evaluate","__driver_unwrap","__webdriver_unwrap","__selenium_unwrap","__fxdriver_unwrap"];for(let a of n)a in document&&(e.push(`document-${a}`),t=Math.max(t,1));Object.keys(window).filter(a=>a.startsWith("$cdc_")||a.startsWith("$wdc_")||a.startsWith("$chrome_asyncScriptInfo")).length>0&&(e.push("chromedriver-variables"),t=Math.max(t,1)),(window.webdriverCallback||document.documentElement.getAttribute("webdriver"))&&(e.push("geckodriver-artifacts"),t=Math.max(t,1));try{let a=document.documentElement;(a.hasAttribute("webdriver")||a.getAttribute("selenium")||a.getAttribute("driver"))&&(e.push("document-webdriver-attr"),t=Math.max(t,1))}catch{}(window.selenium||window.sideex)&&(e.push("selenium-ide"),t=Math.max(t,1));try{let a=Object.getOwnPropertyDescriptor(Navigator.prototype,"webdriver");a&&a.get&&(a.get.toString().includes("[native code]")||(e.push("webdriver-getter-modified"),t=Math.max(t,.7)))}catch{}(window.domAutomation||window.domAutomationController)&&(e.push("dom-automation"),t=Math.max(t,1)),window.awesomium&&(e.push("awesomium"),t=Math.max(t,.9)),window.external&&window.external.toString().includes("Selenium")&&(e.push("external-selenium"),t=Math.max(t,1));let c=e.length>0;return this.createResult(c,{indicators:e},t)}};o(V,"id","selenium"),o(V,"category","automation"),o(V,"weight",1),o(V,"description","Detects Selenium WebDriver artifacts");var K=class extends m{async detect(){let e=[],t=0;window.callPhantom&&(e.push("callPhantom"),t=Math.max(t,1)),window._phantom&&(e.push("_phantom"),t=Math.max(t,1)),window.phantom&&(e.push("phantom"),t=Math.max(t,1));let i=navigator.userAgent||"";i.includes("PhantomJS")&&(e.push("phantomjs-ua"),t=Math.max(t,1)),window.__phantomas&&(e.push("phantomas"),t=Math.max(t,1)),window.__casper&&(e.push("casperjs"),t=Math.max(t,1)),window.casper&&(e.push("casper-global"),t=Math.max(t,1)),window.slimer&&(e.push("slimerjs"),t=Math.max(t,1)),window.__nightmare&&(e.push("nightmare"),t=Math.max(t,1)),window.nightmare&&(e.push("nightmare-global"),t=Math.max(t,1));try{let r=Function.prototype.toString.call(Function);(r.includes("phantom")||r.includes("Phantom"))&&(e.push("function-prototype-phantom"),t=Math.max(t,.8))}catch{}try{throw new Error("test")}catch(r){(r.stack||"").includes("phantom")&&(e.push("stack-trace-phantom"),t=Math.max(t,.9))}navigator.plugins&&navigator.plugins.length===0&&e.length>0&&(e.push("no-plugins-phantom"),t=Math.max(t,.5));let n=["__PHANTOM__","PHANTOM"];for(let r of n)r in window&&(e.push(`phantom-prop-${r.toLowerCase()}`),t=Math.max(t,.9));i.includes("QtWebKit")&&(e.push("qtwebkit"),t=Math.max(t,.7));let s=e.length>0;return this.createResult(s,{indicators:e},t)}};o(K,"id","phantomjs"),o(K,"category","automation"),o(K,"weight",1),o(K,"description","Detects PhantomJS automation artifacts");var Z={WebDriverSignal:v,HeadlessSignal:M,NavigatorAnomalySignal:b,PermissionsSignal:T,MouseMovementSignal:k,KeyboardPatternSignal:E,InteractionTimingSignal:C,ScrollBehaviorSignal:D,PluginsSignal:R,WebGLSignal:S,CanvasSignal:L,AudioContextSignal:P,ScreenSignal:I,PageLoadSignal:H,DOMContentTimingSignal:A,PuppeteerSignal:O,PlaywrightSignal:B,SeleniumSignal:V,PhantomJSSignal:K},J=[new v,new M,new b,new T,new R,new S,new L,new P,new I,new H,new A,new O,new B,new V,new K],tt=[new k,new E,new C,new D],lt=[...J,...tt];function X(f={}){let{includeInteractionSignals:e=!0,instantBotSignals:t=["webdriver","puppeteer","playwright","selenium","phantomjs"],...i}=f,s=(e?[v,M,b,T,R,S,L,P,I,H,A,O,B,V,K,k,E,C,D]:[v,M,b,T,R,S,L,P,I,H,A,O,B,V,K]).map(r=>new r);return new $({signals:s,instantBotSignals:t,...i})}async function Q(f={}){return X({includeInteractionSignals:!f.skipInteractionSignals}).detect(f)}async function et(){return Q({skipInteractionSignals:!0})}var ut={BotDetector:$,createDetector:X,detect:Q,detectInstant:et,Signal:m,Signals:Z,Verdict:U};return ht(dt);})();
package/package.json CHANGED
@@ -1,19 +1,20 @@
1
1
  {
2
2
  "name": "@niksbanna/bot-detector",
3
- "version": "1.0.0",
3
+ "version": "1.0.3",
4
4
  "description": "Production-grade client-side bot detection system using signal-based scoring",
5
5
  "main": "dist/bot-detector.cjs.js",
6
6
  "module": "dist/bot-detector.esm.js",
7
- "browser": "dist/bot-detector.iife.js",
7
+ "browser": "dist/bot-detector.esm.js",
8
8
  "types": "dist/types.d.ts",
9
9
  "exports": {
10
10
  ".": {
11
11
  "types": "./dist/types.d.ts",
12
- "browser": "./dist/bot-detector.iife.js",
13
12
  "import": "./dist/bot-detector.esm.js",
14
13
  "require": "./dist/bot-detector.cjs.js",
15
14
  "default": "./dist/bot-detector.esm.js"
16
- }
15
+ },
16
+ "./iife": "./dist/bot-detector.iife.js",
17
+ "./iife.min": "./dist/bot-detector.iife.min.js"
17
18
  },
18
19
  "files": [
19
20
  "dist",
@@ -145,18 +145,19 @@ class BotDetector {
145
145
 
146
146
  // Run all signals with timeout
147
147
  const detectionPromises = signalsToRun.map(async signal => {
148
- const result = await Promise.race([
149
- signal.run(),
150
- new Promise(resolve =>
151
- setTimeout(() => resolve({
152
- triggered: false,
153
- value: null,
154
- confidence: 0,
155
- error: 'timeout'
156
- }), this._detectionTimeout)
157
- ),
158
- ]);
159
-
148
+ // preventing 15 leaked timers per detect() call in SPAs.
149
+ let timeoutId;
150
+ const timeoutPromise = new Promise(resolve => {
151
+ timeoutId = setTimeout(() => resolve({
152
+ triggered: false,
153
+ value: null,
154
+ confidence: 0,
155
+ error: 'timeout',
156
+ }), this._detectionTimeout);
157
+ });
158
+
159
+ const result = await Promise.race([signal.run(), timeoutPromise]);
160
+ clearTimeout(timeoutId);
160
161
  return { signal, result };
161
162
  });
162
163
 
@@ -257,13 +258,23 @@ class BotDetector {
257
258
  }
258
259
 
259
260
  /**
260
- * Create a detector with default signals.
261
- * @param {Object} [options={}] - Configuration options
262
- * @returns {BotDetector}
261
+ * @deprecated Use `createDetector()` from '@niksbanna/bot-detector' instead.
262
+ * This method cannot load default signals from here due to module boundaries.
263
+ *
264
+ * @example
265
+ * // Correct:
266
+ * import { createDetector } from '@niksbanna/bot-detector';
267
+ * const detector = createDetector();
268
+ *
269
+ * @throws {Error} Always — to prevent silent empty-detector bugs.
263
270
  */
264
- static withDefaults(options = {}) {
265
- // This will be populated with default signals in the main export
266
- return new BotDetector(options);
271
+ static withDefaults() {
272
+ throw new Error(
273
+ 'BotDetector.withDefaults() is not supported. ' +
274
+ 'Use createDetector() from \'@niksbanna/bot-detector\' instead:\n' +
275
+ ' import { createDetector } from \'@niksbanna/bot-detector\';\n' +
276
+ ' const detector = createDetector();'
277
+ );
267
278
  }
268
279
  }
269
280
 
@@ -79,6 +79,7 @@ class ScoringEngine {
79
79
  */
80
80
  getBreakdown() {
81
81
  const breakdown = [];
82
+ const score = this.calculate();
82
83
 
83
84
  for (const [signalId, data] of this._results) {
84
85
  breakdown.push({
@@ -87,8 +88,8 @@ class ScoringEngine {
87
88
  confidence: data.confidence,
88
89
  weight: data.weight,
89
90
  contribution: data.contribution,
90
- percentOfScore: this.calculate() > 0
91
- ? (data.contribution / this.calculate() * 100).toFixed(1)
91
+ percentOfScore: score > 0
92
+ ? (data.contribution / score * 100).toFixed(1)
92
93
  : '0.0',
93
94
  });
94
95
  }
package/src/index.js CHANGED
@@ -158,16 +158,24 @@ function createDetector(options = {}) {
158
158
  ...detectorOptions
159
159
  } = options;
160
160
 
161
- const signals = includeInteractionSignals
162
- ? [...defaultInstantSignals, ...defaultInteractionSignals.map(s => {
163
- // Create fresh instances for interaction signals
164
- const SignalClass = s.constructor;
165
- return new SignalClass(s.options);
166
- })]
167
- : defaultInstantSignals.map(s => {
168
- const SignalClass = s.constructor;
169
- return new SignalClass(s.options);
170
- });
161
+ // singletons. If two detectors share the same signal instances, calling
162
+ // detector1.reset() would corrupt detector2's cached results.
163
+ const signalClasses = includeInteractionSignals
164
+ ? [
165
+ WebDriverSignal, HeadlessSignal, NavigatorAnomalySignal, PermissionsSignal,
166
+ PluginsSignal, WebGLSignal, CanvasSignal, AudioContextSignal, ScreenSignal,
167
+ PageLoadSignal, DOMContentTimingSignal,
168
+ PuppeteerSignal, PlaywrightSignal, SeleniumSignal, PhantomJSSignal,
169
+ MouseMovementSignal, KeyboardPatternSignal, InteractionTimingSignal, ScrollBehaviorSignal,
170
+ ]
171
+ : [
172
+ WebDriverSignal, HeadlessSignal, NavigatorAnomalySignal, PermissionsSignal,
173
+ PluginsSignal, WebGLSignal, CanvasSignal, AudioContextSignal, ScreenSignal,
174
+ PageLoadSignal, DOMContentTimingSignal,
175
+ PuppeteerSignal, PlaywrightSignal, SeleniumSignal, PhantomJSSignal,
176
+ ];
177
+
178
+ const signals = signalClasses.map(Cls => new Cls());
171
179
 
172
180
  return new BotDetector({
173
181
  signals,
@@ -107,16 +107,14 @@ class PhantomJSSignal extends Signal {
107
107
  }
108
108
  }
109
109
 
110
- // Check for specific PhantomJS window properties
110
+ // Check for specific PhantomJS window properties.
111
111
  const phantomProps = [
112
112
  '__PHANTOM__',
113
113
  'PHANTOM',
114
- 'Buffer', // PhantomJS exposes Node.js Buffer
115
- 'process', // May expose Node.js process
116
114
  ];
117
115
 
118
116
  for (const prop of phantomProps) {
119
- if (prop in window && prop !== 'Buffer' && prop !== 'process') {
117
+ if (prop in window) {
120
118
  indicators.push(`phantom-prop-${prop.toLowerCase()}`);
121
119
  confidence = Math.max(confidence, 0.9);
122
120
  }
@@ -52,12 +52,6 @@ class PlaywrightSignal extends Signal {
52
52
  confidence = Math.max(confidence, ua.includes('Playwright') ? 1.0 : 0.7);
53
53
  }
54
54
 
55
- // Check for navigator.webdriver (Playwright sets this in headless)
56
- if (navigator.webdriver === true) {
57
- indicators.push('webdriver-flag');
58
- confidence = Math.max(confidence, 0.8);
59
- }
60
-
61
55
  // Check for Playwright's evaluate scope pattern
62
56
  try {
63
57
  // Playwright injects __pwBinding__ functions
@@ -83,30 +83,36 @@ class PuppeteerSignal extends Signal {
83
83
  confidence = Math.max(confidence, 0.3);
84
84
  }
85
85
 
86
- // Check for navigator.webdriver (Puppeteer sets this)
87
- if (navigator.webdriver === true) {
88
- indicators.push('webdriver-flag');
89
- confidence = Math.max(confidence, 0.9);
90
- }
86
+ // navigator.webdriver is already exclusively checked by WebDriverSignal (environment).
87
+ // Duplicating it here causes triple-counting of the same property across signals.
91
88
 
92
89
  // Check for binding injection pattern
93
90
  // Puppeteer's exposeFunction creates window bindings
91
+ // (Next.js, webpack, React DevTools, Angular) push __* count past the
92
+ // old threshold of 5 on perfectly normal pages.
93
+ const FRAMEWORK_PREFIXES = [
94
+ '__zone_symbol__', // Angular / Zone.js
95
+ '__next', // Next.js
96
+ '__webpack', // webpack
97
+ '__react', // React DevTools
98
+ '__REACT',
99
+ '__vite', // Vite
100
+ '__nuxt', // Nuxt.js
101
+ ];
94
102
  const suspiciousBindings = Object.keys(window).filter(key => {
95
- return key.startsWith('__') && typeof window[key] === 'function';
103
+ if (!key.startsWith('__')) return false;
104
+ if (typeof window[key] !== 'function') return false;
105
+ return !FRAMEWORK_PREFIXES.some(prefix => key.startsWith(prefix));
96
106
  });
97
107
 
98
- if (suspiciousBindings.length > 3) {
108
+ if (suspiciousBindings.length > 10) {
99
109
  indicators.push('suspicious-bindings');
100
110
  confidence = Math.max(confidence, 0.5);
101
111
  }
102
112
 
103
- // Check Chrome object anomalies (Puppeteer headless)
104
- if (typeof window.chrome !== 'undefined') {
105
- if (!window.chrome.runtime || !window.chrome.runtime.id) {
106
- indicators.push('incomplete-chrome-object');
107
- confidence = Math.max(confidence, 0.4);
108
- }
109
- }
113
+ // window.chrome.runtime is ONLY populated inside Chrome extensions.
114
+ // Its absence is completely normal for all real Chrome users.
115
+ // This check was causing every non-extension Chrome user to be flagged as Puppeteer.
110
116
 
111
117
  const triggered = indicators.length > 0;
112
118
 
@@ -18,11 +18,8 @@ class SeleniumSignal extends Signal {
18
18
  const indicators = [];
19
19
  let confidence = 0;
20
20
 
21
- // Check for navigator.webdriver (standard WebDriver flag)
22
- if (navigator.webdriver === true) {
23
- indicators.push('webdriver-flag');
24
- confidence = Math.max(confidence, 1.0);
25
- }
21
+ // navigator.webdriver is already exclusively checked by WebDriverSignal (environment).
22
+ // Duplicating it here causes triple-counting of the same property across signals.
26
23
 
27
24
  // Check for Selenium-specific globals
28
25
  const seleniumGlobals = [
@@ -19,7 +19,7 @@ class KeyboardPatternSignal extends Signal {
19
19
  super(options);
20
20
  this._keystrokes = [];
21
21
  this._isTracking = false;
22
- this._trackingDuration = options.trackingDuration || 5000;
22
+ this._trackingDuration = Math.min(options.trackingDuration || 2500, 2500);
23
23
  this._minKeystrokes = options.minKeystrokes || 10;
24
24
  this._boundKeydownHandler = null;
25
25
  this._boundKeyupHandler = null;
@@ -19,7 +19,7 @@ class MouseMovementSignal extends Signal {
19
19
  super(options);
20
20
  this._movements = [];
21
21
  this._isTracking = false;
22
- this._trackingDuration = options.trackingDuration || 3000; // Default 3 seconds
22
+ this._trackingDuration = Math.min(options.trackingDuration || 2500, 2500);
23
23
  this._minMovements = options.minMovements || 5;
24
24
  this._boundHandler = null;
25
25
  }
@@ -19,7 +19,7 @@ class ScrollBehaviorSignal extends Signal {
19
19
  super(options);
20
20
  this._scrollEvents = [];
21
21
  this._isTracking = false;
22
- this._trackingDuration = options.trackingDuration || 3000;
22
+ this._trackingDuration = Math.min(options.trackingDuration || 2500, 2500);
23
23
  this._boundHandler = null;
24
24
  }
25
25
 
@@ -25,14 +25,14 @@ class HeadlessSignal extends Signal {
25
25
  confidence = Math.max(confidence, 1.0);
26
26
  }
27
27
 
28
- // Check for missing chrome.runtime in Chrome
28
+ // Check for missing window.chrome in Chrome (true headless indicator).
29
+ // NOTE: chrome.runtime is only populated inside Chrome extensions, so
30
+ // its absence is perfectly normal for real users without extensions.
31
+ // Only flag when the entire window.chrome object is absent.
29
32
  if (ua.includes('Chrome') && !ua.includes('Chromium')) {
30
33
  if (typeof window.chrome === 'undefined') {
31
34
  indicators.push('missing-chrome-object');
32
35
  confidence = Math.max(confidence, 0.6);
33
- } else if (!window.chrome.runtime) {
34
- indicators.push('missing-chrome-runtime');
35
- confidence = Math.max(confidence, 0.4);
36
36
  }
37
37
  }
38
38
 
@@ -35,9 +35,10 @@ class NavigatorAnomalySignal extends Signal {
35
35
  totalScore += 1;
36
36
  }
37
37
 
38
- // Check for empty or suspicious platform
38
+ // Check for empty or suspicious platform.
39
39
  checksPerformed++;
40
- if (!platform || platform === '' || platform === 'undefined') {
40
+ const isModernChrome = ua.includes('Chrome') && !ua.includes('Chromium');
41
+ if (!isModernChrome && (!platform || platform === '' || platform === 'undefined')) {
41
42
  anomalies.push('empty-platform');
42
43
  totalScore += 1;
43
44
  }
@@ -81,9 +81,10 @@ class DOMContentTimingSignal extends Signal {
81
81
 
82
82
  // Check DOM manipulation timing
83
83
  try {
84
+ const randomId = `__bdt_${Math.random().toString(36).slice(2)}`;
84
85
  const startMutation = performance.now();
85
86
  const testDiv = document.createElement('div');
86
- testDiv.id = '__bot_detection_test__';
87
+ testDiv.id = randomId;
87
88
  document.body.appendChild(testDiv);
88
89
  const afterAppend = performance.now();
89
90
  document.body.removeChild(testDiv);