@niksbanna/bot-detector 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -0
- package/dist/bot-detector.cjs.js +115 -75
- package/dist/bot-detector.cjs.js.map +2 -2
- package/dist/bot-detector.esm.js +115 -75
- package/dist/bot-detector.esm.js.map +2 -2
- package/dist/bot-detector.iife.js +115 -75
- package/dist/bot-detector.iife.js.map +2 -2
- package/dist/bot-detector.iife.min.js +3 -1
- package/package.json +1 -2
- package/src/core/BotDetector.js +0 -284
- package/src/core/ScoringEngine.js +0 -134
- package/src/core/Signal.js +0 -181
- package/src/core/VerdictEngine.js +0 -132
- package/src/index.js +0 -273
- package/src/signals/automation/PhantomJSSignal.js +0 -137
- package/src/signals/automation/PlaywrightSignal.js +0 -129
- package/src/signals/automation/PuppeteerSignal.js +0 -122
- package/src/signals/automation/SeleniumSignal.js +0 -151
- package/src/signals/automation/index.js +0 -8
- package/src/signals/behavior/InteractionTimingSignal.js +0 -170
- package/src/signals/behavior/KeyboardPatternSignal.js +0 -235
- package/src/signals/behavior/MouseMovementSignal.js +0 -215
- package/src/signals/behavior/ScrollBehaviorSignal.js +0 -236
- package/src/signals/behavior/index.js +0 -8
- package/src/signals/environment/HeadlessSignal.js +0 -97
- package/src/signals/environment/NavigatorAnomalySignal.js +0 -117
- package/src/signals/environment/PermissionsSignal.js +0 -76
- package/src/signals/environment/WebDriverSignal.js +0 -58
- package/src/signals/environment/index.js +0 -8
- package/src/signals/fingerprint/AudioContextSignal.js +0 -158
- package/src/signals/fingerprint/CanvasSignal.js +0 -133
- package/src/signals/fingerprint/PluginsSignal.js +0 -106
- package/src/signals/fingerprint/ScreenSignal.js +0 -157
- package/src/signals/fingerprint/WebGLSignal.js +0 -146
- package/src/signals/fingerprint/index.js +0 -9
- package/src/signals/timing/DOMContentTimingSignal.js +0 -159
- package/src/signals/timing/PageLoadSignal.js +0 -165
- package/src/signals/timing/index.js +0 -6
|
@@ -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:()=>W,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 W=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,j=k.scrollX-w.scrollX,Y=k.t-w.t;if(s.push(Y),Math.abs(v)>0&&(c=!0),Math.abs(j)>0&&(r=!0),Y===0)continue;let X=Math.sqrt(v*v+j*j)/Y;if(i.push(X),Math.abs(v)+Math.abs(j)>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,j=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("__")&&!r.startsWith("__zone_symbol__")&&typeof window[r]=="function").length>5&&(e.push("suspicious-bindings"),t=Math.max(t,.5)),typeof window.chrome<"u"&&(window.chrome.runtime||(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 W({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:W,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,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@niksbanna/bot-detector",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
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",
|
|
@@ -18,7 +18,6 @@
|
|
|
18
18
|
},
|
|
19
19
|
"files": [
|
|
20
20
|
"dist",
|
|
21
|
-
"src",
|
|
22
21
|
"README.md"
|
|
23
22
|
],
|
|
24
23
|
"scripts": {
|
package/src/core/BotDetector.js
DELETED
|
@@ -1,284 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Main BotDetector orchestrator class.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { Signal } from './Signal.js';
|
|
6
|
-
import { ScoringEngine } from './ScoringEngine.js';
|
|
7
|
-
import { VerdictEngine, Verdict } from './VerdictEngine.js';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Main bot detection orchestrator.
|
|
11
|
-
* Manages signals, runs detection, and produces verdicts.
|
|
12
|
-
*
|
|
13
|
-
* @example
|
|
14
|
-
* const detector = new BotDetector();
|
|
15
|
-
* const result = await detector.detect();
|
|
16
|
-
* console.log(result.verdict); // 'human', 'suspicious', or 'bot'
|
|
17
|
-
*/
|
|
18
|
-
class BotDetector {
|
|
19
|
-
/**
|
|
20
|
-
* Creates a new BotDetector instance.
|
|
21
|
-
* @param {Object} [options={}] - Configuration options
|
|
22
|
-
* @param {Array<Signal>} [options.signals=[]] - Initial signals to register
|
|
23
|
-
* @param {Object.<string, number>} [options.weightOverrides={}] - Override signal weights
|
|
24
|
-
* @param {number} [options.humanThreshold=20] - Score threshold for human verdict
|
|
25
|
-
* @param {number} [options.suspiciousThreshold=50] - Score threshold for suspicious verdict
|
|
26
|
-
* @param {Array<string>} [options.instantBotSignals=[]] - Signal IDs that instantly flag as bot
|
|
27
|
-
* @param {boolean} [options.includeDefaults=true] - Include built-in signal detectors
|
|
28
|
-
* @param {number} [options.detectionTimeout=5000] - Timeout for detection in ms
|
|
29
|
-
*/
|
|
30
|
-
constructor(options = {}) {
|
|
31
|
-
this.options = options;
|
|
32
|
-
this._signals = new Map();
|
|
33
|
-
this._scoringEngine = new ScoringEngine({
|
|
34
|
-
weightOverrides: options.weightOverrides,
|
|
35
|
-
});
|
|
36
|
-
this._verdictEngine = new VerdictEngine({
|
|
37
|
-
humanThreshold: options.humanThreshold,
|
|
38
|
-
suspiciousThreshold: options.suspiciousThreshold,
|
|
39
|
-
instantBotSignals: options.instantBotSignals,
|
|
40
|
-
});
|
|
41
|
-
this._lastDetection = null;
|
|
42
|
-
this._detectionTimeout = options.detectionTimeout || 5000;
|
|
43
|
-
this._isRunning = false;
|
|
44
|
-
|
|
45
|
-
// Register initial signals
|
|
46
|
-
if (options.signals) {
|
|
47
|
-
for (const signal of options.signals) {
|
|
48
|
-
this.registerSignal(signal);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Register a signal detector.
|
|
55
|
-
* @param {Signal} signal - Signal instance to register
|
|
56
|
-
* @returns {BotDetector} this instance for chaining
|
|
57
|
-
* @throws {Error} If signal with same ID already registered
|
|
58
|
-
*/
|
|
59
|
-
registerSignal(signal) {
|
|
60
|
-
if (!(signal instanceof Signal)) {
|
|
61
|
-
throw new Error('Signal must be an instance of Signal class');
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const id = signal.id;
|
|
65
|
-
if (this._signals.has(id)) {
|
|
66
|
-
throw new Error(`Signal with ID "${id}" is already registered`);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
this._signals.set(id, signal);
|
|
70
|
-
return this;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Register multiple signals at once.
|
|
75
|
-
* @param {Array<Signal>} signals - Array of signal instances
|
|
76
|
-
* @returns {BotDetector} this instance for chaining
|
|
77
|
-
*/
|
|
78
|
-
registerSignals(signals) {
|
|
79
|
-
for (const signal of signals) {
|
|
80
|
-
this.registerSignal(signal);
|
|
81
|
-
}
|
|
82
|
-
return this;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Unregister a signal by ID.
|
|
87
|
-
* @param {string} signalId - Signal ID to remove
|
|
88
|
-
* @returns {boolean} True if signal was removed
|
|
89
|
-
*/
|
|
90
|
-
unregisterSignal(signalId) {
|
|
91
|
-
return this._signals.delete(signalId);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Get a registered signal by ID.
|
|
96
|
-
* @param {string} signalId - Signal ID
|
|
97
|
-
* @returns {Signal|undefined}
|
|
98
|
-
*/
|
|
99
|
-
getSignal(signalId) {
|
|
100
|
-
return this._signals.get(signalId);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Get all registered signals.
|
|
105
|
-
* @returns {Array<Signal>}
|
|
106
|
-
*/
|
|
107
|
-
getSignals() {
|
|
108
|
-
return Array.from(this._signals.values());
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Get signals by category.
|
|
113
|
-
* @param {string} category - Category name
|
|
114
|
-
* @returns {Array<Signal>}
|
|
115
|
-
*/
|
|
116
|
-
getSignalsByCategory(category) {
|
|
117
|
-
return this.getSignals().filter(s => s.category === category);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Run all signal detectors and calculate verdict.
|
|
122
|
-
* @param {Object} [options={}] - Detection options
|
|
123
|
-
* @param {boolean} [options.skipInteractionSignals=false] - Skip signals requiring interaction
|
|
124
|
-
* @returns {Promise<DetectionResult>}
|
|
125
|
-
*/
|
|
126
|
-
async detect(options = {}) {
|
|
127
|
-
if (this._isRunning) {
|
|
128
|
-
throw new Error('Detection is already running');
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
this._isRunning = true;
|
|
132
|
-
this._scoringEngine.reset();
|
|
133
|
-
|
|
134
|
-
const startTime = performance.now();
|
|
135
|
-
const signalResults = new Map();
|
|
136
|
-
|
|
137
|
-
try {
|
|
138
|
-
// Filter signals based on options
|
|
139
|
-
const signalsToRun = this.getSignals().filter(signal => {
|
|
140
|
-
if (options.skipInteractionSignals && signal.requiresInteraction) {
|
|
141
|
-
return false;
|
|
142
|
-
}
|
|
143
|
-
return true;
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
// Run all signals with timeout
|
|
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
|
-
|
|
160
|
-
return { signal, result };
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
const results = await Promise.all(detectionPromises);
|
|
164
|
-
|
|
165
|
-
// Process results
|
|
166
|
-
for (const { signal, result } of results) {
|
|
167
|
-
signalResults.set(signal.id, {
|
|
168
|
-
...result,
|
|
169
|
-
category: signal.category,
|
|
170
|
-
weight: signal.weight,
|
|
171
|
-
description: signal.description,
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
this._scoringEngine.addResult(signal.id, result, signal.weight);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// Calculate score and verdict
|
|
178
|
-
const score = this._scoringEngine.calculate();
|
|
179
|
-
const triggeredSignals = this._scoringEngine.getTriggeredSignals();
|
|
180
|
-
const verdict = this._verdictEngine.getVerdict(score, triggeredSignals);
|
|
181
|
-
|
|
182
|
-
const detectionTime = performance.now() - startTime;
|
|
183
|
-
|
|
184
|
-
this._lastDetection = {
|
|
185
|
-
...verdict,
|
|
186
|
-
signals: Object.fromEntries(signalResults),
|
|
187
|
-
breakdown: this._scoringEngine.getBreakdown(),
|
|
188
|
-
timestamp: Date.now(),
|
|
189
|
-
detectionTimeMs: Math.round(detectionTime),
|
|
190
|
-
totalSignals: signalsToRun.length,
|
|
191
|
-
triggeredSignals,
|
|
192
|
-
};
|
|
193
|
-
|
|
194
|
-
return this._lastDetection;
|
|
195
|
-
} finally {
|
|
196
|
-
this._isRunning = false;
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* Get the last detection result.
|
|
202
|
-
* @returns {DetectionResult|null}
|
|
203
|
-
*/
|
|
204
|
-
getLastDetection() {
|
|
205
|
-
return this._lastDetection;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* Get the current score (from last detection).
|
|
210
|
-
* @returns {number}
|
|
211
|
-
*/
|
|
212
|
-
getScore() {
|
|
213
|
-
return this._lastDetection?.score ?? 0;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Get triggered signals from last detection.
|
|
218
|
-
* @returns {Array<string>}
|
|
219
|
-
*/
|
|
220
|
-
getTriggeredSignals() {
|
|
221
|
-
return this._lastDetection?.triggeredSignals ?? [];
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* Check if detection is currently running.
|
|
226
|
-
* @returns {boolean}
|
|
227
|
-
*/
|
|
228
|
-
isRunning() {
|
|
229
|
-
return this._isRunning;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Reset the detector state.
|
|
234
|
-
*/
|
|
235
|
-
reset() {
|
|
236
|
-
this._scoringEngine.reset();
|
|
237
|
-
this._lastDetection = null;
|
|
238
|
-
for (const signal of this._signals.values()) {
|
|
239
|
-
signal.reset();
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* Update configuration options.
|
|
245
|
-
* @param {Object} options - New options
|
|
246
|
-
*/
|
|
247
|
-
configure(options) {
|
|
248
|
-
if (options.humanThreshold !== undefined || options.suspiciousThreshold !== undefined) {
|
|
249
|
-
this._verdictEngine.setThresholds({
|
|
250
|
-
human: options.humanThreshold,
|
|
251
|
-
suspicious: options.suspiciousThreshold,
|
|
252
|
-
});
|
|
253
|
-
}
|
|
254
|
-
if (options.detectionTimeout !== undefined) {
|
|
255
|
-
this._detectionTimeout = options.detectionTimeout;
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* Create a detector with default signals.
|
|
261
|
-
* @param {Object} [options={}] - Configuration options
|
|
262
|
-
* @returns {BotDetector}
|
|
263
|
-
*/
|
|
264
|
-
static withDefaults(options = {}) {
|
|
265
|
-
// This will be populated with default signals in the main export
|
|
266
|
-
return new BotDetector(options);
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* @typedef {Object} DetectionResult
|
|
272
|
-
* @property {string} verdict - 'human', 'suspicious', or 'bot'
|
|
273
|
-
* @property {number} score - Calculated score (0-100)
|
|
274
|
-
* @property {string} confidence - Confidence level
|
|
275
|
-
* @property {string} reason - Reason for verdict
|
|
276
|
-
* @property {Object} signals - Map of signal ID to results
|
|
277
|
-
* @property {Array<Object>} breakdown - Score contribution breakdown
|
|
278
|
-
* @property {number} timestamp - Detection timestamp
|
|
279
|
-
* @property {number} detectionTimeMs - Time taken for detection
|
|
280
|
-
* @property {number} totalSignals - Total signals evaluated
|
|
281
|
-
* @property {Array<string>} triggeredSignals - IDs of triggered signals
|
|
282
|
-
*/
|
|
283
|
-
|
|
284
|
-
export { BotDetector, Verdict };
|