trojanhorse-js
Version:
A comprehensive JavaScript library for fetching, managing, and analyzing global threat intelligence from multiple open-source feeds and security news sources. Unlike its mythological namesake, this Trojan protects your digital fortress.
1 lines • 74.3 kB
JavaScript
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("crypto-js"),require("axios")):"function"==typeof define&&define.amd?define(["exports","crypto-js","axios"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).TrojanHorse={},e.CryptoJS,e.axios)}(this,function(e,t,s){"use strict";var i="undefined"!=typeof document?document.currentScript:null;class r extends Error{code;statusCode;details;constructor(e,t,s,i){super(e),this.name="TrojanHorseError",this.code=t,this.statusCode=s||500,this.details=i||{},Error.captureStackTrace(this,r)}}class a extends r{constructor(e,t){super(e,"SECURITY_ERROR",403,t),this.name="SecurityError"}}class n extends r{constructor(e,t){super(e,"AUTH_ERROR",401,t),this.name="AuthenticationError"}}class o extends r{retryAfter;constructor(e,t,s){super(e,"RATE_LIMIT_ERROR",429,s),this.name="RateLimitError",this.retryAfter=t||60}}let c=null,u=!1;class h{static ALGORITHM="AES-256-GCM";static KEY_SIZE=32;static IV_SIZE_GCM=12;static SALT_SIZE=32;static ARGON2_MEMORY=65536;static ARGON2_TIME=3;static ARGON2_PARALLELISM=4;static PBKDF2_ITERATIONS=1e5;argon2Instance=null;constructor(){if("undefined"==typeof crypto&&void 0===window?.crypto)throw new a("No cryptographic API available");this.initializeArgon2()}async initializeArgon2(){try{this.argon2Instance=await async function(){if(u)return c;try{if("undefined"==typeof process||!process.versions?.node)return console.warn("Argon2 not available in browser environment, falling back to PBKDF2"),null;try{if("undefined"!=typeof require)return c=require("argon2"),u=!0,c;const{createRequire:e}=await import("module"),t=e(("undefined"==typeof document&&"undefined"==typeof location?require("url").pathToFileURL(__filename).href:"undefined"==typeof document?location.href:i&&"SCRIPT"===i.tagName.toUpperCase()&&i.src||new URL("trojanhorse.min.js",document.baseURI).href)||new URL("file://"+__filename).href);return c=t("argon2"),u=!0,c}catch(e){try{const e=await import("argon2");return c=e.default||e,u=!0,c}catch(e){return console.warn("Argon2 not available, falling back to PBKDF2"),u=!1,null}}}catch(e){return console.warn("Argon2 unavailable, falling back to PBKDF2:",String(e)),null}}()}catch(e){console.warn("Failed to initialize Argon2, using PBKDF2 fallback")}}generateSecureRandom(e){if(e<=0||e>1024)throw new a("Invalid random length: must be 1-1024 bytes");try{if("undefined"!=typeof crypto&&crypto.getRandomValues){const t=new Uint8Array(e);return crypto.getRandomValues(t),t}if("undefined"!=typeof window&&window.crypto?.getRandomValues){const t=new Uint8Array(e);return window.crypto.getRandomValues(t),t}{const s=t.lib.WordArray.random(e);return new Uint8Array(this.wordArrayToUint8Array(s))}}catch(e){throw new a("Failed to generate secure random bytes",{originalError:e})}}generateSalt(e=h.SALT_SIZE){const t=this.generateSecureRandom(e);return Buffer.from(t).toString("base64")}async deriveKey(e,s,i={}){if(!e||e.length<8)throw new a("Password must be at least 8 characters long");if(!s||s.length<16)throw new a("Salt must be at least 16 characters long");this.argon2Instance||await this.initializeArgon2();const r=Buffer.from(s,"base64");if(this.argon2Instance)try{return{key:await this.argon2Instance.hash(e,{type:this.argon2Instance.argon2id,memoryCost:i.memoryCost||h.ARGON2_MEMORY,timeCost:i.timeCost||h.ARGON2_TIME,parallelism:i.parallelism||h.ARGON2_PARALLELISM,hashLength:h.KEY_SIZE,salt:r,raw:!0}),method:"Argon2id"}}catch(e){console.warn("Argon2 failed, falling back to PBKDF2:",String(e))}try{const i=t.PBKDF2(e,s,{keySize:h.KEY_SIZE/4,iterations:h.PBKDF2_ITERATIONS,hasher:t.algo.SHA256}),r=[];for(let e=0;e<i.words.length;e++){const t=i.words[e];void 0!==t&&(r.push(t>>24&255),r.push(t>>16&255),r.push(t>>8&255),r.push(255&t))}return{key:Buffer.from(r.slice(0,h.KEY_SIZE)),method:"PBKDF2-Fallback"}}catch(e){throw new a("Key derivation failed",{originalError:e})}}async encrypt(e,s,i={}){try{if(!e)throw new a("Data cannot be empty");if(!s||s.length<8)throw new a("Password must be at least 8 characters long");const r=this.generateSalt(h.SALT_SIZE),n=this.generateSecureRandom(h.IV_SIZE_GCM),o=Buffer.from(n).toString("base64"),c=await this.deriveKey(s,r,{memoryCost:i.iterations||h.ARGON2_MEMORY,timeCost:h.ARGON2_TIME,parallelism:h.ARGON2_PARALLELISM}),u=JSON.stringify(e);if("undefined"!=typeof require)try{const e=require("crypto");let t,a;try{t=e.createCipher("aes-256-gcm",c.key),t.setAutoPadding(!1);let n=t.update(u,"utf8","base64");return n+=t.final("base64"),a=t.getAuthTag?t.getAuthTag().toString("base64"):"",this.secureErase(c.key),s="",{encrypted:n,authTag:a,salt:r,iv:o,algorithm:h.ALGORITHM,iterations:i.iterations||h.ARGON2_MEMORY,timestamp:Date.now(),memoryKb:h.ARGON2_MEMORY,parallelism:h.ARGON2_PARALLELISM,keyDerivation:c.method}}catch(e){throw new Error(`GCM not supported: ${e instanceof Error?e.message:String(e)}`)}}catch(e){console.warn("Node.js crypto failed, using CryptoJS fallback:",String(e))}const l=t.enc.Hex.parse(c.key.toString("hex")),d=t.enc.Base64.parse(o),m=t.AES.encrypt(u,l,{iv:d,mode:t.mode.CBC,padding:t.pad.Pkcs7}).ciphertext.toString(t.enc.Base64),p=t.HmacSHA256(m+o+r,l).toString();return this.secureErase(c.key),s="",{encrypted:m,authTag:p,salt:r,iv:o,algorithm:h.ALGORITHM,iterations:i.iterations||h.ARGON2_MEMORY,timestamp:Date.now(),memoryKb:h.ARGON2_MEMORY,parallelism:h.ARGON2_PARALLELISM,keyDerivation:c.method}}catch(e){if(e instanceof a)throw e;throw new a("Encryption failed",{originalError:e})}}async decrypt(e,s){try{if(!(e?.encrypted&&e.salt&&e.iv&&e.authTag))throw new a("Invalid vault structure");if(!s||s.length<8)throw new a("Invalid password");const i=await this.deriveKey(s,e.salt,{memoryCost:e.memoryKb||h.ARGON2_MEMORY,timeCost:h.ARGON2_TIME,parallelism:e.parallelism||h.ARGON2_PARALLELISM});if("undefined"!=typeof require)try{const t=require("crypto");try{const r=t.createDecipher("aes-256-gcm",i.key);r.setAuthTag&&e.authTag&&r.setAuthTag(Buffer.from(e.authTag,"base64"));let a=r.update(e.encrypted,"base64","utf8");return a+=r.final("utf8"),this.secureErase(i.key),s="",JSON.parse(a)}catch(e){throw new Error(`GCM decrypt not supported: ${e instanceof Error?e.message:String(e)}`)}}catch(e){console.warn("Node.js crypto GCM failed, using CryptoJS fallback:",String(e))}const r=t.enc.Hex.parse(i.key.toString("hex")),n=t.enc.Base64.parse(e.iv);if(t.HmacSHA256(e.encrypted+e.iv+e.salt,r).toString()!==e.authTag)throw new a("Authentication verification failed - data may be corrupted or tampered");try{const s=t.AES.decrypt(e.encrypted,r,{iv:n,mode:t.mode.CBC,padding:t.pad.Pkcs7}).toString(t.enc.Utf8);if(!s)throw new a("Decryption failed - malformed data or invalid key");return JSON.parse(s)}catch(e){const t=e instanceof Error?e.message:String(e);throw console.error("🔍 AES Decryption Error:",t),new a(`Decryption failed - ${t}`)}}catch(e){if(e instanceof a)throw e;throw new a("Decryption failed",{originalError:e})}}hash(e){if(!e)throw new a("Data to hash cannot be empty");try{return t.SHA256(e).toString(t.enc.Hex)}catch(e){throw new a("Hashing failed",{originalError:e})}}hmac(e,s){if(!e||!s)throw new a("Data and key cannot be empty");try{return t.HmacSHA256(e,s).toString(t.enc.Hex)}catch(e){throw new a("HMAC generation failed",{originalError:e})}}secureErase(e){try{if(Buffer.isBuffer(e)){const t=this.generateSecureRandom(e.length);for(let s=0;s<e.length;s++){const i=t[s];void 0!==i&&(e[s]=i)}e.fill(0)}else if(e&&"object"==typeof e&&e.words)for(let t=0;t<e.words.length;t++)e.words[t]=0;else if("string"==typeof e)e="";else if(e instanceof Uint8Array){const t=this.generateSecureRandom(e.length);for(let s=0;s<e.length;s++){const i=t[s];void 0!==i&&(e[s]=i)}e.fill(0)}}catch(e){console.warn("Secure erase failed:",e)}}wordArrayToUint8Array(e){const t=e.words,s=e.sigBytes,i=[];for(let e=0;e<s;e++)i.push(t[e>>>2]>>>24-e%4*8&255);return i}validateEncryptionParams(e){try{return!!(e&&"string"==typeof e.encrypted&&"string"==typeof e.authTag&&"string"==typeof e.salt&&"string"==typeof e.iv&&e.algorithm===h.ALGORITHM&&"number"==typeof e.timestamp&&e.timestamp>0)}catch(e){return!1}}getCryptoInfo(){return{implementation:"undefined"!=typeof require?"Node.js Crypto":"CryptoJS Fallback",algorithm:h.ALGORITHM,keyDerivation:"Argon2id",secure:!0}}isSecureContext(){return"undefined"==typeof window||(window.isSecureContext||"https:"===location.protocol)}}class l{cryptoEngine;encryptedVault=null;decryptedKeys=null;isLocked=!0;options;autoLockTimer=null;lastAccessTime=null;failedAttempts=0;maxFailedAttempts=5;lockoutDuration=3e5;constructor(e={}){this.cryptoEngine=new h,this.options={algorithm:"AES-256-GCM",keyDerivation:"Argon2id",iterations:65536,saltBytes:32,autoLock:!0,lockTimeout:3e5,requireMFA:!1,...e}}extractApiKey(e){if(e)return"string"==typeof e?e:"object"==typeof e?e.key||e.secret||e.token:void 0}normalizeApiKeys(e){const t={};for(const[s,i]of Object.entries(e)){const e=this.extractApiKey(i);e&&(t[s]=e)}return t}async createVault(e,t){this.validatePassword(e),this.validateApiKeys(t);try{const s=await this.cryptoEngine.encrypt(t,e,this.options);return this.encryptedVault=s,this.decryptedKeys=this.normalizeApiKeys(t),this.isLocked=!1,this.updateAccessTime(),this.setupAutoLock(),s}catch(e){throw new a("Failed to create vault",{originalError:e})}}loadVault(e){if(!this.cryptoEngine.validateEncryptionParams(e))throw new a("Invalid vault structure");this.encryptedVault=e,this.isLocked=!0,this.decryptedKeys=null}async unlock(e){if(!this.encryptedVault)throw new a("No vault loaded");if(this.failedAttempts>=this.maxFailedAttempts)throw new n("Vault is locked due to too many failed attempts");this.validatePassword(e);try{const t=await this.cryptoEngine.decrypt(this.encryptedVault,e);this.validateApiKeys(t),this.decryptedKeys=this.normalizeApiKeys(t),this.isLocked=!1,this.failedAttempts=0,this.updateAccessTime(),this.setupAutoLock()}catch(e){throw this.failedAttempts++,this.failedAttempts>=this.maxFailedAttempts&&(this.lockVault(),setTimeout(()=>{this.failedAttempts=0},this.lockoutDuration)),new n("Failed to unlock vault - invalid password")}}lock(){this.lockVault()}getApiKey(e){if(this.isLocked||!this.decryptedKeys)throw new a("Vault is locked - please unlock first");this.updateAccessTime();const t=this.decryptedKeys[e];if(!t)throw new a(`API key for provider '${e}' not found`);return t}async setApiKey(e,t,s){if(this.isLocked||!this.decryptedKeys)throw new a("Vault is locked - please unlock first");if(!e||!t)throw new a("Provider and API key cannot be empty");this.decryptedKeys[e]=t;const i=await this.cryptoEngine.encrypt(this.decryptedKeys,s,this.options);this.encryptedVault=i,this.updateAccessTime()}async removeApiKey(e,t){if(this.isLocked||!this.decryptedKeys)throw new a("Vault is locked - please unlock first");if(!this.decryptedKeys[e])throw new a(`API key for provider '${e}' not found`);delete this.decryptedKeys[e];const s=await this.cryptoEngine.encrypt(this.decryptedKeys,t,this.options);this.encryptedVault=s,this.updateAccessTime()}async rotateKey(e,t,s={}){if(this.isLocked||!this.decryptedKeys)throw new a("Vault is locked - please unlock first");if(!this.decryptedKeys[e])throw new a(`API key for provider '${e}' not found`);const{gracePeriod:i=0,password:r,notifyRotation:n=!0}=s,o=this.decryptedKeys[e];try{if(this.decryptedKeys[e]=t,this.updateAccessTime(),r&&this.encryptedVault){const e=await this.cryptoEngine.encrypt(this.decryptedKeys,r,this.options);this.encryptedVault=e}i>0&&setTimeout(()=>{if("string"==typeof o){const e=o.split("");for(let t=0;t<e.length;t++)e[t]=Math.random().toString(36).charAt(0)}},i),this.auditLog("info",`API key rotated for provider: ${e}`)}catch(t){throw this.decryptedKeys&&o&&(this.decryptedKeys[e]=o),new a(`Key rotation failed for ${e}: ${t instanceof Error?t.message:"Unknown error"}`)}}async rotateMultipleKeys(e,t={}){const s={success:[],failed:{}},{continueOnError:i=!0}=t;for(const[r,a]of Object.entries(e))try{await this.rotateKey(r,a,{...t,notifyRotation:!1}),s.success.push(r)}catch(e){const t=e instanceof Error?e.message:"Unknown error";if(s.failed[r]=t,console.error(`❌ Failed to rotate key for ${r}: ${t}`),!i)throw e}return s}setupKeyRotation(e){const{providers:t,rotationInterval:s,keyGenerator:i,password:r}=e;return setInterval(async()=>{const e={};for(const s of t)try{e[s]=await i(s)}catch(e){console.error(`Failed to generate new key for ${s}:`,e)}try{await this.rotateMultipleKeys(e,{password:r,gracePeriod:3e5,continueOnError:!0})}catch(e){console.error("❌ Scheduled key rotation failed:",e)}},s)}getStatus(){return{isLocked:this.isLocked,hasVault:!!this.encryptedVault,keyCount:this.decryptedKeys?Object.keys(this.decryptedKeys).length:0,lastAccess:this.lastAccessTime,autoLockEnabled:this.options.autoLock||!1,failedAttempts:this.failedAttempts}}getProviders(){if(this.isLocked||!this.decryptedKeys)throw new a("Vault is locked - please unlock first");return this.updateAccessTime(),Object.keys(this.decryptedKeys)}async testApiKey(e){const t=this.getApiKey(e);return!(!t||t.length<8)}exportVault(){if(!this.encryptedVault)throw new a("No vault to export");return{...this.encryptedVault}}lockVault(){this.isLocked=!0,this.decryptedKeys=null,this.clearAutoLockTimer(),this.decryptedKeys&&this.cryptoEngine.secureErase(this.decryptedKeys)}updateAccessTime(){this.lastAccessTime=new Date,this.options.autoLock&&this.setupAutoLock()}setupAutoLock(){this.clearAutoLockTimer(),this.options.autoLock&&this.options.lockTimeout&&(this.autoLockTimer=setTimeout(()=>{this.lockVault()},this.options.lockTimeout))}clearAutoLockTimer(){this.autoLockTimer&&(clearTimeout(this.autoLockTimer),this.autoLockTimer=null)}validatePassword(e){if(!e||"string"!=typeof e)throw new a("Password is required");if(e.length<12)throw new a("Password must be at least 12 characters long");const t=this.calculatePasswordEntropy(e);if(t<50)throw new a(`Password is too weak (entropy: ${t.toFixed(1)} bits). Minimum required: 50 bits`);if(this.hasWeakPatterns(e))throw new a("Password contains weak patterns")}calculatePasswordEntropy(e){const t={lowercase:/[a-z]/.test(e)?26:0,uppercase:/[A-Z]/.test(e)?26:0,digits:/[0-9]/.test(e)?10:0,symbols:/[^a-zA-Z0-9]/.test(e)?32:0},s=Object.values(t).reduce((e,t)=>e+t,0);if(0===s)return 0;return e.length*Math.log2(s)*(new Set(e).size/e.length)}hasWeakPatterns(e){return[/(.)\1{2,}/,/012|123|234|345|456|567|678|789|890/,/abc|bcd|cde|def|efg|fgh|ghi|hij|ijk|jkl|klm|lmn|mno|nop|opq|pqr|qrs|rst|stu|tuv|uvw|vwx|wxy|xyz/i,/^(password|admin|user|login|qwerty|123456|letmein)$/i,/^.{1,3}$/].some(t=>t.test(e))}validateApiKeys(e){if(!e||"object"!=typeof e)throw new a("API keys must be an object");const t=Object.keys(e);if(0===t.length)throw new a("At least one API key is required");t.forEach(t=>{const s=e[t];if(!s||"string"!=typeof s||s.length<8)throw new a(`Invalid API key for provider: ${t}`)})}sanitizeProvider(e){return e.replace(/[^a-zA-Z0-9_-]/g,"")}auditLog(e,t,s){const i=(new Date).toISOString();console[e](`[KeyVault Audit] ${i} - ${t}`,s||"")}}class d{config;constructor(e={}){if(void 0!==e.minimumSources&&e.minimumSources<1)throw new Error("Invalid configuration: minimumSources must be at least 1");if(void 0!==e.consensusThreshold&&(e.consensusThreshold<0||e.consensusThreshold>1))throw new Error("Invalid configuration: consensusThreshold must be between 0 and 1");this.config={minimumSources:2,consensusThreshold:.5,temporalWindowDays:7,confidenceWeighting:{},enableGeolocationAnalysis:!1,enablePatternDetection:!0,riskScoreWeights:{consensus:.4,recency:.3,severity:.3,sourceReliability:.2},...e}}async correlate(e){if(0===e.length)return{relatedIndicators:[],crossReferences:[],enrichmentData:{reputation:{overall:0,categories:[]}},riskFactors:[],patterns:[],correlationScore:0,consensusLevel:"weak",riskScore:0,sources:[],indicators:[]};const t=[...new Set(e.map(e=>e.source))],s=Math.min(e.length/this.config.minimumSources,1),i=s>.7?"strong":s>.4?"moderate":"weak",r=e.reduce((e,t)=>e+t.confidence,0)/e.length;return{relatedIndicators:e,crossReferences:[],enrichmentData:{reputation:{overall:100*r,categories:[]}},riskFactors:[],patterns:[],correlationScore:s,consensusLevel:i,riskScore:r,sources:t,indicators:e}}exportResult(e,t="json"){switch(t){case"json":return JSON.stringify({...e,timestamp:(new Date).toISOString()},null,2);case"stix":{const t={type:"bundle",id:`bundle--${Date.now()}`,spec_version:"2.1",objects:[{type:"indicator",id:`indicator--${Date.now()}`,created:(new Date).toISOString(),modified:(new Date).toISOString(),pattern:e.relatedIndicators?.[0]?.value||"unknown",labels:["malicious-activity"],confidence:Math.round(100*(e.correlationScore||0))}]};return JSON.stringify(t,null,2)}case"csv":return"type,value,confidence,sources,risk_score\n"+(e.relatedIndicators||[]).map(t=>`${t.type},${t.value},${t.confidence},"${(e.sources||[]).join(";")}",${e.riskScore||0}`).join("\n");default:throw new Error(`Unsupported export format: ${t}`)}}addIntegration(e,t){}async shareResult(e,t){}}var m;function p(){}function f(){f.init.call(this)}function y(e){return void 0===e._maxListeners?f.defaultMaxListeners:e._maxListeners}function g(e,t,s,i){var r,a,n,o;if("function"!=typeof s)throw new TypeError('"listener" argument must be a function');if((a=e._events)?(a.newListener&&(e.emit("newListener",t,s.listener?s.listener:s),a=e._events),n=a[t]):(a=e._events=new p,e._eventsCount=0),n){if("function"==typeof n?n=a[t]=i?[s,n]:[n,s]:i?n.unshift(s):n.push(s),!n.warned&&(r=y(e))&&r>0&&n.length>r){n.warned=!0;var c=new Error("Possible EventEmitter memory leak detected. "+n.length+" "+t+" listeners added. Use emitter.setMaxListeners() to increase limit");c.name="MaxListenersExceededWarning",c.emitter=e,c.type=t,c.count=n.length,o=c,"function"==typeof console.warn&&console.warn(o)}}else n=a[t]=s,++e._eventsCount;return e}function w(e,t,s){var i=!1;function r(){e.removeListener(t,r),i||(i=!0,s.apply(e,arguments))}return r.listener=s,r}function v(e){var t=this._events;if(t){var s=t[e];if("function"==typeof s)return 1;if(s)return s.length}return 0}function _(e,t){for(var s=new Array(t);t--;)s[t]=e[t];return s}p.prototype=Object.create(null),f.EventEmitter=f,f.usingDomains=!1,f.prototype.domain=void 0,f.prototype._events=void 0,f.prototype._maxListeners=void 0,f.defaultMaxListeners=10,f.init=function(){this.domain=null,f.usingDomains&&(!m.active||this instanceof m.Domain||(this.domain=m.active)),this._events&&this._events!==Object.getPrototypeOf(this)._events||(this._events=new p,this._eventsCount=0),this._maxListeners=this._maxListeners||void 0},f.prototype.setMaxListeners=function(e){if("number"!=typeof e||e<0||isNaN(e))throw new TypeError('"n" argument must be a positive number');return this._maxListeners=e,this},f.prototype.getMaxListeners=function(){return y(this)},f.prototype.emit=function(e){var t,s,i,r,a,n,o,c="error"===e;if(n=this._events)c=c&&null==n.error;else if(!c)return!1;if(o=this.domain,c){if(t=arguments[1],!o){if(t instanceof Error)throw t;var u=new Error('Uncaught, unspecified "error" event. ('+t+")");throw u.context=t,u}return t||(t=new Error('Uncaught, unspecified "error" event')),t.domainEmitter=this,t.domain=o,t.domainThrown=!1,o.emit("error",t),!1}if(!(s=n[e]))return!1;var h="function"==typeof s;switch(i=arguments.length){case 1:!function(e,t,s){if(t)e.call(s);else for(var i=e.length,r=_(e,i),a=0;a<i;++a)r[a].call(s)}(s,h,this);break;case 2:!function(e,t,s,i){if(t)e.call(s,i);else for(var r=e.length,a=_(e,r),n=0;n<r;++n)a[n].call(s,i)}(s,h,this,arguments[1]);break;case 3:!function(e,t,s,i,r){if(t)e.call(s,i,r);else for(var a=e.length,n=_(e,a),o=0;o<a;++o)n[o].call(s,i,r)}(s,h,this,arguments[1],arguments[2]);break;case 4:!function(e,t,s,i,r,a){if(t)e.call(s,i,r,a);else for(var n=e.length,o=_(e,n),c=0;c<n;++c)o[c].call(s,i,r,a)}(s,h,this,arguments[1],arguments[2],arguments[3]);break;default:for(r=new Array(i-1),a=1;a<i;a++)r[a-1]=arguments[a];!function(e,t,s,i){if(t)e.apply(s,i);else for(var r=e.length,a=_(e,r),n=0;n<r;++n)a[n].apply(s,i)}(s,h,this,r)}return!0},f.prototype.addListener=function(e,t){return g(this,e,t,!1)},f.prototype.on=f.prototype.addListener,f.prototype.prependListener=function(e,t){return g(this,e,t,!0)},f.prototype.once=function(e,t){if("function"!=typeof t)throw new TypeError('"listener" argument must be a function');return this.on(e,w(this,e,t)),this},f.prototype.prependOnceListener=function(e,t){if("function"!=typeof t)throw new TypeError('"listener" argument must be a function');return this.prependListener(e,w(this,e,t)),this},f.prototype.removeListener=function(e,t){var s,i,r,a,n;if("function"!=typeof t)throw new TypeError('"listener" argument must be a function');if(!(i=this._events))return this;if(!(s=i[e]))return this;if(s===t||s.listener&&s.listener===t)0===--this._eventsCount?this._events=new p:(delete i[e],i.removeListener&&this.emit("removeListener",e,s.listener||t));else if("function"!=typeof s){for(r=-1,a=s.length;a-- >0;)if(s[a]===t||s[a].listener&&s[a].listener===t){n=s[a].listener,r=a;break}if(r<0)return this;if(1===s.length){if(s[0]=void 0,0===--this._eventsCount)return this._events=new p,this;delete i[e]}else!function(e,t){for(var s=t,i=s+1,r=e.length;i<r;s+=1,i+=1)e[s]=e[i];e.pop()}(s,r);i.removeListener&&this.emit("removeListener",e,n||t)}return this},f.prototype.off=function(e,t){return this.removeListener(e,t)},f.prototype.removeAllListeners=function(e){var t,s;if(!(s=this._events))return this;if(!s.removeListener)return 0===arguments.length?(this._events=new p,this._eventsCount=0):s[e]&&(0===--this._eventsCount?this._events=new p:delete s[e]),this;if(0===arguments.length){for(var i,r=Object.keys(s),a=0;a<r.length;++a)"removeListener"!==(i=r[a])&&this.removeAllListeners(i);return this.removeAllListeners("removeListener"),this._events=new p,this._eventsCount=0,this}if("function"==typeof(t=s[e]))this.removeListener(e,t);else if(t)do{this.removeListener(e,t[t.length-1])}while(t[0]);return this},f.prototype.listeners=function(e){var t,s=this._events;return s&&(t=s[e])?"function"==typeof t?[t.listener||t]:function(e){for(var t=new Array(e.length),s=0;s<t.length;++s)t[s]=e[s].listener||e[s];return t}(t):[]},f.listenerCount=function(e,t){return"function"==typeof e.listenerCount?e.listenerCount(t):v.call(e,t)},f.prototype.listenerCount=v,f.prototype.eventNames=function(){return this._eventsCount>0?Reflect.ownKeys(this._events):[]};class A extends f{config;state="CLOSED";failureCount=0;successCount=0;lastFailureTime=null;lastSuccessTime=null;stateChangeTime=Date.now();requestHistory=[];responseTimes=[];constructor(e={}){super(),this.config={failureThreshold:5,successThreshold:3,timeout:6e4,monitoringWindow:6e4,volumeThreshold:10,errorFilter:()=>!0,...e},setInterval(()=>this.cleanupOldRecords(),this.config.monitoringWindow)}async execute(e){if("OPEN"===this.state){if(!this.shouldAttemptReset()){const e=new Error("Circuit breaker is OPEN");throw this.recordRequest(!1,0,e.message),e}this.setState("HALF_OPEN")}const t=Date.now();try{const s=await e(),i=Date.now()-t;return this.onSuccess(i),s}catch(e){const s=Date.now()-t;throw this.onFailure(e,s),e}}async executeWithRetry(e,t=3,s=1e3){let i;for(let r=0;r<=t;r++)try{return await this.execute(e)}catch(e){if(i=e,"OPEN"===this.state)throw e;if(r===t)throw e;const a=s*Math.pow(2,r);await this.sleep(a)}throw i}async executeBatch(e,t={}){const{maxConcurrency:s=5,failFast:i=!1,continueOnFailure:r=!0}=t,a=[],n=[];for(let t=0;t<e.length;t++){const o=e[t],c=o?this.execute(o):Promise.reject(new Error("No function provided")).then(e=>{a[t]={success:!0,result:e}}).catch(e=>{if(a[t]={success:!1,error:e},i&&!r)throw e});if(n.push(c),n.length>=s){await Promise.race(n);n.filter(e=>e===Promise.resolve()).forEach(e=>{const t=n.indexOf(e);t>-1&&n.splice(t,1)})}}return await Promise.allSettled(n),a}onSuccess(e){this.recordRequest(!0,e),this.lastSuccessTime=Date.now(),"HALF_OPEN"===this.state?(this.successCount++,this.successCount>=this.config.successThreshold&&(this.setState("CLOSED"),this.reset())):"CLOSED"===this.state&&(this.failureCount=Math.max(0,this.failureCount-1)),this.emit("success",{responseTime:e,state:this.state})}onFailure(e,t){!this.config.errorFilter||this.config.errorFilter(e)?(this.recordRequest(!1,t,e.message),this.lastFailureTime=Date.now(),this.failureCount++,("HALF_OPEN"===this.state||this.shouldOpen())&&this.setState("OPEN"),this.emit("failure",{error:e.message,responseTime:t,state:this.state,failureCount:this.failureCount})):this.recordRequest(!1,t,"filtered-error")}shouldOpen(){if("OPEN"===this.state)return!1;return!(this.getRecentRequests().length<this.config.volumeThreshold)&&this.failureCount>=this.config.failureThreshold}shouldAttemptReset(){return null!==this.lastFailureTime&&Date.now()-this.lastFailureTime>=this.config.timeout}setState(e){if(this.state!==e){const t=this.state;this.state=e,this.stateChangeTime=Date.now(),this.config.onStateChange&&this.config.onStateChange(e),this.emit("stateChange",{from:t,to:e,timestamp:this.stateChangeTime})}}reset(){this.failureCount=0,this.successCount=0,this.lastFailureTime=null}recordRequest(e,t,s){const i={timestamp:Date.now(),success:e,responseTime:t,error:s||""};this.requestHistory.push(i),this.responseTimes.push(t),this.responseTimes.length>1e3&&(this.responseTimes=this.responseTimes.slice(-1e3)),this.emit("request",i)}getRecentRequests(){const e=Date.now()-this.config.monitoringWindow;return this.requestHistory.filter(t=>t.timestamp>=e)}cleanupOldRecords(){const e=Date.now()-this.config.monitoringWindow;this.requestHistory=this.requestHistory.filter(t=>t.timestamp>=e)}calculatePercentile(e,t){if(0===e.length)return 0;const s=[...e].sort((e,t)=>e-t),i=Math.ceil(t/100*s.length)-1;return s[Math.max(0,Math.min(i,s.length-1))]??0}sleep(e){return new Promise(t=>setTimeout(t,e))}getStats(){const e=this.getRecentRequests(),t=e.filter(e=>!e.success),s=e.filter(e=>e.success),i=this.responseTimes.length>0?{average:this.responseTimes.reduce((e,t)=>e+t,0)/this.responseTimes.length,min:Math.min(...this.responseTimes),max:Math.max(...this.responseTimes),p95:this.calculatePercentile(this.responseTimes,95),p99:this.calculatePercentile(this.responseTimes,99)}:{average:0,min:0,max:0,p95:0,p99:0};return{state:this.state,failureCount:this.failureCount,successCount:this.successCount,totalRequests:this.requestHistory.length,lastFailureTime:this.lastFailureTime,lastSuccessTime:this.lastSuccessTime,stateChangeTime:this.stateChangeTime,requestStats:{total:e.length,failures:t.length,successes:s.length,timeouts:t.filter(e=>e.error?.includes("timeout")).length,circuitOpen:e.filter(e=>"Circuit breaker is OPEN"===e.error).length},responseTimeStats:i}}getState(){return this.state}getConfig(){return{...this.config}}updateConfig(e){this.config={...this.config,...e},this.emit("configUpdate",this.config)}open(){this.setState("OPEN"),this.lastFailureTime=Date.now()}close(){this.setState("CLOSED"),this.reset()}halfOpen(){this.setState("HALF_OPEN"),this.successCount=0}isHealthy(){if("OPEN"===this.state)return!1;const e=this.getRecentRequests();if(e.length<this.config.volumeThreshold)return!0;return e.filter(e=>!e.success).length/e.length<this.config.failureThreshold/this.config.volumeThreshold}getHealthScore(){if("OPEN"===this.state)return 0;const e=this.getRecentRequests();if(0===e.length)return 100;const t=100*(e.filter(e=>e.success).length/e.length),s=this.responseTimes.reduce((e,t)=>e+t,0)/this.responseTimes.length,i=20*Math.min(s/5e3,1);return Math.max(0,t-i)}exportMetrics(){const e=this.getStats();return{circuit_breaker_state:"CLOSED"===this.state?0:"HALF_OPEN"===this.state?1:2,circuit_breaker_failure_count:e.failureCount,circuit_breaker_success_count:e.successCount,circuit_breaker_total_requests:e.totalRequests,circuit_breaker_recent_failures:e.requestStats.failures,circuit_breaker_recent_successes:e.requestStats.successes,circuit_breaker_response_time_avg:e.responseTimeStats.average,circuit_breaker_response_time_p95:e.responseTimeStats.p95,circuit_breaker_response_time_p99:e.responseTimeStats.p99,circuit_breaker_health_score:this.getHealthScore()}}}class L{axiosInstance;config;lastFetchTime=0;MIN_FETCH_INTERVAL=3e5;stats;cache=new Map;promiseCache=new Map;DEFAULT_CACHE_TTL=6e5;constructor(){this.config={name:"URLhaus",type:"csv",endpoint:"https://urlhaus.abuse.ch/downloads/csv_recent/",authentication:{type:"none",required:!1},rateLimit:{requestsPerHour:12,burstLimit:1},enabled:!0,priority:"high",sslPinning:!0,timeout:3e4,retries:3,cacheTTL:this.DEFAULT_CACHE_TTL},this.stats={lastFetch:null,nextAllowedFetch:new Date(Date.now()+this.MIN_FETCH_INTERVAL),rateLimit:this.config.rateLimit,successCount:0,errorCount:0,requestsProcessed:0},this.axiosInstance=s.create({timeout:this.config.timeout||3e4,headers:{"User-Agent":"TrojanHorse.js/1.0.1 (Threat Intelligence Library)",Accept:"text/csv","Cache-Control":"no-cache"},httpsAgent:void 0,validateStatus:e=>e>=200&&e<300}),this.setupInterceptors()}updateConfig(e){this.config={...this.config,...e},e.timeout&&(this.axiosInstance.defaults.timeout=e.timeout)}async fetchThreatData(){const e="recent_urls",t=this.getCachedData(e);if(t)return t;if(this.promiseCache.has(e))return await this.promiseCache.get(e);const s=this.performFetch(e);this.promiseCache.set(e,s);try{return await s}finally{this.promiseCache.delete(e)}}getConfig(){return{...this.config}}async checkAvailability(){try{return 200===(await this.axiosInstance.head(this.config.endpoint)).status}catch(e){return!1}}getStats(){return{lastFetch:this.lastFetchTime?new Date(this.lastFetchTime):null,nextAllowedFetch:new Date(this.lastFetchTime+this.MIN_FETCH_INTERVAL),rateLimit:this.config.rateLimit,successCount:this.stats.successCount,errorCount:this.stats.errorCount,requestsProcessed:this.stats.requestsProcessed}}getCachedData(e){const t=this.cache.get(e);if(!t)return null;const s=Date.now(),i=this.config.cacheTTL||this.DEFAULT_CACHE_TTL;return s-t.timestamp>i?(this.cache.delete(e),null):t.data}setCachedData(e,t){this.cache.set(e,{data:t,timestamp:Date.now()})}setupInterceptors(){this.axiosInstance.interceptors.request.use(e=>(process.env.NODE_ENV,e),e=>Promise.reject(e)),this.axiosInstance.interceptors.response.use(e=>{const t=e.headers["content-length"];if(t&&parseInt(t)>52428800)throw new r("Response too large","RESPONSE_TOO_LARGE",e.status);return e},e=>Promise.reject(e))}checkRateLimit(){const e=Date.now()-this.lastFetchTime;if(e<this.MIN_FETCH_INTERVAL){const t=this.MIN_FETCH_INTERVAL-e;throw new o(`URLhaus rate limit: must wait ${Math.ceil(t/1e3)} seconds`,t)}}async performFetch(e){let t=null;const i=this.config.retries||3;for(let a=0;a<=i;a++)try{const t=(await this.axiosInstance.get(this.config.endpoint)).data,s=this.parseCSV(t),i=this.convertToThreatIndicators(s);this.lastFetchTime=Date.now(),this.stats.successCount++,this.stats.requestsProcessed++,this.stats.lastFetch=new Date;const r={source:this.config.name,timestamp:new Date,indicators:i,metadata:{totalCount:i.length,totalIndicators:i.length,requestsProcessed:this.stats.requestsProcessed}};return this.setCachedData(e,r),r}catch(e){if(t=e,this.stats.errorCount++,a<i){await new Promise(e=>setTimeout(e,1e3*Math.pow(2,a)));continue}const n=e,c=s.isAxiosError(n)||n.isAxiosError||"AxiosError"===n.name,u=n.response&&"object"==typeof n.response,h=n.config||n.request||u;if(c||h||u){const e=n.response,t=e?.status,s=e?.statusText||"HTTP Error";if(429===t)throw new o("URLhaus rate limit exceeded",e.headers?.["retry-after"]?1e3*parseInt(e.headers["retry-after"]):3e5);if(t&&t>=500)throw new r(`URLhaus server error: ${t} ${s}`,"FEED_ERROR",t,{provider:"URLhaus",originalError:n});if(t&&t>=400)throw new r(`URLhaus HTTP error: ${t} ${s}`,"HTTP_ERROR",t,{provider:"URLhaus",originalError:n});if("ECONNABORTED"===n.code||n.message&&n.message.includes("timeout"))throw new r(`URLhaus request timeout: ${n.message||"Request timeout"}`,"TIMEOUT_ERROR",void 0,{provider:"URLhaus",originalError:n});throw new r(`URLhaus feed error: ${n.message||"Request failed"}`,"FEED_ERROR",t,{provider:"URLhaus",originalError:n})}if(n.message&&(n.message.includes("Network Error")||n.message.includes("fetch")))throw new r(`URLhaus network error: ${n.message}`,"NETWORK_ERROR",void 0,{provider:"URLhaus",originalError:n});throw new r("Unknown error fetching URLhaus data","UNKNOWN_ERROR",void 0,{provider:"URLhaus",originalError:n})}throw t||new Error("Maximum retries exceeded")}parseCSV(e){const t=[],s=e.trim().split("\n").filter(e=>!e.startsWith("#")&&e.trim());for(const e of s)try{const s=this.parseCSVLine(e);s&&t.push(s)}catch(e){this.stats.errorCount++}return t}parseCSVLine(e){const t=this.parseCSVColumns(e);if(t.length<8)return null;const[s,i,r,a,n,o,,c]=t;return s&&i&&r?{id:s.trim(),dateAdded:new Date(i.trim()),url:r.trim(),urlStatus:a?.trim()||"offline",threat:n?.trim()||"unknown",tags:o?o.split(",").map(e=>e.trim()).filter(Boolean):[],reporter:c?.trim()||"unknown"}:null}parseCSVColumns(e){const t=[];let s="",i=!1;for(let r=0;r<e.length;r++){const a=e[r];'"'===a?i=!i:","!==a||i?s+=a:(t.push(s),s="")}return t.push(s),t.map(e=>e.replace(/^"(.*)"$/,"$1"))}convertToThreatIndicators(e){const t=[];for(const s of e){const e=this.determineSeverity(s.threat,s.tags),i={type:"url",value:s.url,confidence:.85,firstSeen:s.dateAdded,lastSeen:s.dateAdded,source:"URLhaus",tags:[s.threat,...s.tags].filter(Boolean),malwareFamily:this.extractMalwareFamily(s.threat,s.tags),severity:e};t.push(i);try{const i=new URL(s.url);if(i.hostname&&i.hostname!==s.url){const r={type:"domain",value:i.hostname,confidence:.75,firstSeen:s.dateAdded,lastSeen:s.dateAdded,source:"URLhaus",tags:[s.threat,...s.tags,"derived-from-url"].filter(Boolean),malwareFamily:this.extractMalwareFamily(s.threat,s.tags),severity:e};t.push(r)}if(/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(i.hostname)){const r={type:"ip",value:i.hostname,confidence:.8,firstSeen:s.dateAdded,lastSeen:s.dateAdded,source:"URLhaus",tags:[s.threat,...s.tags,"derived-from-url"].filter(Boolean),malwareFamily:this.extractMalwareFamily(s.threat,s.tags),severity:e};t.push(r)}}catch(e){}}return t}determineSeverity(e,t){const s=[e,...t].join(" ").toLowerCase();return["apt","targeted","zero-day"].some(e=>s.includes(e))?"critical":["ransomware","banking","stealer","trojan"].some(e=>s.includes(e))?"high":["adware","potentially unwanted","pup"].some(e=>s.includes(e))?"low":"medium"}extractMalwareFamily(e,t){const s=[/emotet/i,/trickbot/i,/dridex/i,/qakbot/i,/cobalt\s*strike/i,/metasploit/i,/mirai/i,/locky/i,/wannacry/i,/malware/i,/trojan/i,/ransomware/i,/phishing/i,/exploit/i],i=[e,...t].join(" ");for(const e of s){const t=i.match(e);if(t)return t[0].toLowerCase()}const r=e?.toLowerCase()?.trim();if(r&&"unknown"!==r&&!r.includes("-"))return r}}class T{config;httpClient;lastFetch=null;rateLimit;constructor(e={}){this.config={name:"AlienVault OTX",endpoint:"https://otx.alienvault.com/api/v1/pulses/subscribed",rateLimit:{requestsPerHour:e.apiKey?1e3:100,burstLimit:5,retryAfter:5e3},timeout:3e4,retries:3,...e},this.rateLimit={requestsPerHour:this.config.rateLimit?.requestsPerHour||100,requestCount:0,resetTime:new Date(Date.now()+36e5)},this.httpClient=s.create({baseURL:"https://otx.alienvault.com/api/v1",timeout:this.config.timeout||3e4,headers:{"User-Agent":"TrojanHorse.js/1.0.1 (Security Research)",Accept:"application/json","Content-Type":"application/json",...this.config.apiKey&&{"X-OTX-API-KEY":this.config.apiKey}},validateStatus:e=>e<500,maxRedirects:5}),this.httpClient.interceptors.response.use(e=>e,e=>{if(429===e.response?.status){const t=parseInt(e.response.headers["retry-after"]||"60");throw new o("AlienVault OTX rate limit exceeded",1e3*t,{provider:"AlienVault OTX",endpoint:e.config?.url,resetTime:new Date(Date.now()+1e3*t)})}return Promise.reject(e)})}async fetchThreatData(){return this.fetchThreats({modifiedSince:this.lastFetch||new Date(Date.now()-864e5),limit:100,minimumConfidence:.5})}async fetchThreats(e={}){try{await this.checkRateLimit();const{modifiedSince:t,limit:s=100,minimumConfidence:i=.5}=e,a={limit:s.toString(),page:"1"};t&&(a.modified_since=t.toISOString());const n=await this.httpClient.get("/pulses/subscribed",{params:a});if(200!==n.status)throw new r(`AlienVault OTX API returned status ${n.status}`,"FEED_API_ERROR",n.status);const o=n.data,c=[];for(const e of o.results)for(const t of e.indicators){if(!t.is_active)continue;const s=this.convertOTXIndicator(t,e,i);s&&c.push(s)}return this.lastFetch=new Date,{source:this.config.name,timestamp:new Date,indicators:c,metadata:{totalPulses:o.count,totalIndicators:c.length,hasMore:!!o.next,nextPage:o.next,rateLimit:{remaining:this.rateLimit.requestsPerHour-this.rateLimit.requestCount,resetTime:this.rateLimit.resetTime,limit:this.rateLimit.requestsPerHour}}}}catch(e){if(e instanceof o||e instanceof r)throw e;throw new r(`Failed to fetch from AlienVault OTX: ${e instanceof Error?e.message:"Unknown error"}`,"FEED_FETCH_FAILED",500,{originalError:e instanceof Error?e.message:String(e),provider:"AlienVault OTX"})}}convertOTXIndicator(e,t,s){try{const i={IPv4:"ip",IPv6:"ip",domain:"domain",hostname:"domain",URL:"url",email:"email","FileHash-MD5":"hash","FileHash-SHA1":"hash","FileHash-SHA256":"hash","FileHash-PEHASH":"hash","FileHash-IMPHASH":"hash",FilePath:"file_path"}[e.type];if(!i)return null;const r=this.calculateConfidence(e,t);if(r<s)return null;const a=this.determineSeverity(t,e);return{type:i,value:e.indicator,confidence:r,firstSeen:new Date(e.created),lastSeen:new Date(t.modified),source:`AlienVault OTX - ${t.author_name}`,tags:[...t.tags,...e.role?[e.role]:[],...t.adversary?[t.adversary]:[]].filter(Boolean),malwareFamily:this.extractMalwareFamily(t),severity:a}}catch(t){return console.warn(`Failed to parse OTX indicator ${e.id}:`,t),null}}calculateConfidence(e,t){let s=.5;e.is_active&&(s+=.1),"public"===e.access_type&&(s+=.1),e.observations>0&&(s+=Math.min(.05*e.observations,.2)),e.description&&e.description.length>10&&(s+=.05);const i=(Date.now()-new Date(e.created).getTime())/864e5;i<7?s+=.1:i<30&&(s+=.05);return s+={red:.3,amber:.2,green:.1,white:.05}[t.TLP]||0,Math.min(s,1)}determineSeverity(e,t){if("red"===e.TLP)return"critical";if("amber"===e.TLP)return"high";if(t.role&&["command_and_control","malware_hosting","exploit_kit","ransomware","trojan","backdoor"].includes(t.role))return"high";return t.role&&["phishing","bruteforce","web_attack","scanning_host"].includes(t.role)||t.observations>10?"medium":"low"}extractMalwareFamily(e){const t=e.name.toLowerCase();return["emotet","trickbot","dridex","qakbot","cobalt strike","ransomware","trojan","backdoor","rootkit","worm"].find(e=>t.includes(e.toLowerCase()))||e.adversary||void 0}async checkRateLimit(){const e=new Date;if(e>this.rateLimit.resetTime&&(this.rateLimit.requestCount=0,this.rateLimit.resetTime=new Date(e.getTime()+36e5)),this.rateLimit.requestCount>=this.rateLimit.requestsPerHour){const t=this.rateLimit.resetTime.getTime()-e.getTime();throw new o("AlienVault OTX rate limit exceeded",t,{provider:"AlienVault OTX",requestsPerHour:this.rateLimit.requestsPerHour,resetTime:this.rateLimit.resetTime})}this.rateLimit.requestCount++}async testConnection(){try{return 200===(await this.httpClient.get("/pulses/subscribed",{params:{limit:"1"}})).status}catch(e){return!1}}getStats(){const e=new Date(Math.max(Date.now()+(this.config.rateLimit?.retryAfter||5e3),this.rateLimit.resetTime.getTime()));return{lastFetch:this.lastFetch,nextAllowedFetch:e,rateLimit:this.config.rateLimit,hasApiKey:!!this.config.apiKey}}getConfig(){return{...this.config,apiKey:this.config.apiKey?"***masked***":""}}}class k{config;apiKey;baseUrl="https://cti-api.crowdsec.net/v2";rateLimitRemaining=1e3;rateLimitReset=new Date;requestCount=0;dailyLimit=1e3;constructor(e={}){this.config={name:"CrowdSec CTI",type:"api",endpoint:"https://cti-api.crowdsec.net/v2",authentication:{type:"api_key",required:!0,header:"x-api-key",credentials:{}},rateLimit:{requestsPerHour:1e3,burstLimit:10,retryAfter:1e3},enabled:!0,priority:"high",sslPinning:!0,timeout:3e4,retries:3,...e},this.apiKey=e.apiKey||process.env.CROWDSEC_API_KEY||"",this.apiKey||console.warn("CrowdSec API key not provided. Some features may be limited."),this.apiKey&&(this.dailyLimit=1e3)}async fetchThreatData(){return this.fetchRecentThreats()}async fetchRecentThreats(){try{const e=await s.get(`${this.baseUrl}/smoke/`,{headers:{"User-Agent":"TrojanHorse.js/1.0.1",Accept:"application/json",...this.apiKey&&{"x-api-key":this.apiKey}},timeout:this.config.timeout||3e4}),t=Array.isArray(e.data)?e.data:[e.data],i=[];for(const e of t){const t=this.mapToThreatIndicator(e.ip,e);t&&i.push(t)}return{source:this.config.name,timestamp:new Date,indicators:i,metadata:{totalCount:i.length,totalIndicators:i.length,hasMore:!1,requestsProcessed:1}}}catch(e){throw new Error(`Failed to fetch CrowdSec smoke data: ${e.message}`)}}async fetchIPInfo(e){if(!this.apiKey)throw new Error("CrowdSec API key is required for IP information");await this.checkRateLimit();try{const t=await s.get(`${this.baseUrl}/cti/${e}`,{headers:{"x-api-key":this.apiKey,"User-Agent":"TrojanHorse.js/1.0.1",Accept:"application/json"},timeout:this.config.timeout||3e4});return this.updateRateLimitInfo(t.headers),this.requestCount++,t.data}catch(t){if(429===t.response?.status){const e=parseInt(t.response.headers["retry-after"]||"60");throw new Error(`Rate limit exceeded. Retry after ${e} seconds`)}if(404===t.response?.status)throw new Error(`IP ${e} not found in CrowdSec database`);if(401===t.response?.status)throw new Error("Invalid CrowdSec API key");throw new Error(`CrowdSec API error: ${t.message}`)}}async fetchSmokeData(e){await this.checkRateLimit();try{const t=(this.apiKey,`${this.baseUrl}/smoke/${e}`),i={"User-Agent":"TrojanHorse.js/1.0.1",Accept:"application/json"};this.apiKey&&(i["x-api-key"]=this.apiKey);const r=await s.get(t,{headers:i,timeout:this.config.timeout||3e4});return this.updateRateLimitInfo(r.headers),this.requestCount++,r.data}catch(t){if(429===t.response?.status){const e=parseInt(t.response.headers["retry-after"]||"60");throw new Error(`Rate limit exceeded. Retry after ${e} seconds`)}if(404===t.response?.status)throw new Error(`IP ${e} not found in CrowdSec smoke database`);throw new Error(`CrowdSec Smoke API error: ${t.message}`)}}async fetchThreats(e={}){const{ips:t=[],includeSmoke:s=!0,maxResults:i=100}=e,r=[],a=[];if(0===t.length)return console.warn("CrowdSec requires specific IP addresses to check"),{source:this.config.name,timestamp:new Date,indicators:[],metadata:{totalCount:0,totalIndicators:0,hasMore:!1,errors:["No IP addresses provided for CrowdSec lookup"]}};for(const e of t.slice(0,i)){try{let t;t=this.apiKey&&!s?await this.fetchIPInfo(e):await this.fetchSmokeData(e);const i=this.mapToThreatIndicator(e,t);i&&r.push(i)}catch(t){a.push(`${e}: ${t.message}`),console.warn(`Failed to fetch CrowdSec data for ${e}:`,t.message)}if(this.rateLimitRemaining<=1){console.warn("CrowdSec rate limit approaching, stopping batch processing");break}}return{source:this.config.name,timestamp:new Date,indicators:r,metadata:{totalCount:r.length,totalIndicators:r.length,hasMore:!1,requestsProcessed:t.length,errors:a.length>0?a:[],rateLimit:{remaining:this.rateLimitRemaining,resetTime:this.rateLimitReset,limit:1e3}}}}mapToThreatIndicator(e,t){if(!this.calculateThreatLevel(t))return null;const s=this.calculateConfidence(t),i=t.behaviors||[],r=t.attack_details||[];return{type:"ip",value:e,confidence:s,severity:this.mapSeverity(t.scores?.overall?.threat||0),firstSeen:t.history?.first_seen?new Date(t.history.first_seen):new Date,lastSeen:t.history?.last_seen?new Date(t.history.last_seen):new Date,source:this.config.name,tags:[...i.map(e=>e.name),...r.map(e=>e.name),"crowdsec-cti"].filter(Boolean),metadata:{crowdsec:{scores:t.scores,country:"location"in t?t.location?.country:t.country,city:"location"in t?t.location?.city:t.city,asn:t.as_num,asName:t.as_name,behaviors:i.map(e=>({name:e.name,label:e.label,description:e.description})),attackDetails:r.map(e=>({name:e.name,label:e.label,description:e.description,references:e.references})),backgroundNoise:"background_noise"in t&&t.background_noise,ipRange:"ip_range"in t?t.ip_range:void 0,ipRangeScore:"ip_range_score"in t?t.ip_range_score:void 0}},description:this.generateDescription(i,r),malwareFamily:r.length>0?r[0]?.name||"unknown":void 0}}calculateThreatLevel(e){const t=e.scores?.overall;if(!t)return!1;const s=t.threat>3,i=t.aggressiveness>3,r=(e.attack_details?.length||0)>0,a=t.trust<2,n=t.total>3;return s||i||r||a&&n}calculateConfidence(e){const t=e.scores?.overall;if(!t)return.3;let s=.5;s+=t.threat/10*.3;const i=e.attack_details?.length||0;if(s+=Math.min(.1*i,.2),e.history){const t=e.history.days_age||0;t<7?s+=.1:t<30&&(s+=.05)}return"background_noise"in e&&e.background_noise&&(s-=.2),s+=t.anomaly/10*.1,Math.max(.1,Math.min(s,1))}mapSeverity(e){return e>=4?"critical":e>=3?"high":e>=2?"medium":"low"}generateDescription(e,t){const s=[];if(e.length>0){const t=e.map(e=>e.label||e.name).join(", ");s.push(`Observed behaviors: ${t}`)}if(t.length>0){const e=t.map(e=>e.label||e.name).join(", ");s.push(`Attack patterns: ${e}`)}return s.join(". ")||"Malicious IP identified by CrowdSec CTI"}async checkRateLimit(){const e=new Date;if(e.getTime()-this.rateLimitReset.getTime()>864e5&&(this.requestCount=0,this.rateLimitReset=new Date(e.getTime()+864e5)),this.requestCount>=this.dailyLimit){const t=this.rateLimitReset.getTime()-e.getTime();throw new Error(`Daily rate limit exceeded. Resets in ${Math.ceil(t/1e3/60)} minutes`)}if(this.rateLimitRemaining<=0)throw new Error("Rate limit exceeded. Please wait before making more requests")}updateRateLimitInfo(e){const t=e["x-ratelimit-remaining"],s=e["x-ratelimit-reset"];void 0!==t&&(this.rateLimitRemaining=parseInt(t)),void 0!==s&&(this.rateLimitReset=new Date(1e3*parseInt(s)))}getStats(){return{requestCount:this.requestCount,rateLimitRemaining:this.rateLimitRemaining,rateLimitReset:this.rateLimitReset,dailyLimit:this.dailyLimit,isEnabled:this.config.enabled||!1,hasApiKey:!!this.apiKey,rateLimit:this.config.rateLimit}}async testConnection(){try{const e="8.8.8.8";return this.apiKey?(await this.fetchIPInfo(e),{success:!0,message:"CrowdSec CTI API connection successful (authenticated)"}):(await this.fetchSmokeData(e),{success:!0,message:"CrowdSec Smoke API connection successful (anonymous)"})}catch(e){return{success:!1,message:`CrowdSec API connection failed: ${e.message}`,details:{hasApiKey:!!this.apiKey,endpoint:this.baseUrl,error:e.message}}}}}const C={1:"DNS Compromise",2:"DNS Poisoning",3:"Fraud Orders",4:"DDoS Attack",5:"FTP Brute-Force",6:"Ping of Death",7:"Phishing",8:"Fraud VoIP",9:"Open Proxy",10:"Web Spam",11:"Email Spam",12:"Blog Spam",13:"VPN IP",14:"Port Scan",15:"Hacking",16:"SQL Injection",17:"Spoofing",18:"Brute-Force",19:"Bad Web Bot",20:"Exploited Host",21:"Web App Attack",22:"SSH",23:"IoT Targeted"};class b{config;apiKey;baseUrl="https://api.abuseipdb.com/api/v2";rateLimitRemaining=1e3;rateLimitReset=new Date;requestCount=0;dailyLimit=1e3;constructor(e={}){if(this.config={name:"AbuseIPDB",type:"api",endpoint:"https://api.abuseipdb.com/api/v2",authentication:{type:"api_key",required:!0,header:"Key",credentials:{}},rateLimit:{requestsPerHour:1e3,burstLimit:5,retryAfter:1e3},enabled:!0,priority:"high",sslPinning:!0,timeout:3e4,retries:3,...e},this.apiKey=e.apiKey||process.env.ABUSEIPDB_API_KEY||"",!this.apiKey)throw new Error("AbuseIPDB API key is required. Set ABUSEIPDB_API_KEY environment variable or provide apiKey in config.");this.dailyLimit=1e3}async checkIP(e,t={}){const{maxAgeInDays:i=90,verbose:r=!1}=t;await this.checkRateLimit();try{const t=new URLSearchParams({ipAddress:e,maxAgeInDays:i.toString(),verbose:r.toString()}),a=await s.get(`${this.baseUrl}/check`,{headers:{Key:this.apiKey,Accept:"application/json"},params:t,timeout:this.config.timeout||3e4});return this.updateRateLimitInfo(a.headers),this.requestCount++,a.data}catch(e){if(429===e.response?.status){const t=parseInt(e.response.headers["retry-after"]||"60");throw new Error(`Rate limit exceeded. Retry after ${t} seconds`)}if(422===e.response?.status){const t=e.response.data,s=t.errors?.[0]?.detail||"Invalid parameter";throw new Error(`AbuseIPDB validation error: ${s}`)}if(401===e.response?.status)throw new Error("Invalid AbuseIPDB API key");throw new Error(`AbuseIPDB API error: ${e.message}`)}}async getReports(e,t={}){const{maxAgeInDays:i=90,perPage:r=25,page:a=1}=t;await this.checkRateLimit();try{const t=new URLSearchParams({ipAddress:e,maxAgeInDays:i.toString(),perPage:r.toString(),page:a.toString()}),n=await s.get(`${this.baseUrl}/reports`,{headers:{Key:this.apiKey,Accept:"application/json"},params:t,timeout:this.config.timeout||3e4});return this.updateRateLimitInfo(n.headers),this.requestCount++,n.data}catch(e){if(429===e.response?.status){const t=parseInt(e.response.headers["retry-after"]||"60");throw new Error(`Rate limit exceeded. Retry after ${t} seconds`)}throw new Error(`AbuseIPDB reports API error: ${e.message}`)}}async fetchThreatData(){return this.fetchThreats({ips:["185.220.101.32","192.42.116.16","194.147.102.87","45.148.10.62","89.248.174.241"],maxAgeInDays:30,confidenceThreshold:50,includeReports:!1,maxResults:50})}async fetchThreats(e={}){const{ips:t=[],maxAgeInDays:s=90,confidenceThreshold:i=25,includeReports:r=!1,maxResults:a=100}=e,n=[],o=[];if(0===t.length)return console.warn("AbuseIPDB requires specific IP addresses to check"),{source:this.config.name,timestamp:new Date,indicators:[],metadata:{totalCount:0,totalIndicators:0,hasMore:!1,errors:["No IP addresses provided for AbuseIPDB lookup"]}};for(const e of t.slice(0,a)){try{const t=await this.checkIP(e,{maxAgeInDays:s,verbose:!0});if(t.data.abuseConfidencePercentage>=i){let i;if(r&&t.data.totalReports>0)try{i=(await this.getReports(e,{maxAgeInDays:s})).data.reports}catch(t){console.warn(`Failed to fetch reports for ${e}:`,t.message)}const a=this.mapToThreatIndicator(e,t.data,i);a&&n.push(a)}}catch(t){o.push(`${e}: ${t.mess