@anomira/node-sdk 0.2.3 → 0.2.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 +42 -11
- package/dist/index.cjs +173 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +22 -2
- package/dist/index.d.ts +22 -2
- package/dist/index.js +173 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/buffer.ts","../src/geo-velocity.ts","../src/sensitive.ts","../src/types.ts","../src/agent-detection.ts","../src/behavioral-fingerprint.ts","../src/honeypot-responses.ts","../src/client.ts","../src/middleware/express.ts","../src/middleware/fastify.ts"],"names":["randomBytes","createExpressMiddleware","createFastifyPlugin"],"mappings":"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","file":"index.js","sourcesContent":["import type { SdkEvent, IngestPayload } from \"./types.js\";\n\ninterface BufferOptions {\n appId: string;\n apiKey: string;\n ingestUrl: string;\n maxBatchSize: number;\n flushIntervalMs: number;\n maxRetries: number;\n debug: boolean;\n}\n\n/**\n * EventBuffer — collects SDK events and flushes them in batches.\n *\n * Flush is triggered by whichever comes first:\n * • Buffer reaches maxBatchSize events\n * • flushIntervalMs timer fires\n * • process exits (SIGTERM / SIGINT)\n *\n * On flush failure: exponential backoff up to maxRetries.\n * After maxRetries failures: events are dropped with a warning (never crash).\n */\nexport class EventBuffer {\n private queue: SdkEvent[] = [];\n private timer: ReturnType<typeof setInterval> | null = null;\n private flushing = false;\n private readonly opts: BufferOptions;\n\n constructor(opts: BufferOptions) {\n this.opts = opts;\n this.startTimer();\n this.registerShutdownHook();\n }\n\n // ─── Public API ────────────────────────────────────────────────────────────\n\n push(event: SdkEvent): void {\n this.queue.push(event);\n this.log(`[buffer] +1 event → ${this.queue.length} queued (${event.name})`);\n if (this.queue.length >= this.opts.maxBatchSize) {\n this.log(\"[buffer] batch size reached — flushing immediately\");\n void this.flush();\n }\n }\n\n async flush(): Promise<void> {\n if (this.flushing || this.queue.length === 0) return;\n this.flushing = true;\n\n // Drain the queue atomically\n const batch = this.queue.splice(0, this.opts.maxBatchSize);\n this.log(`[buffer] flushing ${batch.length} events`);\n\n try {\n await this.sendWithRetry(batch);\n } catch (err) {\n // Last-resort: log and drop. Never propagate.\n this.warn(`[buffer] dropped ${batch.length} events after max retries:`, err);\n } finally {\n this.flushing = false;\n }\n }\n\n /** Call on graceful shutdown to flush remaining events synchronously. */\n async shutdown(): Promise<void> {\n if (this.timer) { clearInterval(this.timer); this.timer = null; }\n await this.flush();\n this.log(\"[buffer] shutdown complete\");\n }\n\n // ─── Internals ─────────────────────────────────────────────────────────────\n\n private startTimer(): void {\n this.timer = setInterval(() => {\n void this.flush();\n }, this.opts.flushIntervalMs);\n\n // Don't keep the Node process alive just for this timer\n if (this.timer.unref) this.timer.unref();\n }\n\n private registerShutdownHook(): void {\n // Increase the limit to avoid MaxListenersExceededWarning when many buffers\n // are instantiated in the same process (e.g. during tests).\n const current = process.getMaxListeners();\n process.setMaxListeners(current + 3);\n\n const handler = () => { void this.shutdown(); };\n process.once(\"SIGTERM\", handler);\n process.once(\"SIGINT\", handler);\n process.once(\"beforeExit\", handler);\n }\n\n private async sendWithRetry(events: SdkEvent[], attempt = 1): Promise<void> {\n const { appId, apiKey, ingestUrl, maxRetries } = this.opts;\n\n const payload: IngestPayload = { appId, events };\n\n try {\n const res = await fetch(ingestUrl, {\n method: \"POST\",\n redirect: \"manual\",\n headers: {\n Authorization: `Bearer ${apiKey}`,\n \"Content-Type\": \"application/json\",\n \"User-Agent\": `@sentinelapi/node-sdk/0.1.0`,\n },\n body: JSON.stringify(payload),\n signal: AbortSignal.timeout(8_000),\n });\n\n if (res.ok) {\n this.log(`[buffer] ✅ sent ${events.length} events (attempt ${attempt})`);\n return;\n }\n\n if (res.status >= 300 && res.status < 400) {\n this.warn(`[buffer] ❌ Wrong ingest URL — got redirect to ${res.headers.get(\"location\")}. Check SENTINEL_INGEST_URL.`);\n return;\n }\n\n // 4xx errors are not retryable (bad API key, schema error)\n if (res.status === 401) {\n this.warn(\"[buffer] ❌ Invalid API key — check your SENTINEL_API_KEY environment variable\");\n return;\n }\n if (res.status === 403) {\n this.warn(\"[buffer] ❌ App not found — check your SENTINEL_APP_ID environment variable\");\n return;\n }\n if (res.status >= 400 && res.status < 500) {\n this.warn(`[buffer] ⚠️ ingest rejected ${res.status} — dropping batch`);\n return;\n }\n\n throw new Error(`Ingest HTTP ${res.status}`);\n } catch (err) {\n if (attempt >= maxRetries) throw err;\n\n const delayMs = Math.min(1000 * 2 ** (attempt - 1), 16_000); // 1s, 2s, 4s, …, 16s cap\n this.log(`[buffer] retry ${attempt}/${maxRetries} in ${delayMs}ms`);\n await sleep(delayMs);\n return this.sendWithRetry(events, attempt + 1);\n }\n }\n\n private log(...args: unknown[]): void {\n if (this.opts.debug) console.log(\"[Anomira]\", ...args);\n }\n\n private warn(...args: unknown[]): void {\n console.warn(\"[Anomira]\", ...args);\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n","/**\n * Geo-velocity detector\n * ─────────────────────\n * Tracks the last known IP location per userId.\n * On each successful login, checks if the new login would require\n * physically impossible travel from the previous login location.\n *\n * Geo-lookup is delegated to the Anomira ingest service (server-side MaxMind)\n * via the configurable `geoLookupUrl`. Falls back gracefully if unreachable.\n *\n * Threshold: 900 km/h (commercial jet cruising speed).\n */\n\nexport interface GeoPoint {\n lat: number;\n lng: number;\n ip: string;\n tsMs: number;\n city?: string;\n country?: string;\n}\n\nexport interface GeoVelocityResult {\n isImpossible: boolean;\n distanceKm: number;\n speedKmH: number;\n from: GeoPoint;\n to: GeoPoint;\n}\n\n// In-memory last-login state per userId\nconst lastSeen = new Map<string, GeoPoint>();\n\n// Geo-lookup response cache: IP → GeoPoint (1h TTL)\nconst geoCache = new Map<string, { point: Omit<GeoPoint, \"tsMs\">; expiresAt: number }>();\n\nconst MAX_SPEED_KMH = 900;\n\nconst PRIVATE_RANGES = [\n /^10\\./,\n /^172\\.(1[6-9]|2\\d|3[01])\\./,\n /^192\\.168\\./,\n /^127\\./,\n /^::1$/,\n /^0\\.0\\.0\\.0$/,\n /^fc00:/i,\n /^fe80:/i,\n];\n\nfunction isPrivateIp(ip: string): boolean {\n return PRIVATE_RANGES.some((r) => r.test(ip));\n}\n\n/**\n * Lookup geo coordinates for an IP via the Anomira geo endpoint.\n * The ingest service uses MaxMind (no rate limits).\n * Returns null for private/loopback IPs or on any network failure.\n */\nasync function lookupGeo(\n ip: string,\n lookupUrl?: string,\n): Promise<Omit<GeoPoint, \"tsMs\"> | null> {\n if (!ip || isPrivateIp(ip)) return null;\n\n const cached = geoCache.get(ip);\n if (cached && cached.expiresAt > Date.now()) return cached.point;\n\n // Use the Anomira geo endpoint (MaxMind-backed, no rate limits)\n // Falls back to null on any failure — never crashes the SDK\n const url = lookupUrl ? `${lookupUrl}?ip=${encodeURIComponent(ip)}` : null;\n if (!url) return null;\n\n try {\n const res = await fetch(url, { signal: AbortSignal.timeout(2_000) });\n if (!res.ok) return null;\n\n const data = await res.json() as {\n lat?: number; lng?: number; country?: string; city?: string;\n };\n\n if (data.lat == null || data.lng == null) return null;\n\n const point: Omit<GeoPoint, \"tsMs\"> = {\n ip,\n lat: data.lat,\n lng: data.lng,\n country: data.country,\n city: data.city,\n };\n\n geoCache.set(ip, { point, expiresAt: Date.now() + 3_600_000 });\n return point;\n } catch {\n return null;\n }\n}\n\n/** Haversine formula — great-circle distance in km */\nfunction haversineKm(lat1: number, lng1: number, lat2: number, lng2: number): number {\n const R = 6371;\n const dLat = toRad(lat2 - lat1);\n const dLng = toRad(lng2 - lng1);\n const a =\n Math.sin(dLat / 2) ** 2 +\n Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) * Math.sin(dLng / 2) ** 2;\n return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));\n}\n\nfunction toRad(deg: number): number { return (deg * Math.PI) / 180; }\n\n/**\n * Check for impossible travel on a successful login event.\n *\n * @param userId - The authenticated user's ID\n * @param ip - The IP from which they just logged in\n * @param tsMs - Timestamp of this login (ms since epoch)\n * @param lookupUrl - Base URL for geo lookup endpoint (optional)\n */\nexport async function checkGeoVelocity(\n userId: string,\n ip: string,\n tsMs: number,\n lookupUrl?: string,\n): Promise<GeoVelocityResult | null> {\n const geo = await lookupGeo(ip, lookupUrl);\n if (!geo) return null;\n\n const currentPoint: GeoPoint = { ...geo, tsMs };\n const prev = lastSeen.get(userId);\n\n lastSeen.set(userId, currentPoint);\n\n // Evict entries older than 24 hours\n if (lastSeen.size > 10_000) {\n const cutoff = Date.now() - 86_400_000;\n for (const [key, val] of lastSeen) {\n if (val.tsMs < cutoff) lastSeen.delete(key);\n }\n }\n\n if (!prev) return null;\n if (prev.ip === ip) return null;\n\n const distanceKm = haversineKm(prev.lat, prev.lng, currentPoint.lat, currentPoint.lng);\n const hours = Math.max((tsMs - prev.tsMs) / 3_600_000, 0.001);\n const speedKmH = distanceKm / hours;\n\n if (speedKmH < MAX_SPEED_KMH) return null;\n\n return {\n isImpossible: true,\n distanceKm: Math.round(distanceKm),\n speedKmH: Math.round(speedKmH),\n from: prev,\n to: currentPoint,\n };\n}\n\nexport function resetGeoState(): void {\n lastSeen.clear();\n geoCache.clear();\n}\n","// ─── Credential / PII leak scanner ───────────────────────────────────────────\n// Scans log messages and source files for sensitive data patterns.\n// Nigeria-aware: includes BVN/NIN (11-digit), Nigerian phone numbers, and\n// fintech credential patterns alongside universal secret formats.\n\nexport interface LeakMatch {\n type: string;\n label: string;\n}\n\ninterface Pattern {\n type: string;\n label: string;\n regex: RegExp;\n}\n\nconst PATTERNS: Pattern[] = [\n\n // ── Cryptographic private keys ────────────────────────────────────────────\n // NOTE: -----BEGIN CERTIFICATE----- is intentionally excluded — public\n // certificates are designed to be public and committing them is correct.\n // Only PRIVATE keys are dangerous.\n {\n type: \"private_key\",\n label: \"Private Key\",\n regex: /-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/,\n },\n\n // ── Cloud provider credentials ────────────────────────────────────────────\n {\n // AWS access key ID — highly specific, almost no false positives\n type: \"aws_key\",\n label: \"AWS Access Key\",\n regex: /\\bAKIA[0-9A-Z]{16}\\b/,\n },\n {\n // AWS secret access key — 40-char base64 string after keyword\n type: \"aws_secret\",\n label: \"AWS Secret Key\",\n regex: /\\b(?:aws[_-]?secret|AWS_SECRET_ACCESS_KEY)\\s*[:=]\\s*[A-Za-z0-9+/]{40}\\b/i,\n },\n {\n // Google API key\n type: \"google_key\",\n label: \"Google API Key\",\n regex: /\\bAIza[0-9A-Za-z\\-_]{35}\\b/,\n },\n {\n // Google OAuth client secret\n type: \"google_oauth\",\n label: \"Google OAuth Secret\",\n regex: /\\bGOCSP[A-Za-z0-9\\-_]{28}\\b/,\n },\n {\n // Firebase server key\n type: \"firebase_key\",\n label: \"Firebase Server Key\",\n regex: /\\bAAAA[A-Za-z0-9_-]{7}:[A-Za-z0-9_-]{140}\\b/,\n },\n {\n // Azure storage/connection string\n type: \"azure_key\",\n label: \"Azure Key\",\n regex: /\\bDefaultEndpointsProtocol=https;AccountName=[^;]+;AccountKey=[A-Za-z0-9+/=]{88}/,\n },\n\n // ── Source control & CI tokens ────────────────────────────────────────────\n {\n // GitHub personal access tokens (classic and fine-grained)\n type: \"github_token\",\n label: \"GitHub Token\",\n regex: /\\b(?:ghp|gho|ghu|ghs|ghr|github_pat)_[A-Za-z0-9_]{36,255}\\b/,\n },\n {\n // GitLab personal/project/group tokens\n type: \"gitlab_token\",\n label: \"GitLab Token\",\n regex: /\\bglpat-[A-Za-z0-9\\-_]{20}\\b/,\n },\n {\n // NPM access tokens\n type: \"npm_token\",\n label: \"NPM Token\",\n regex: /\\bnpm_[A-Za-z0-9]{36}\\b/,\n },\n\n // ── Payment providers ─────────────────────────────────────────────────────\n {\n // Stripe — secret, restricted, webhook keys\n type: \"stripe_key\",\n label: \"Stripe Key\",\n regex: /\\b(?:sk|rk|whsec)_(?:live|test)_[A-Za-z0-9]{24,}\\b/,\n },\n {\n // Paystack secret/public keys\n type: \"paystack_key\",\n label: \"Paystack Key\",\n regex: /\\b(?:sk|pk)_(?:live|test)_[A-Za-z0-9]{40,}\\b/,\n },\n {\n // Card PANs: Visa (4), Mastercard (51-55), Amex (34/37), Discover (6011/65)\n type: \"card_pan\",\n label: \"Card PAN\",\n regex: /\\b(?:4[0-9]{15}|5[1-5][0-9]{14}|3[47][0-9]{13}|6(?:011|5[0-9]{2})[0-9]{12})\\b/,\n },\n\n // ── Communication & messaging ─────────────────────────────────────────────\n {\n // Slack bot/user/app tokens\n type: \"slack_token\",\n label: \"Slack Token\",\n regex: /\\bxox[baprs]-[0-9A-Za-z]{10,48}\\b/,\n },\n {\n // Slack webhook URL\n type: \"slack_webhook\",\n label: \"Slack Webhook URL\",\n regex: /https:\\/\\/hooks\\.slack\\.com\\/services\\/T[A-Z0-9]+\\/B[A-Z0-9]+\\/[A-Za-z0-9]+/,\n },\n {\n // Twilio account SID and auth token\n type: \"twilio\",\n label: \"Twilio Credential\",\n regex: /\\bAC[a-z0-9]{32}\\b|\\bSK[a-z0-9]{32}\\b/,\n },\n {\n // SendGrid / Brevo / Mailgun API keys\n type: \"email_provider_key\",\n label: \"Email Provider API Key\",\n regex: /\\bSG\\.[A-Za-z0-9._-]{66}\\b|\\bkey-[0-9a-zA-Z]{32}\\b/,\n },\n\n // ── Database connection strings with embedded credentials ─────────────────\n {\n type: \"db_connection\",\n label: \"Database Connection String\",\n regex: /(?:postgresql|postgres|mysql|mongodb(?:\\+srv)?|redis|amqp(?:s)?):\\/\\/[^:]+:[^@\\s]{3,}@/i,\n },\n\n // ── Auth tokens ───────────────────────────────────────────────────────────\n {\n // JWT — three base64url segments separated by dots\n type: \"jwt\",\n label: \"JWT Token\",\n regex: /eyJ[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]{10,}/,\n },\n {\n // Generic API key / token — only flag QUOTED literals, not variable/env references.\n // Skips: api_key = process.env.KEY, token = myVar\n // Matches: api_key = \"sk-abc123...\", bearer: \"eyJhb...\"\n type: \"api_key\",\n label: \"API Key / Token\",\n regex: /\\b(?:api[_-]?key|access[_-]?token|auth[_-]?token|bearer|client[_-]?secret)\\s*[:=]\\s*[\"'][A-Za-z0-9_.\\/+\\-]{20,}[\"']/i,\n },\n\n // ── Password fields ───────────────────────────────────────────────────────\n {\n // Only flag QUOTED string literals after a password/secret keyword.\n // Skips: process.env.*, variable references, undefined/null, template literals.\n // Matches: password: \"hunter2\", secret: 'abc123def', pass=\"hardcoded!\"\n type: \"password\",\n label: \"Hardcoded Password\",\n regex: /\\b(?:password|passwd|pwd|pass|secret|credentials?)\\s*[:=]\\s*[\"'][^\"'$\\s]{6,}[\"']/i,\n },\n\n // ── Nigeria-specific PII ──────────────────────────────────────────────────\n {\n // BVN / NIN: 11 digits, first digit 1-9 (not a phone number starting with 0)\n // Exclude: inside URLs (preceded by / : @ - %), hex strings (followed by a-f),\n // and UUIDs/hashes (surrounded by alphanumeric chars)\n type: \"bvn\",\n label: \"BVN / NIN (11-digit identifier)\",\n regex: /(?<![/\\-:@%=a-fA-F\\w])[1-9]\\d{10}(?![a-fA-F\\d])/,\n },\n {\n // Nigerian phone numbers: 080x, 081x, 070x, 090x, 091x — or with +234 prefix\n type: \"ng_phone\",\n label: \"Nigerian Phone Number\",\n regex: /\\b(?:\\+?234|0)(?:7[0-9]|8[0-1]|9[0-1])\\d{8}\\b/,\n },\n\n // ── PII in credential context ─────────────────────────────────────────────\n {\n // Email only flagged when adjacent to a password/credential keyword\n // Prevents false positives on normal email references in code\n type: \"email_credential\",\n label: \"Email + Password Combo\",\n regex: /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}\\s+(?:password|passwd|pwd|secret|pass)\\s*[:=]?\\s*\\S+/i,\n },\n];\n\n// Returns which sensitive patterns matched, deduplicated by type.\nexport function scanForLeaks(message: string): LeakMatch[] {\n const seen = new Set<string>();\n const found: LeakMatch[] = [];\n for (const p of PATTERNS) {\n p.regex.lastIndex = 0;\n if (p.regex.test(message) && !seen.has(p.type)) {\n seen.add(p.type);\n found.push({ type: p.type, label: p.label });\n }\n }\n return found;\n}\n\n// Returns a copy of the message with sensitive values partially masked.\nexport function redactMessage(message: string): string {\n return message\n // Private keys\n .replace(/-----BEGIN (?:[A-Z ]+ )?PRIVATE KEY-----[\\s\\S]+?-----END (?:[A-Z ]+ )?PRIVATE KEY-----/g,\n \"[PRIVATE KEY REDACTED]\")\n // AWS access key\n .replace(/\\bAKIA[0-9A-Z]{16}\\b/g, \"AKIA••••••••••••••••\")\n // Google API key\n .replace(/\\bAIza[0-9A-Za-z\\-_]{35}\\b/g, \"AIza••••••••••••••••••••••••••••••••\")\n // GitHub tokens\n .replace(/\\b(ghp|gho|ghu|ghs|ghr|github_pat)_[A-Za-z0-9_]{4}[A-Za-z0-9_]{32,251}\\b/g,\n (_, prefix) => `${prefix}_••••[REDACTED]`)\n // Stripe keys\n .replace(/\\b((?:sk|rk|whsec)_(?:live|test)_)[A-Za-z0-9]{4}[A-Za-z0-9]{20,}/g,\n (_, prefix) => `${prefix}••••[REDACTED]`)\n // JWT tokens\n .replace(/eyJ[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]{10,}/g, \"[JWT REDACTED]\")\n // Database URLs — redact password part\n .replace(/((?:postgresql|postgres|mysql|mongodb(?:\\+srv)?|redis):\\/\\/[^:]+:)[^@\\s]{3,}(@)/gi,\n \"$1••••••••$2\")\n // API keys after keyword\n .replace(/(\\b(?:api[_-]?key|access[_-]?token|auth[_-]?token|bearer|client[_-]?secret)\\s*[:=]\\s*[\"']?)([A-Za-z0-9_.\\/+\\-]{4})([A-Za-z0-9_.\\/+\\-]{16,})[\"']?/gi,\n (_, prefix, first4) => `${prefix}${first4}••••[REDACTED]`)\n // Passwords after keyword\n .replace(/(\\b(?:passwd|pwd|pass|secret|credentials?|password)\\s*[:=]\\s*[\"']?)(\\S{2})(\\S{4,})/gi,\n (_, prefix, first2) => `${prefix}${first2}••••••`)\n // Card PANs — show first 6 + last 4\n .replace(/\\b(4[0-9]{5}|5[1-5][0-9]{4}|3[47][0-9]{4}|6(?:011|5[0-9]{2})[0-9]{1})[0-9]{6,9}([0-9]{4})\\b/g,\n (_, first6, last4) => `${first6}••••••${last4}`)\n // BVN/NIN — show first 2 + last 2\n .replace(/(?<!\\d)([1-9]\\d)(\\d{7})(\\d{2})(?!\\d)/g, (_, f2, _mid, l2) => `${f2}•••••••${l2}`)\n // Nigerian phone numbers\n .replace(/\\b((?:\\+?234|0)(?:7[0-9]|8[0-1]|9[0-1])\\d{2})\\d{6}\\b/g, \"$1••••••\");\n}\n","// ─── SDK configuration ────────────────────────────────────────────────────────\n\nexport interface AnomiraConfig {\n /** SDK API key — get this from the Anomira dashboard */\n apiKey: string;\n\n /** Your app ID from the Anomira dashboard */\n appId: string;\n\n /**\n * Ingest endpoint URL.\n * @default \"https://ingest.anomira.io/v1/events\"\n */\n ingestUrl?: string;\n\n /**\n * Geo-lookup endpoint URL for client-side geo-velocity checks.\n * Should point to your Anomira ingest service geo endpoint.\n * If not provided, client-side geo-velocity is skipped (server-side still runs).\n * @example \"https://ingest.anomira.io/v1/geo\"\n */\n geoLookupUrl?: string;\n\n /**\n * Max events to buffer before forcing a flush.\n * @default 100\n */\n maxBatchSize?: number;\n\n /**\n * Max milliseconds to hold events before flushing.\n * @default 5000\n */\n flushIntervalMs?: number;\n\n /**\n * Max retry attempts on ingest failure.\n * @default 3\n */\n maxRetries?: number;\n\n /**\n * If true, log SDK activity to console (useful during integration).\n * @default false\n */\n debug?: boolean;\n\n /**\n * If true, automatically intercept console.log/info/warn/error/debug\n * and forward them to the Anomira Logs dashboard.\n * Existing console output is preserved — logs still print to your terminal.\n * @default false\n */\n captureConsole?: boolean;\n\n /**\n * Service name tag applied to all captured console logs.\n * @default \"app\"\n */\n service?: string;\n\n /**\n * Auto-detection features to enable in the middleware.\n * All default to true.\n */\n detect?: {\n /** Flag repeated login failures from the same IP as brute_force */\n bruteForce?: boolean;\n /** Flag 429 responses as rate_abuse */\n rateAbuse?: boolean;\n /** Flag path traversal patterns in the URL */\n pathTraversal?: boolean;\n /** Flag XSS patterns in request body */\n xss?: boolean;\n /** Flag suspicious scan patterns (many 404s) */\n scanDetection?: boolean;\n /** Detect impossible travel between login events */\n geoVelocity?: boolean;\n };\n\n /**\n * Custom function to extract userId from the request.\n * Default: reads req.user?.id || req.user?.userId || req.user?.sub\n */\n getUserId?: (req: unknown) => string | undefined;\n\n /**\n * Custom function to extract the client IP from the request.\n * Default: reads X-Forwarded-For → X-Real-IP → socket.remoteAddress\n */\n getIp?: (req: unknown) => string;\n\n /**\n * Invisible security — automatic blocking from Anomira's community\n * threat intelligence network.\n *\n * When enabled (the default), the SDK periodically fetches high-confidence\n * attacker IPs from the Anomira network — IPs that have been seen attacking\n * multiple customers — and blocks them automatically, without any manual\n * decision required.\n *\n * This is the \"invisible\" part: your API is protected from known bad actors\n * the moment they appear in the network, before they even reach your handlers.\n *\n * Set `enabled: false` to disable auto-blocking and use threat data for\n * alerting only (the data is still fetched and logged).\n */\n autoBlock?: {\n /**\n * Whether to automatically block IPs from the community threat network.\n * @default true\n */\n enabled?: boolean;\n\n /**\n * Minimum community confidence score (0-100) to trigger an auto-block.\n * Only IPs above this threshold are blocked automatically.\n *\n * @default 85\n *\n * Guidance:\n * 85 (default) — confirmed malicious across multiple customers, very low false-positive risk\n * 70 — broader coverage, slightly higher false-positive risk\n * 95 — maximum precision, only the most confirmed threats\n *\n * Do not set below 60 — scores below that reflect limited data and carry\n * meaningful false-positive risk.\n */\n communityThreshold?: number;\n };\n}\n\n// ─── SDK event ────────────────────────────────────────────────────────────────\n\nexport interface SdkEvent {\n name: string;\n ts: number;\n ip: string;\n userId?: string;\n meta?: Record<string, unknown>;\n}\n\n// ─── Internal buffer event ────────────────────────────────────────────────────\n\nexport interface BufferedEvent extends SdkEvent {\n _retries: number;\n}\n\n// ─── Ingest request body ─────────────────────────────────────────────────────\n\nexport interface IngestPayload {\n appId: string;\n events: SdkEvent[];\n}\n\n// ─── Well-known event names ───────────────────────────────────────────────────\n\nexport const EventName = {\n // Auth\n LOGIN_SUCCESS: \"auth.login.success\",\n LOGIN_FAILED: \"auth.login.failed\",\n LOGOUT: \"auth.logout\",\n OTP_FAILED: \"auth.otp.failed\",\n OTP_SUCCESS: \"auth.otp.success\",\n BVN_LOOKUP: \"auth.bvn.lookup\",\n NIN_LOOKUP: \"auth.nin.lookup\", // NIN enumeration detection\n GEO_VELOCITY: \"auth.login.geo_velocity\",\n CREDENTIAL_STUFF: \"auth.credential.stuffing\",\n SIM_SWAP: \"auth.sim_swap.suspected\", // SIM swap fraud signal\n PHONE_AUTH: \"auth.phone.verified\", // Phone-based auth (OTP/2FA via phone)\n\n // HTTP layer (auto-detected by middleware)\n REQUEST: \"http.request\", // every request — feeds the Events dashboard\n RATE_LIMIT: \"http.ratelimit.exceeded\",\n XSS_DETECTED: \"http.xss.detected\",\n PATH_TRAVERSAL: \"http.path.traversal\",\n SCAN_DETECTED: \"http.scan.detected\",\n IDOR_ATTEMPT: \"user.idor.attempt\",\n SQL_ERROR: \"db.sql.error\",\n\n // Firewall (emitted when a custom request filtering rule fires)\n FIREWALL_BLOCK: \"http.firewall.block\",\n FIREWALL_FLAG: \"http.firewall.flag\",\n} as const;\n\nexport type EventNameValue = typeof EventName[keyof typeof EventName];\n\n// ─── Firewall rule (synced from ingest, cached in SDK) ────────────────────────\n\nexport type FirewallField = \"url\" | \"body\" | \"header\" | \"user_agent\" | \"ip\";\nexport type FirewallOperator = \"contains\" | \"equals\" | \"starts_with\" | \"ends_with\" | \"regex\";\nexport type FirewallAction = \"block\" | \"flag\";\n\n// ─── Declared endpoint ────────────────────────────────────────────────────────\n\nexport interface EndpointDeclaration {\n /** HTTP method, e.g. \"GET\". Use \"*\" to match any method. */\n method: string;\n /** Express-style path, e.g. \"/api/users/:id\" — colons are normalized automatically. */\n path: string;\n /** Whether this endpoint requires authentication. Defaults to true. */\n auth?: boolean;\n}\n\n// ─── Firewall rule ────────────────────────────────────────────────────────────\n\nexport interface FirewallRule {\n id: string;\n field: FirewallField;\n headerName?: string | null;\n operator: FirewallOperator;\n value: string;\n action: FirewallAction;\n attackType: string;\n}\n","/**\n * AI Agent & MCP Traffic Detection\n *\n * Identifies requests originating from AI agents, LLM tool-callers, and\n * Model Context Protocol (MCP) clients using only confirmed, primary-source\n * verified signals.\n *\n * Signal sources:\n * - MCP Streamable HTTP spec (2025-03-26):\n * https://modelcontextprotocol.io/specification/2025-03-26/basic/transports\n * - OpenAI bot documentation:\n * https://developers.openai.com/api/docs/bots\n * - Anthropic crawler documentation:\n * https://support.claude.com/en/articles/8896518-does-anthropic-crawl-data\n * - Perplexity bot documentation:\n * https://docs.perplexity.ai/guides/bots\n * - RFC 9421 HTTP Message Signatures (ChatGPT Agent + Google Agent):\n * https://blog.castle.io/how-to-authenticate-openai-operator-requests-using-http-message-signatures/\n */\n\nexport type AgentType =\n | \"openai\" // GPTBot, ChatGPT-User, OAI-SearchBot, ChatGPT Agent\n | \"anthropic\" // ClaudeBot, Claude-User, Claude-SearchBot\n | \"perplexity\" // PerplexityBot, Perplexity-User\n | \"mcp_client\" // Generic MCP client (no provider-specific UA)\n | \"rfc9421_agent\" // Signed agent via RFC 9421 HTTP Message Signatures\n | \"unknown_agent\"; // Has agent signals but no provider identified\n\nexport type AgentConfidence = \"high\" | \"medium\";\n\nexport interface AgentDetectionResult {\n isAgent: boolean;\n confidence: AgentConfidence;\n agentType: AgentType | null;\n /** The matched UA token, e.g. \"GPTBot/1.3\" or \"ClaudeBot/1.0\" */\n agentName: string | null;\n /** Mcp-Session-Id header value when MCP protocol is confirmed */\n sessionId: string | null;\n /** True when Mcp-Session-Id or MCP Accept header is confirmed present */\n isMcp: boolean;\n /** Which specific signals fired — for observability and tuning */\n signals: string[];\n}\n\n// ─── Confirmed user-agent substrings (official documentation only) ─────────────\n//\n// Each entry: [substring_to_match (case-insensitive), agentType, display_name]\n// Only UA patterns confirmed from first-party sources are listed here.\n// Do NOT add patterns based on community aggregators or unverified reports.\nconst CONFIRMED_UA_PATTERNS: [RegExp, AgentType, string][] = [\n // OpenAI — https://developers.openai.com/api/docs/bots\n [/GPTBot\\//i, \"openai\", \"GPTBot\"],\n [/ChatGPT-User\\//i, \"openai\", \"ChatGPT-User\"],\n [/OAI-SearchBot\\//i, \"openai\", \"OAI-SearchBot\"],\n [/OAI-AdsBot\\//i, \"openai\", \"OAI-AdsBot\"],\n\n // Anthropic — https://support.claude.com/en/articles/8896518\n [/ClaudeBot\\//i, \"anthropic\", \"ClaudeBot\"],\n [/Claude-User\\//i, \"anthropic\", \"Claude-User\"],\n [/Claude-SearchBot\\//i, \"anthropic\", \"Claude-SearchBot\"],\n\n // Perplexity — https://docs.perplexity.ai/guides/bots\n [/PerplexityBot\\//i, \"perplexity\", \"PerplexityBot\"],\n [/Perplexity-User\\//i, \"perplexity\", \"Perplexity-User\"],\n];\n\n// ─── MCP Protocol headers (confirmed from MCP spec 2025-03-26) ─────────────────\n//\n// Node.js normalises all incoming HTTP header names to lowercase, so we\n// match the lowercase form here.\nconst MCP_SESSION_HEADER = \"mcp-session-id\"; // Mcp-Session-Id (normalised)\nconst RFC9421_SIG_HEADER = \"signature-agent\"; // Signature-Agent (RFC 9421)\n\n// The MCP spec mandates clients send EXACTLY this Accept header value.\n// Source: spec + confirmed via multiple bug reports about HTTP 406 errors\n// when this exact value is missing.\nconst MCP_ACCEPT_EXACT = \"application/json, text/event-stream\";\n\n// ─── Detection function ─────────────────────────────────────────────────────────\n\n/**\n * Analyse request headers and identify AI agent / MCP client traffic.\n * Accepts a plain header map (as returned by Node.js `req.headers` — all\n * keys lowercase, values are string or string[]).\n */\nexport function detectAgent(\n headers: Record<string, string | string[] | undefined>,\n): AgentDetectionResult {\n const signals: string[] = [];\n let agentType: AgentType | null = null;\n let agentName: string | null = null;\n let sessionId: string | null = null;\n let isMcp = false;\n let confidence: AgentConfidence | null = null;\n\n // ── 1. RFC 9421 Signature-Agent (HIGH confidence) ──────────────────────────\n // Confirmed: OpenAI ChatGPT Agent and Google Agent (Mariner) both send this.\n // Source: https://blog.castle.io/how-to-authenticate-openai-operator-requests\n const sigAgent = headerStr(headers[RFC9421_SIG_HEADER]);\n if (sigAgent) {\n signals.push(\"rfc9421_signature_agent\");\n confidence = \"high\";\n agentType = sigAgent.includes(\"chatgpt.com\") ? \"openai\" : \"rfc9421_agent\";\n agentName = `rfc9421:${sigAgent.slice(0, 60)}`;\n }\n\n // ── 2. MCP Session ID header (HIGH confidence) ─────────────────────────────\n const mcpSession = headerStr(headers[MCP_SESSION_HEADER]);\n if (mcpSession) {\n signals.push(\"mcp_session_id\");\n isMcp = true;\n sessionId = mcpSession;\n confidence = \"high\";\n if (!agentType) agentType = \"mcp_client\";\n }\n\n // ── 3. MCP mandatory Accept header (HIGH confidence when alone) ────────────\n // The exact value \"application/json, text/event-stream\" is mandated by the\n // MCP Streamable HTTP spec. No standard browser sends this exact combination.\n const accept = headerStr(headers[\"accept\"]);\n if (accept === MCP_ACCEPT_EXACT) {\n signals.push(\"mcp_accept_header\");\n isMcp = true;\n if (!confidence) confidence = \"high\";\n if (!agentType) agentType = \"mcp_client\";\n }\n\n // ── 4. Known AI agent User-Agent strings (HIGH confidence) ─────────────────\n const ua = headerStr(headers[\"user-agent\"]) ?? \"\";\n for (const [pattern, type, name] of CONFIRMED_UA_PATTERNS) {\n const match = ua.match(pattern);\n if (match) {\n signals.push(`ua_${name.toLowerCase().replace(/[^a-z0-9]/g, \"_\")}`);\n confidence = \"high\";\n // Extract the full token with version e.g. \"GPTBot/1.3\"\n const tokenStart = ua.indexOf(match[0]);\n const tokenEnd = ua.indexOf(\" \", tokenStart);\n agentName = tokenEnd > -1 ? ua.slice(tokenStart, tokenEnd) : match[0];\n agentType = type;\n break; // First match wins — patterns are mutually exclusive\n }\n }\n\n // ── 5. python-httpx User-Agent (MEDIUM confidence) ─────────────────────────\n // The MCP Python SDK (modelcontextprotocol/python-sdk) uses httpx as its\n // HTTP client and sets no custom User-Agent, resulting in \"python-httpx/<v>\".\n // Source: https://github.com/modelcontextprotocol/python-sdk/issues/1641\n // NOTE: Any Python service using httpx will produce this UA, so we only\n // treat it as a signal when combined with MCP-specific headers above.\n if (!agentType && /^python-httpx\\//i.test(ua) && isMcp) {\n signals.push(\"ua_python_httpx_with_mcp\");\n confidence = \"medium\";\n agentType = \"mcp_client\";\n agentName = ua.split(\" \")[0] ?? \"python-httpx\";\n }\n\n const isAgent = confidence !== null;\n\n return {\n isAgent,\n confidence: confidence ?? \"medium\",\n agentType: isAgent ? (agentType ?? \"unknown_agent\") : null,\n agentName: isAgent ? agentName : null,\n sessionId,\n isMcp,\n signals,\n };\n}\n\n// ─── Helper ─────────────────────────────────────────────────────────────────────\n\nfunction headerStr(v: string | string[] | undefined): string | undefined {\n if (v === undefined) return undefined;\n return Array.isArray(v) ? v[0] : v;\n}\n","/**\n * Behavioral Browser Fingerprinting\n *\n * Computes a 0-100 automation score from HTTP request headers.\n * Higher score = more likely to be a programmatic client (script, agent, bot).\n *\n * Three tiers of signals, all from confirmed primary sources:\n *\n * Tier 1 — Definitive: User-Agent strings that are unambiguously automated.\n * Sources: python-requests source (psf/requests utils.py), httpx test suite\n * (encode/httpx tests/client/test_headers.py), undici issue #1305,\n * curl documentation (everything.curl.dev), aiohttp docs, Go stdlib.\n *\n * Tier 2 — Strong: Absence of browser-only headers.\n * Sources: W3C Fetch Metadata spec (w3.org/TR/fetch-metadata/),\n * MDN Sec-Fetch-Mode, undici issue #1305, SearXNG bot detection source.\n *\n * Tier 3 — Corroborating: Incoherent header combinations.\n * Source: undici issue #1305 (sends Sec-Fetch-Mode without Sec-Fetch-Site),\n * MDN Sec-CH-UA (Chromium only — Firefox/Safari do not send it).\n */\n\nexport interface FingerprintResult {\n /** 0-100. ≥55 = likely automated. 100 = definitive known bot UA. */\n score: number;\n /** Human-readable signals that contributed to the score. */\n signals: string[];\n /** True when the User-Agent matches a confirmed automated client string. */\n isDefiniteBot: boolean;\n /** The specific known client name when isDefiniteBot is true, e.g. \"python-requests\". */\n knownClient: string | null;\n}\n\n/**\n * HTTP/2 SETTINGS values extracted from the client's connection preface.\n * Populated only when the customer's Node.js app handles HTTP/2 directly\n * (no reverse proxy in between for TLS termination).\n *\n * Source for expected values:\n * - Chrome 119+: initialWindowSize=6,291,456 (6 MB), headerTableSize=65,536\n * - python-requests / httpx / curl: initialWindowSize=65,535 (HTTP/2 default)\n * Reference: Akamai Black Hat EU 2017 whitepaper; scrapfly.io HTTP/2 guide\n */\nexport interface Http2SettingsResult {\n /** INITIAL_WINDOW_SIZE from client SETTINGS frame (bytes). */\n initialWindowSize: number;\n /** HEADER_TABLE_SIZE from client SETTINGS frame. */\n headerTableSize: number;\n /** ENABLE_PUSH from client SETTINGS frame. */\n enablePush: boolean;\n /**\n * Automation score contribution (0-20) derived from SETTINGS values.\n * Chrome/modern browsers use ~6 MB window; all Python/curl clients use ~65 KB.\n */\n score: number;\n signals: string[];\n}\n\n/**\n * JA3 or JA4 hash passed by an upstream proxy (customer's Nginx with the\n * ngx_ssl_fingerprint module, or a Cloudflare JA3 Worker).\n * The ingest includes this in event meta for dashboard display and future\n * mismatch-based scoring when a lookup database is available.\n */\nexport interface UpstreamTlsResult {\n ja3: string | null; // X-JA3-Hash header (HanadaLee / phuslu modules)\n ja4: string | null; // X-JA4 header\n}\n\n// ─── Tier 1: Confirmed known-automated User-Agent prefixes/patterns ────────────\n//\n// Each entry is [regex, display_name].\n// Only patterns confirmed from primary sources (source code, official docs).\n// Do NOT add speculative patterns.\nconst KNOWN_BOT_UA: [RegExp, string][] = [\n // Confirmed: psf/requests utils.py (default_headers)\n [/^python-requests\\//i, \"python-requests\"],\n // Confirmed: encode/httpx tests/client/test_headers.py\n [/^python-httpx\\//i, \"python-httpx\"],\n // Confirmed: aiohttp docs (format: \"Python/3.x aiohttp/3.x.x\")\n [/\\baiohttp\\//i, \"aiohttp\"],\n // Confirmed: nodejs/undici issue #1305\n [/^undici$/i, \"undici\"],\n // Confirmed: everything.curl.dev\n [/^curl\\//i, \"curl\"],\n // Confirmed: GNU wget docs\n [/^Wget\\//i, \"wget\"],\n // Confirmed: Go net/http DefaultClient (golang.org/pkg/net/http)\n [/^Go-http-client\\//i, \"Go-http-client\"],\n // Confirmed: Java HttpURLConnection default\n [/^Java\\//i, \"Java\"],\n // Confirmed: Scrapy docs (scrapy.org)\n [/^Scrapy\\//i, \"Scrapy\"],\n // Confirmed: OkHttp (square.github.io/okhttp)\n [/\\bokhttp\\//i, \"okhttp\"],\n // Confirmed: libwww-perl (metacpan.org/pod/LWP)\n [/^libwww-perl\\//i, \"libwww-perl\"],\n // Confirmed: node-fetch (github.com/node-fetch/node-fetch README)\n [/^node-fetch\\//i, \"node-fetch\"],\n // Confirmed: axios docs (axios-http.com/docs/config_defaults)\n [/^axios\\//i, \"axios\"],\n // Confirmed: Ruby net/http default (ruby-doc.org)\n [/^Ruby$/i, \"Ruby\"],\n];\n\n// ─── Tier 1: axios-specific Accept header ─────────────────────────────────────\n//\n// Confirmed from axios documentation: axios sets\n// `Accept: application/json, text/plain, */*` by default.\n// No browser or other HTTP library sends this exact value.\nconst AXIOS_ACCEPT = \"application/json, text/plain, */*\";\n\n// ─── Detection function ────────────────────────────────────────────────────────\n\n/**\n * Compute a browser fingerprint score from a Node.js request's headers.\n *\n * @param headers Node.js normalises incoming headers to lowercase — pass\n * `req.headers` directly.\n * @param ua The User-Agent string (extracted separately for clarity).\n */\nexport function computeBrowserFingerprint(\n headers: Record<string, string | string[] | undefined>,\n ua: string,\n): FingerprintResult {\n const signals: string[] = [];\n let score = 0;\n let isDefiniteBot = false;\n let knownClient: string | null = null;\n\n // ── Tier 1a: Known automated User-Agent ──────────────────────────────────────\n for (const [pattern, name] of KNOWN_BOT_UA) {\n if (pattern.test(ua)) {\n isDefiniteBot = true;\n knownClient = name;\n score = 100;\n signals.push(`known_ua:${name}`);\n break;\n }\n }\n\n // ── Tier 1b: axios Accept header (definitive even with custom UA) ─────────────\n const accept = str(headers[\"accept\"]);\n if (!isDefiniteBot && accept === AXIOS_ACCEPT) {\n isDefiniteBot = true;\n knownClient = \"axios\";\n score = 100;\n signals.push(\"known_accept:axios\");\n }\n\n // If already definitive, no need to score further\n if (isDefiniteBot) return { score, signals, isDefiniteBot, knownClient };\n\n // ── Tier 2: Absence of browser-mandatory headers ──────────────────────────────\n //\n // Sec-Fetch-Site and Sec-Fetch-Mode are sent on EVERY fetch()/XHR call by\n // Chrome (Chromium), Firefox (since 2023), and most modern browsers.\n // They are W3C \"forbidden\" headers — JavaScript cannot set or modify them.\n // Absence = 100% certainty the caller is not a standard browser fetch call.\n // Source: https://www.w3.org/TR/fetch-metadata/\n const hasSecFetchSite = \"sec-fetch-site\" in headers;\n const hasSecFetchMode = \"sec-fetch-mode\" in headers;\n\n if (!hasSecFetchSite && !hasSecFetchMode) {\n score += 30;\n signals.push(\"missing_sec_fetch\");\n }\n\n // Accept-Language: all browsers send this. python-requests, httpx, curl,\n // aiohttp, and Node.js undici do NOT send it by default.\n // Source: SearXNG bot detection; confirmed by examining default headers of\n // each library above.\n // Exception: undici sends `accept-language: *` — we handle that separately.\n const acceptLang = str(headers[\"accept-language\"]);\n if (!acceptLang) {\n score += 25;\n signals.push(\"missing_accept_language\");\n }\n\n // Accept-Encoding: curl sends NO Accept-Encoding by default (unlike all others).\n // Its absence alone is a strong curl signal. Not a general \"bot\" signal,\n // but useful combined with other indicators.\n const acceptEnc = str(headers[\"accept-encoding\"]);\n if (!acceptEnc) {\n score += 10;\n signals.push(\"missing_accept_encoding\");\n }\n\n // ── Tier 3: Incoherent header combinations ────────────────────────────────────\n //\n // Node.js undici (built-in fetch) sends `sec-fetch-mode: cors` but does NOT\n // send `sec-fetch-site` or `sec-fetch-dest`. Real browsers always send all\n // three together. This pattern is unique to undici.\n // Source: nodejs/undici GitHub issue #1305\n if (hasSecFetchMode && !hasSecFetchSite) {\n score += 20;\n signals.push(\"undici_pattern:sec_fetch_mode_without_site\");\n knownClient = \"undici (Node.js fetch)\";\n }\n\n // undici also sends `accept-language: *` (a bare wildcard) — not a valid\n // BCP 47 language tag, which browsers always send (e.g. \"en-US,en;q=0.9\").\n // Source: nodejs/undici issue #1305\n if (acceptLang === \"*\") {\n score += 15;\n signals.push(\"undici_pattern:accept_language_wildcard\");\n }\n\n // Sec-CH-UA is sent by Chromium browsers on ALL requests (including fetch())\n // without any server opt-in. If the UA claims to be Chrome but Sec-CH-UA\n // is absent, the Chrome UA is likely spoofed (or it's Firefox/Safari, which\n // never send Sec-CH-UA). Source: MDN Sec-CH-UA; Corbado blog.\n const claimsChrome = /Chrome\\//i.test(ua) && !/Chromium/i.test(ua);\n const hasSecChUa = \"sec-ch-ua\" in headers;\n if (claimsChrome && !hasSecChUa) {\n score += 10;\n signals.push(\"chrome_ua_without_sec_ch_ua\");\n }\n\n // Cap at 100\n score = Math.min(100, score);\n\n return { score, signals, isDefiniteBot, knownClient };\n}\n\n// ─── HTTP/2 SETTINGS extraction ───────────────────────────────────────────────\n\n/**\n * Extract HTTP/2 SETTINGS from the Node.js `http2.Http2Session` that handled\n * this request, if available. Works only when the customer's Node.js app is\n * the TLS endpoint (no reverse proxy terminating TLS before Node.js).\n *\n * The function is safe to call on any request — it returns null for HTTP/1.1\n * connections or when the session is not accessible.\n *\n * @param req The raw Node.js IncomingMessage (Express: `req`, Fastify: `req.raw`)\n *\n * Reference values (Akamai Black Hat EU 2017 + scrapfly.io HTTP/2 guide):\n * Chrome 119+ : initialWindowSize = 6,291,456 | headerTableSize = 65,536\n * Firefox : initialWindowSize = 65,535 | headerTableSize = 65,536\n * python libs : initialWindowSize = 65,535 | headerTableSize = 4,096\n * curl : initialWindowSize = 65,535 | headerTableSize = 4,096\n */\nexport function extractHttp2Settings(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n req: Record<string, any>,\n): Http2SettingsResult | null {\n // Access the http2.ServerHttp2Stream → http2.Http2Session → remoteSettings.\n // Express: req.stream (available when using http2.createServer)\n // Fastify: req.raw.stream (same path via req.raw which is the Node.js request)\n const stream = req[\"stream\"] ?? req[\"raw\"]?.[\"stream\"];\n if (!stream) return null;\n\n const session = stream[\"session\"];\n if (!session) return null;\n\n // remoteSettings is the Http2Settings object sent by the CLIENT in its\n // SETTINGS frame during the HTTP/2 connection preface.\n // Node.js docs: https://nodejs.org/api/http2.html#http2sessionremotesettings\n const remote = session[\"remoteSettings\"] as {\n headerTableSize?: number;\n enablePush?: boolean;\n initialWindowSize?: number;\n } | undefined;\n\n if (!remote) return null;\n\n const initialWindowSize = remote.initialWindowSize ?? 65535;\n const headerTableSize = remote.headerTableSize ?? 4096;\n const enablePush = remote.enablePush ?? true;\n\n const signals: string[] = [];\n let score = 0;\n\n // The most discriminating signal: Chrome uses ~6 MB (6,291,456) while ALL\n // Python/curl HTTP/2 clients use the protocol default of 65,535 bytes.\n // Threshold of 131,072 (128 KB) gives a 50× margin above the default —\n // no realistic browser sends a value this low.\n // Source: Akamai Passive Fingerprinting of HTTP/2 Clients (Black Hat EU 2017)\n if (initialWindowSize <= 131_072) {\n score += 20;\n signals.push(`h2_window_${initialWindowSize}`);\n }\n\n // Chrome also uses a much larger HEADER_TABLE_SIZE (65,536 vs default 4,096).\n // Python libs leave this at the default.\n if (headerTableSize <= 4096 && initialWindowSize <= 131_072) {\n score += 5;\n signals.push(\"h2_header_table_default\");\n }\n\n return { initialWindowSize, headerTableSize, enablePush, score, signals };\n}\n\n// ─── Upstream TLS fingerprint headers ─────────────────────────────────────────\n\n/**\n * Read JA3/JA4 hashes injected by an upstream proxy that has TLS fingerprinting\n * enabled. Supports:\n * - HanadaLee/ngx_ssl_fingerprint_module → X-JA3-Hash, X-JA4\n * - phuslu/nginx-ssl-fingerprint → X-JA3-Hash, X-JA4\n * - Custom Nginx configs → X-JA3, X-JA4 (alternate names)\n *\n * These values are included in event meta as `_ja3` / `_ja4` for:\n * a) Display in the AI Agent Sessions dashboard\n * b) Future mismatch-based scoring (UA claims Chrome but JA3 is python-requests)\n *\n * @param headers Node.js lowercase-normalised header map\n */\nexport function extractUpstreamTls(\n headers: Record<string, string | string[] | undefined>,\n): UpstreamTlsResult {\n // Try both common header name conventions\n const ja3 = str(headers[\"x-ja3-hash\"]) || str(headers[\"x-ja3\"]) || null;\n const ja4 = str(headers[\"x-ja4\"]) || null;\n return { ja3: ja3 || null, ja4: ja4 || null };\n}\n\n// ─── Helper ────────────────────────────────────────────────────────────────────\n\nfunction str(v: string | string[] | undefined): string {\n if (v === undefined) return \"\";\n return Array.isArray(v) ? (v[0] ?? \"\") : v;\n}\n","/**\n * Honeypot Response Engine\n * ─────────────────────────\n * Generates convincing fake HTTP responses for honeypot traps.\n *\n * Design principles (based on security research into attacker behaviour):\n *\n * 1. Always return HTTP 200 with realistic content — 403/404 tells the attacker\n * the path exists but is protected; 200 with plausible fake data makes them\n * believe they succeeded and linger long enough to reveal their tooling.\n * Source: Thinkst blog \"Creating REST API Canary Endpoints\" (2022)\n *\n * 2. Every response embeds a unique canary token in a realistic position.\n * The token appears as a legitimate-looking credential value — NOT as a\n * special prefix or obvious marker — so automated scanners do not filter it.\n * Source: Thinkst canarytokens.org documentation\n *\n * 3. Credentials must follow the exact format of real credentials:\n * - AWS Access Key ID: `AKIA` + `I` (pos 5) + 14 base32 chars + `A` = 20 chars\n * Source: AWS docs + awsteele.com/blog/2020/09/26/aws-access-key-format\n * - AWS Secret Key: 40 base64-standard chars (A-Z, a-z, 0-9, +, /)\n * Source: summitroute.com/blog/2018/06/20/aws_security_credential_formats\n * - JWT: valid HS256 signature, `exp` set 24h in the past, `kid` contains\n * the canary token for detection. Source: Auth0 JWT docs + canarytokens.org\n *\n * 4. HTTP headers must match the technology being impersonated — e.g., Apache\n * headers on /.env not Express headers, since attackers cross-reference them.\n *\n * 5. Callback URLs embedded in fake webhook fields point to the Anomira ingest.\n * When an attacker configures their system with the fake webhook and it fires,\n * the ingest records the canary_triggered event immediately.\n * Source: Thinkst \"A Safety Net for AWS Canarytokens\" blog (2022)\n */\n\nimport { randomBytes, createHmac } from \"node:crypto\";\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type HoneypotType =\n | \"env_file\" // .env, .env.production, .env.local\n | \"aws_credentials\" // .aws/credentials\n | \"git_config\" // .git/config\n | \"graphql\" // /graphql with introspection\n | \"spring_actuator\" // /actuator/env (Spring Boot)\n | \"json_config\" // config.json, settings.json, secrets.json\n | \"htpasswd\" // .htpasswd — Apache password file with hashed credentials\n | \"s3_bucket\" // S3-style XML responses for bucket listing paths\n | \"admin_portal\" // HTML admin login form — captures credential attempts\n | \"generic\"; // all other honeypot paths\n\nexport interface HoneypotResponse {\n statusCode: number;\n contentType: string;\n headers: Record<string, string>;\n body: string;\n canaryToken: string; // stored in ingest for subsequent detection\n}\n\n// ─── Path → type detection ────────────────────────────────────────────────────\n\n/**\n * Determine the honeypot type from the request path so the right fake\n * response can be generated.\n */\nexport function detectHoneypotType(path: string): HoneypotType {\n const p = path.toLowerCase().split(\"?\")[0] ?? path.toLowerCase();\n\n if (/\\/\\.env(\\.[\\w]+)?$/.test(p) || p.endsWith(\"/.env\")) return \"env_file\";\n if (p.includes(\".aws/credentials\") || p.includes(\"aws/credentials\")) return \"aws_credentials\";\n if (p.includes(\".git/config\") || p.endsWith(\"/git/config\")) return \"git_config\";\n if (p.includes(\"/graphql\") || p.includes(\"/graphiql\")) return \"graphql\";\n if (p.includes(\"/actuator/env\") || p.includes(\"/actuator/\")) return \"spring_actuator\";\n if (/\\/(config|settings|secrets?)(\\.json)?$/.test(p)) return \"json_config\";\n // .htpasswd — Apache password file\n if (p.endsWith(\"/.htpasswd\") || p.endsWith(\"/.htaccess\")) return \"htpasswd\";\n // S3 bucket listing paths\n if (p.includes(\"/.s3cfg\") || p.endsWith(\"/s3\") || p.includes(\"s3cfg\")) return \"s3_bucket\";\n // Admin portals — match common admin paths\n if (/\\/(admin|wp-admin|administrator|panel|cpanel|dashboard|manage|backend)(\\/|$)/.test(p)) return \"admin_portal\";\n return \"generic\";\n}\n\n// ─── Main generator ───────────────────────────────────────────────────────────\n\n/**\n * Generate a convincing fake HTTP response for a honeypot trap.\n *\n * @param type The type of honeypot (derived from path)\n * @param canaryToken Unique token to embed in the response for later detection\n * @param callbackBase Base URL of the Anomira ingest, used for webhook canaries\n * (e.g. \"https://ingest.anomira.io\")\n * @param orgId Organisation ID — used in callback URLs and canary key generation\n */\nexport function generateHoneypotResponse(\n type: HoneypotType,\n canaryToken: string,\n callbackBase: string,\n orgId: string,\n): HoneypotResponse {\n switch (type) {\n case \"env_file\": return makeEnvFile(canaryToken, callbackBase, orgId);\n case \"aws_credentials\": return makeAwsCredentials(canaryToken);\n case \"git_config\": return makeGitConfig(canaryToken, callbackBase, orgId);\n case \"graphql\": return makeGraphQL();\n case \"spring_actuator\": return makeSpringActuator(canaryToken);\n case \"json_config\": return makeJsonConfig(canaryToken, callbackBase, orgId);\n case \"htpasswd\": return makeHtpasswd(canaryToken);\n case \"s3_bucket\": return makeS3Bucket(canaryToken);\n case \"admin_portal\": return makeAdminPortal(canaryToken);\n default: return makeGeneric(canaryToken);\n }\n}\n\n// ─── .env file ────────────────────────────────────────────────────────────────\n\nfunction makeEnvFile(canaryToken: string, _callbackBase: string, _orgId: string): HoneypotResponse {\n const jwtSecret = genHex(32);\n const dbPassword = genAlphanumeric(20);\n const redisPassword = genAlphanumeric(16);\n const apiKey = genHex(32);\n\n // DNS canary hostnames — fire detection the instant an attacker's tool\n // resolves the domain, before any TCP connection is attempted.\n // The DNS server at srv.anomira.io receives the query, extracts the canary\n // token from the subdomain, and fires the alert via our ingest API.\n const dnsDb = `db.${canaryToken}.srv.anomira.io`;\n const dnsCache = `cache.${canaryToken}.srv.anomira.io`;\n const dnsMonitoring = `hooks.monitoring.${canaryToken}.srv.anomira.io`;\n const dnsAlerts = `hooks.alerts.${canaryToken}.srv.anomira.io`;\n const dnsDeploy = `deploy.internal.${canaryToken}.srv.anomira.io`;\n\n // JWT token: valid format, expired 24h ago. Detection is via jti claim.\n const fakeJwt = generateCanaryJwt(canaryToken, jwtSecret);\n\n const body = `# Production Environment Configuration\n# Last updated: ${new Date(Date.now() - 7 * 86400000).toISOString().split(\"T\")[0]}\n\nNODE_ENV=production\nPORT=3000\n\n# ── Database ──────────────────────────────────────────────────────────────\nDATABASE_URL=postgresql://db_prod:${dbPassword}@${dnsDb}:5432/app_production\nDATABASE_REPLICA_URL=postgresql://db_prod:${dbPassword}@${dnsDb}:5433/app_production\nDATABASE_POOL_MIN=2\nDATABASE_POOL_MAX=10\n\n# ── Cache ─────────────────────────────────────────────────────────────────\nREDIS_URL=redis://:${redisPassword}@${dnsCache}:6379/0\n\n# ── Authentication ────────────────────────────────────────────────────────\nJWT_SECRET=${jwtSecret}\nJWT_EXPIRES_IN=15m\nREFRESH_TOKEN_SECRET=${genHex(32)}\nSESSION_SECRET=${genHex(32)}\n\n# ── Sample authenticated token (service account) ─────────────────────────\nSERVICE_ACCOUNT_TOKEN=${fakeJwt}\n\n# ── AWS ───────────────────────────────────────────────────────────────────\nAWS_REGION=eu-west-1\nAWS_S3_BUCKET=app-production-${genHex(4)}\n\n# ── Monitoring / Webhooks ─────────────────────────────────────────────────\nMONITORING_WEBHOOK=https://${dnsMonitoring}/v1/events/ingest\nALERT_WEBHOOK=https://${dnsAlerts}/services/${genHex(7).toUpperCase()}/notify\nDEPLOY_HOOK=https://${dnsDeploy}/hooks/deploy/${genHex(6)}\n\n# ── External APIs ─────────────────────────────────────────────────────────\nINTERNAL_API_KEY=${apiKey}\n`;\n\n return {\n statusCode: 200,\n contentType: \"text/plain; charset=utf-8\",\n headers: {\n \"Cache-Control\": \"no-store\",\n \"Last-Modified\": new Date(Date.now() - 7 * 86400000).toUTCString(),\n \"ETag\": `\"${genHex(8)}-${genHex(4)}\"`,\n // Apache-style headers — most exposed .env files are on PHP/Apache stacks\n \"Server\": \"Apache/2.4.41 (Ubuntu)\",\n \"X-Content-Type-Options\": \"nosniff\",\n },\n body,\n canaryToken,\n };\n}\n\n// ─── .aws/credentials ─────────────────────────────────────────────────────────\n\nfunction makeAwsCredentials(canaryToken: string): HoneypotResponse {\n // AWS Access Key ID format (confirmed from awsteele.com + summitroute.com):\n // - Exactly 20 characters\n // - Character set: A-Z and 2-7 (base32 alphabet, no 0,1,8,9)\n // - Position 5 (first char after \"AKIA\"): always I or J (per statistical analysis)\n // - Last character: A or Q\n const fakeKeyId = makeAwsKeyId();\n const fakeSecret = makeAwsSecret();\n // Second profile uses ASIA prefix (temporary/STS credentials — more enticing)\n const stsPrefixKey = \"ASIA\" + makeAwsKeyId().slice(4);\n const stsSecret = makeAwsSecret();\n const stsToken = genBase64(300);\n\n const body = `[default]\naws_access_key_id=${fakeKeyId}\naws_secret_access_key=${fakeSecret}\nregion=eu-west-1\n\n[production]\naws_access_key_id=${fakeKeyId}\naws_secret_access_key=${fakeSecret}\nregion=eu-west-1\n\n[staging]\naws_access_key_id=${stsPrefixKey}\naws_secret_access_key=${stsSecret}\naws_session_token=${stsToken}\nregion=eu-west-1\n`;\n\n return {\n statusCode: 200,\n contentType: \"text/plain; charset=utf-8\",\n headers: {\n \"Cache-Control\": \"no-store\",\n \"Last-Modified\": new Date(Date.now() - 14 * 86400000).toUTCString(),\n // Match the Server header an AWS EC2 instance metadata endpoint might have\n \"Server\": \"EC2ws\",\n \"X-Content-Type-Options\": \"nosniff\",\n },\n body,\n canaryToken,\n };\n}\n\n// ─── .git/config ─────────────────────────────────────────────────────────────\n\nfunction makeGitConfig(canaryToken: string, callbackBase: string, orgId: string): HoneypotResponse {\n // GitHub PAT format: ghp_ + 36 alphanumeric chars (confirmed from GitHub docs)\n const ghpToken = `ghp_${genAlphanumeric(36)}`;\n const glpatToken = `glpat-${genAlphanumeric(20)}`;\n const webhookUrl = `${callbackBase}/v1/canary/${orgId}/${canaryToken}`;\n // DNS canary in the git remote URL — fires on `git fetch` or `git clone`\n const dnsGit = `git.${canaryToken}.srv.anomira.io`;\n\n const body = `[core]\n\trepositoryformatversion = 0\n\tfilemode = true\n\tbare = false\n\tlogallrefupdates = true\n\n[remote \"origin\"]\n\turl = https://oauth2:${ghpToken}@github.com/company/app.git\n\tfetch = +refs/heads/*:refs/remotes/origin/*\n\n[remote \"deploy\"]\n\turl = https://deploy-token:${glpatToken}@${dnsGit}/backend/app.git\n\tfetch = +refs/heads/*:refs/remotes/deploy/*\n\n[remote \"notify\"]\n\turl = ${webhookUrl}\n\n[branch \"main\"]\n\tremote = origin\n\tmerge = refs/heads/main\n\n[branch \"production\"]\n\tremote = deploy\n\tmerge = refs/heads/production\n\n[credential \"https://github.com\"]\n\tusername = deploy-bot\n`;\n\n return {\n statusCode: 200,\n contentType: \"text/plain; charset=utf-8\",\n headers: {\n \"Cache-Control\": \"no-store\",\n \"Last-Modified\": new Date(Date.now() - 30 * 86400000).toUTCString(),\n \"Server\": \"Apache/2.4.41 (Ubuntu)\",\n },\n body,\n canaryToken,\n };\n}\n\n// ─── GraphQL introspection ────────────────────────────────────────────────────\n\nfunction makeGraphQL(): HoneypotResponse {\n // Minimal but realistic introspection response.\n // Includes an \"AdminMutation\" type with enticing-sounding fields to\n // fingerprint any tool that auto-generates exploit queries after introspection.\n const body = JSON.stringify({\n data: {\n __schema: {\n queryType: { name: \"Query\" },\n mutationType: { name: \"Mutation\" },\n types: [\n { kind: \"OBJECT\", name: \"Query\", fields: [{ name: \"user\" }, { name: \"users\" }, { name: \"posts\" }] },\n { kind: \"OBJECT\", name: \"Mutation\", fields: [{ name: \"login\" }, { name: \"createUser\" }, { name: \"adminReset\" }, { name: \"exportData\" }] },\n { kind: \"OBJECT\", name: \"User\", fields: [{ name: \"id\" }, { name: \"email\" }, { name: \"role\" }, { name: \"createdAt\" }] },\n { kind: \"OBJECT\", name: \"AuthPayload\", fields: [{ name: \"token\" }, { name: \"user\" }] },\n { kind: \"SCALAR\", name: \"String\", fields: null },\n { kind: \"SCALAR\", name: \"Boolean\", fields: null },\n { kind: \"SCALAR\", name: \"Int\", fields: null },\n { kind: \"SCALAR\", name: \"ID\", fields: null },\n ],\n },\n },\n });\n\n return {\n statusCode: 200,\n contentType: \"application/json; charset=utf-8\",\n headers: {\n \"X-Powered-By\": \"Express\",\n \"X-Request-Id\": `req_${genHex(16)}`,\n },\n body,\n canaryToken: \"\",\n };\n}\n\n// ─── Spring Boot actuator ─────────────────────────────────────────────────────\n\nfunction makeSpringActuator(canaryToken: string): HoneypotResponse {\n // Spring Boot /actuator/env format confirmed from official docs.\n // Sensitive values are shown UNMASKED (simulating misconfigured `show-values=ALWAYS`)\n // — the most dangerous configuration, which is what attackers specifically look for.\n const dbPassword = genAlphanumeric(20);\n const secretKey = genHex(32);\n\n const body = JSON.stringify({\n activeProfiles: [\"production\"],\n defaultProfiles: [\"default\"],\n propertySources: [\n {\n name: \"systemProperties\",\n properties: {\n \"java.runtime.version\": { value: \"17.0.9+9\" },\n \"server.port\": { value: \"8080\" },\n \"user.home\": { value: \"/home/appuser\" },\n },\n },\n {\n name: \"applicationConfig: [classpath:/application-production.properties]\",\n properties: {\n \"spring.datasource.url\": { origin: \"class path resource [application-production.properties] - 3:1\", value: \"jdbc:postgresql://db.internal:5432/myapp\" },\n \"spring.datasource.username\": { origin: \"class path resource [application-production.properties] - 4:1\", value: \"dbadmin\" },\n \"spring.datasource.password\": { origin: \"class path resource [application-production.properties] - 5:1\", value: dbPassword },\n \"app.jwt.secret\": { origin: \"class path resource [application-production.properties] - 8:1\", value: secretKey },\n \"app.canary.token\": { value: canaryToken },\n \"management.endpoints.web.exposure.include\": { value: \"health,info,env,metrics,loggers\" },\n },\n },\n ],\n }, null, 2);\n\n return {\n statusCode: 200,\n contentType: \"application/vnd.spring-boot.actuator.v3+json\",\n headers: {\n \"X-Application-Context\": \"myapp:production:8080\",\n \"X-Content-Type-Options\": \"nosniff\",\n \"X-XSS-Protection\": \"1; mode=block\",\n },\n body,\n canaryToken,\n };\n}\n\n// ─── JSON config ──────────────────────────────────────────────────────────────\n\nfunction makeJsonConfig(canaryToken: string, callbackBase: string, orgId: string): HoneypotResponse {\n const webhookUrl = `${callbackBase}/v1/canary/${orgId}/${canaryToken}`;\n\n const body = JSON.stringify({\n environment: \"production\",\n version: \"2.4.1\",\n database: {\n host: \"db.internal.company.com\",\n port: 5432,\n name: \"app_production\",\n user: \"db_prod\",\n password: genAlphanumeric(20),\n },\n jwt: {\n secret: genHex(32),\n expiresIn: \"15m\",\n },\n webhooks: {\n events: webhookUrl,\n alerts: `${callbackBase}/v1/canary/${orgId}/${canaryToken}`,\n },\n internalApiKey: genHex(32),\n }, null, 2);\n\n return {\n statusCode: 200,\n contentType: \"application/json; charset=utf-8\",\n headers: {\n \"Cache-Control\": \"no-store\",\n \"ETag\": `\"${genHex(8)}\"`,\n \"X-Powered-By\": \"Express\",\n },\n body,\n canaryToken,\n };\n}\n\n// ─── .htpasswd ────────────────────────────────────────────────────────────────\n//\n// Apache password file format:\n// username:$apr1$salt$hash ← APR1-MD5 (most common)\n// username:$2y$10$... ← bcrypt (newer systems)\n//\n// The canary token is embedded as part of one of the password hashes so that\n// any attacker who cracks it offline and tries to use it will be detected.\n// Format confirmed: Apache httpd documentation + htpasswd man page.\n\nfunction makeHtpasswd(canaryToken: string): HoneypotResponse {\n // APR1-MD5: $apr1$ + 8-char salt + $ + 22-char base64-like hash\n // Character set for APR1: A-Z, a-z, 0-9, /, .\n const apr1Salt = genAlphanumeric(8).toLowerCase();\n const apr1Hash = genBase64(22).replace(/[+=/]/g, (c) =>\n ({ \"+\": \".\", \"=\": \"/\", \"/\": \"X\" }[c] ?? c)\n );\n\n // bcrypt: $2y$10$ + 53-char base64url hash (standard bcrypt output)\n const bcryptHash = genBase64(53).replace(/[+=]/g, (c) =>\n ({ \"+\": \".\", \"=\": \"/\" }[c] ?? c)\n );\n\n // Third entry uses the canary token woven into the hash — if cracked and used:\n const canaryApr1 = `$apr1$${canaryToken.slice(0, 8)}$${genBase64(22).slice(0, 22)}`;\n\n const body = `# Apache HTTP Server password file\nadmin:$apr1$${apr1Salt}$${apr1Hash}\ndeploy:$2y$10$${bcryptHash}\nroot:${canaryApr1}\nbackup-user:$apr1$${genAlphanumeric(8).toLowerCase()}$${genBase64(22).slice(0, 22)}\n`;\n\n return {\n statusCode: 200,\n contentType: \"text/plain; charset=utf-8\",\n headers: {\n \"Cache-Control\": \"no-store\",\n \"Last-Modified\": new Date(Date.now() - 45 * 86400000).toUTCString(),\n \"Server\": \"Apache/2.4.41 (Ubuntu)\",\n \"Content-Disposition\": \"inline\",\n },\n body,\n canaryToken,\n };\n}\n\n// ─── S3 bucket listing XML ─────────────────────────────────────────────────────\n//\n// AWS S3 ListAllMyBucketsResult XML format.\n// Source: AWS S3 REST API documentation (ListBuckets response syntax).\n// The canary token is embedded in one bucket name — if the attacker tries\n// to access that bucket on real AWS, their IP and tool are logged by CloudTrail.\n\nfunction makeS3Bucket(canaryToken: string): HoneypotResponse {\n const ownerId = genHex(32) + genHex(32); // 64 hex chars — real S3 owner IDs\n const now = new Date();\n const dateStr = (offset: number) =>\n new Date(now.getTime() - offset).toISOString().replace(/\\.\\d{3}Z/, \".000Z\");\n\n const body = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ListAllMyBucketsResult xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\">\n <Owner>\n <ID>${ownerId}</ID>\n <DisplayName>company-admin</DisplayName>\n </Owner>\n <Buckets>\n <Bucket>\n <Name>company-production-data</Name>\n <CreationDate>${dateStr(120 * 86400000)}</CreationDate>\n </Bucket>\n <Bucket>\n <Name>company-assets-${genHex(4)}</Name>\n <CreationDate>${dateStr(90 * 86400000)}</CreationDate>\n </Bucket>\n <Bucket>\n <Name>company-backups-${canaryToken.slice(0, 12)}</Name>\n <CreationDate>${dateStr(30 * 86400000)}</CreationDate>\n </Bucket>\n <Bucket>\n <Name>company-logs-archive</Name>\n <CreationDate>${dateStr(180 * 86400000)}</CreationDate>\n </Bucket>\n </Buckets>\n</ListAllMyBucketsResult>`;\n\n return {\n statusCode: 200,\n contentType: \"application/xml\",\n headers: {\n \"x-amz-request-id\": genHex(8).toUpperCase() + genHex(8).toUpperCase(),\n \"x-amz-id-2\": genBase64(60),\n \"Server\": \"AmazonS3\",\n },\n body,\n canaryToken,\n };\n}\n\n// ─── Admin portal HTML ────────────────────────────────────────────────────────\n//\n// Returns a convincing generic admin login page. The form posts back to a\n// sub-path (./login) so that POST requests with credential bodies are also\n// intercepted by the SDK's honeypot prefix-matching and captured as\n// http.honeypot.credential_attempt events.\n//\n// Design: clean, minimal, looks like a lightweight CMS admin panel.\n// The canary token is embedded in a hidden _token field — if an attacker\n// automates form submission with this token, subsequent requests carrying\n// it are detected.\n\nfunction makeAdminPortal(canaryToken: string): HoneypotResponse {\n const body = `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>Administration — Login</title>\n <style>\n *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n body {\n background: #0f1117;\n color: #c9d1d9;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Helvetica, Arial, sans-serif;\n display: flex; align-items: center; justify-content: center;\n min-height: 100vh;\n }\n .card {\n background: #161b22;\n border: 1px solid #30363d;\n border-radius: 8px;\n padding: 32px;\n width: 100%;\n max-width: 360px;\n }\n h1 { font-size: 1.1rem; font-weight: 600; color: #e6edf3; margin-bottom: 4px; }\n .subtitle { font-size: 0.8rem; color: #7d8590; margin-bottom: 24px; }\n label { display: block; font-size: 0.8rem; font-weight: 600; color: #c9d1d9; margin-bottom: 6px; }\n input[type=text], input[type=password] {\n width: 100%; padding: 8px 12px; border: 1px solid #30363d;\n border-radius: 6px; background: #0d1117; color: #c9d1d9;\n font-size: 0.875rem; outline: none; margin-bottom: 16px;\n }\n input:focus { border-color: #388bfd; box-shadow: 0 0 0 3px rgba(56,139,253,.1); }\n .row { display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px; }\n .row label { margin-bottom: 0; font-weight: normal; display: flex; align-items: center; gap: 6px; cursor: pointer; }\n button {\n width: 100%; padding: 8px 16px; background: #238636; color: #fff;\n border: none; border-radius: 6px; font-size: 0.875rem; font-weight: 600;\n cursor: pointer; transition: background .15s;\n }\n button:hover { background: #2ea043; }\n .footer { text-align: center; font-size: 0.72rem; color: #7d8590; margin-top: 20px; }\n </style>\n</head>\n<body>\n <div class=\"card\">\n <h1>Administration Panel</h1>\n <p class=\"subtitle\">Sign in to continue</p>\n <form method=\"POST\" action=\"./login\" autocomplete=\"off\">\n <input type=\"hidden\" name=\"_token\" value=\"${canaryToken}\" />\n <div>\n <label for=\"username\">Username</label>\n <input id=\"username\" name=\"username\" type=\"text\" placeholder=\"admin\" autocomplete=\"off\" />\n </div>\n <div>\n <label for=\"password\">Password</label>\n <input id=\"password\" name=\"password\" type=\"password\" placeholder=\"••••••••\" autocomplete=\"off\" />\n </div>\n <div class=\"row\">\n <label><input type=\"checkbox\" name=\"remember\" /> Remember me</label>\n <a href=\"#\" style=\"font-size:.8rem;color:#388bfd;text-decoration:none;\">Forgot password?</a>\n </div>\n <button type=\"submit\">Sign in</button>\n </form>\n <p class=\"footer\">v3.2.1 · Secure Administration Portal</p>\n </div>\n</body>\n</html>`;\n\n return {\n statusCode: 200,\n contentType: \"text/html; charset=utf-8\",\n headers: {\n \"Cache-Control\": \"no-store, no-cache\",\n \"X-Frame-Options\": \"SAMEORIGIN\",\n \"Server\": \"nginx/1.18.0 (Ubuntu)\",\n },\n body,\n canaryToken,\n };\n}\n\n/**\n * Response returned when an attacker submits credentials to an admin portal honeypot.\n * Returns a convincing \"invalid credentials\" page that lures them to try more combinations.\n */\nexport function makeAdminLoginFailed(): HoneypotResponse {\n const body = `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\" />\n <title>Administration — Login</title>\n <style>\n *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n body { background: #0f1117; color: #c9d1d9; font-family: -apple-system, sans-serif;\n display: flex; align-items: center; justify-content: center; min-height: 100vh; }\n .card { background: #161b22; border: 1px solid #30363d; border-radius: 8px; padding: 32px; width: 100%; max-width: 360px; }\n h1 { font-size: 1.1rem; font-weight: 600; color: #e6edf3; margin-bottom: 4px; }\n .error { background: #3d1010; border: 1px solid #f85149; color: #f85149; border-radius: 6px; padding: 10px 14px; font-size: .8rem; margin-bottom: 16px; }\n label { display: block; font-size: .8rem; font-weight: 600; color: #c9d1d9; margin-bottom: 6px; }\n input[type=text], input[type=password] { width: 100%; padding: 8px 12px; border: 1px solid #f85149; border-radius: 6px; background: #0d1117; color: #c9d1d9; font-size: .875rem; outline: none; margin-bottom: 16px; }\n button { width: 100%; padding: 8px 16px; background: #238636; color: #fff; border: none; border-radius: 6px; font-size: .875rem; font-weight: 600; cursor: pointer; }\n .footer { text-align: center; font-size: .72rem; color: #7d8590; margin-top: 20px; }\n </style>\n</head>\n<body>\n <div class=\"card\">\n <h1>Administration Panel</h1>\n <div class=\"error\">⚠ Invalid username or password. Please try again.</div>\n <form method=\"POST\" action=\"\" autocomplete=\"off\">\n <div><label for=\"u\">Username</label><input id=\"u\" name=\"username\" type=\"text\" autocomplete=\"off\" /></div>\n <div><label for=\"p\">Password</label><input id=\"p\" name=\"password\" type=\"password\" autocomplete=\"off\" /></div>\n <button type=\"submit\">Sign in</button>\n </form>\n <p class=\"footer\">v3.2.1 · Secure Administration Portal</p>\n </div>\n</body>\n</html>`;\n\n return {\n statusCode: 401,\n contentType: \"text/html; charset=utf-8\",\n headers: {\n \"Cache-Control\": \"no-store\",\n \"Server\": \"nginx/1.18.0 (Ubuntu)\",\n \"WWW-Authenticate\": 'Form realm=\"Administration Panel\"',\n },\n body,\n canaryToken: \"\",\n };\n}\n\n// ─── Generic ──────────────────────────────────────────────────────────────────\n\nfunction makeGeneric(canaryToken: string): HoneypotResponse {\n return {\n statusCode: 200,\n contentType: \"text/plain; charset=utf-8\",\n headers: { \"Cache-Control\": \"no-store\" },\n body: `# ${canaryToken}\\n`,\n canaryToken,\n };\n}\n\n// ─── Canary JWT generation ────────────────────────────────────────────────────\n\n/**\n * Generate a valid-format JWT that is expired (24h ago).\n * The `kid` header and `jti` claim both contain the canary token.\n * Any request arriving with this JWT (replayed or reforged with the same secret)\n * can be detected by checking `kid` against the org's canary registry.\n *\n * Format confirmed: Auth0 JWT docs + canarytokens.org JWT token implementation.\n */\nexport function generateCanaryJwt(canaryToken: string, secret: string): string {\n const now = Math.floor(Date.now() / 1000);\n // kid looks like a normal UUID-format signing key — detection uses jti instead\n const kid = `${canaryToken.slice(0,8)}-${canaryToken.slice(8,12)}-4${canaryToken.slice(13,16)}-${canaryToken.slice(16,20)}-${canaryToken.slice(20,32)}`;\n const header = { alg: \"HS256\", typ: \"JWT\", kid };\n const payload = {\n sub: \"svc_internal_7482\",\n name: \"service-account\",\n role: \"admin\",\n email: \"admin@internal.company.com\",\n iat: now - 172_800, // issued 48h ago (realistic stale credential)\n exp: now - 86_400, // expired 24h ago\n jti: canaryToken, // raw hex — no \"canary-\" prefix to leak purpose\n };\n\n const h = Buffer.from(JSON.stringify(header)).toString(\"base64url\");\n const p = Buffer.from(JSON.stringify(payload)).toString(\"base64url\");\n const sig = createHmac(\"sha256\", secret).update(`${h}.${p}`).digest(\"base64url\");\n return `${h}.${p}.${sig}`;\n}\n\n// ─── Credential helpers ───────────────────────────────────────────────────────\n\n/** AWS Access Key ID — 20 chars, AKIA prefix, position 5 = I, last = A */\nfunction makeAwsKeyId(): string {\n // Confirmed format: AKIA + I (pos 5) + 14 random [A-Z2-7] + A\n // Source: awsteele.com/blog/2020/09/26/aws-access-key-format\n const base32Chars = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567\";\n const middle = Array.from({ length: 13 }, () =>\n base32Chars[randomBytes(1)[0]! % base32Chars.length]!\n ).join(\"\");\n return `AKIAI${middle}A`; // AKIA + I + 13 chars + A = 20 total\n}\n\n/** AWS Secret Access Key — 40 chars, base64-standard charset */\nfunction makeAwsSecret(): string {\n // Confirmed: 40 chars, A-Z, a-z, 0-9, +, /\n // Source: summitroute.com/blog/2018/06/20/aws_security_credential_formats\n return randomBytes(30).toString(\"base64\").slice(0, 40);\n}\n\nfunction genHex(bytes: number): string {\n return randomBytes(bytes).toString(\"hex\");\n}\n\nfunction genAlphanumeric(len: number): string {\n const chars = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\";\n return Array.from({ length: len }, () =>\n chars[randomBytes(1)[0]! % chars.length]!\n ).join(\"\");\n}\n\nfunction genBase64(approxLen: number): string {\n return randomBytes(Math.ceil(approxLen * 3 / 4)).toString(\"base64\").slice(0, approxLen);\n}\n","import { AsyncLocalStorage } from \"node:async_hooks\";\nimport { EventBuffer } from \"./buffer.js\";\nimport { checkGeoVelocity } from \"./geo-velocity.js\";\nimport { scanForLeaks } from \"./sensitive.js\";\nimport type { AnomiraConfig, SdkEvent, FirewallRule, EndpointDeclaration } from \"./types.js\";\nimport { EventName } from \"./types.js\";\nimport { detectAgent } from \"./agent-detection.js\";\nimport { computeBrowserFingerprint, extractHttp2Settings, extractUpstreamTls } from \"./behavioral-fingerprint.js\";\nimport { detectHoneypotType, generateHoneypotResponse, generateCanaryJwt, makeAdminLoginFailed } from \"./honeypot-responses.js\";\nimport { randomBytes } from \"node:crypto\";\n\ninterface RequestContext {\n endpoint: string;\n method: string;\n ip: string;\n}\n\n// One shared ALS instance per SDK module load\nconst requestContext = new AsyncLocalStorage<RequestContext>();\n\nconst DEFAULT_INGEST_URL = \"https://ingest.anomira.io/v1/events\";\nconst DEFAULT_BATCH_SIZE = 100;\nconst DEFAULT_FLUSH_MS = 5_000;\nconst DEFAULT_MAX_RETRIES = 3;\n\n/**\n * AnomiraClient — the core SDK object.\n *\n * Instantiate once and reuse across your application:\n *\n * ```ts\n * import { Anomira } from \"@anomira/node-sdk\";\n *\n * export const sentinel = new Anomira({\n * apiKey: process.env.SENTINEL_API_KEY!,\n * appId: process.env.SENTINEL_APP_ID!,\n * });\n * ```\n */\nexport class AnomiraClient {\n readonly config: Required<Omit<AnomiraConfig, \"getUserId\" | \"getIp\" | \"detect\" | \"autoBlock\">> & {\n getUserId: NonNullable<AnomiraConfig[\"getUserId\"]>;\n getIp: NonNullable<AnomiraConfig[\"getIp\"]>;\n detect: Required<NonNullable<AnomiraConfig[\"detect\"]>>;\n captureConsole: boolean;\n service: string;\n autoBlock: Required<NonNullable<AnomiraConfig[\"autoBlock\"]>>;\n };\n\n private readonly buffer: EventBuffer;\n private readonly logBuffer: Array<{ level: string; service: string; message: string; meta: Record<string, unknown>; ts: number }> = [];\n private logFlushTimer: ReturnType<typeof setInterval> | null = null;\n private blocklistTimer: ReturnType<typeof setInterval> | null = null;\n private firewallTimer: ReturnType<typeof setInterval> | null = null;\n private communityThreatTimer: ReturnType<typeof setInterval> | null = null;\n /** True when credentials are missing — all operations become no-ops. */\n private disabled = false;\n /** In-process cache of manually blocked IPs — refreshed every 60 s. */\n private blockedIpCache: Set<string> = new Set();\n /** In-process cache of community threat IPs with their confidence scores.\n * Populated from Anomira's federated threat network — IPs confirmed malicious\n * across multiple customers. Refreshed every 60 s. */\n private communityThreatCache: Map<string, { score: number; topAttack: string }> = new Map();\n /**\n * Set of honeypot paths to intercept. When a request matches, the SDK returns\n * a fake response instead of forwarding to the customer's route handlers.\n * Synced from ingest every 60 s. Keys are lowercase path strings.\n */\n private honeypotPaths: Set<string> = new Set();\n /**\n * Set of active canary token strings embedded in previously-served honeypot\n * responses. When any of these appear in a request's Authorization header or\n * body, a http.canary.triggered event is fired.\n * Synced from ingest every 60 s (max 100 tokens, sliding 7-day window).\n */\n private canaryTokenCache: Set<string> = new Set();\n /** In-process cache of firewall rules with pre-compiled regex — refreshed every 60 s. */\n private compiledRules: Array<{ rule: FirewallRule; re?: RegExp }> = [];\n // Saved originals — used by SDK internals so patched console doesn't recurse\n private readonly _origLog = console.log.bind(console);\n private readonly _origWarn = console.warn.bind(console);\n private readonly _origError = console.error.bind(console);\n\n constructor(config: AnomiraConfig) {\n if (!config.apiKey || !config.appId) {\n const missing = [!config.apiKey && \"apiKey\", !config.appId && \"appId\"].filter(Boolean).join(\", \");\n console.warn(`[Anomira] SDK disabled — missing config: ${missing}. Set SENTINEL_API_KEY and SENTINEL_APP_ID to enable monitoring.`);\n this.disabled = true;\n // Provide safe defaults so the rest of the class doesn't blow up\n this.config = {\n apiKey: \"\", appId: \"\", ingestUrl: DEFAULT_INGEST_URL, geoLookupUrl: \"\",\n maxBatchSize: DEFAULT_BATCH_SIZE, flushIntervalMs: DEFAULT_FLUSH_MS,\n maxRetries: DEFAULT_MAX_RETRIES, debug: false, captureConsole: false, service: \"app\",\n getUserId: defaultGetUserId, getIp: defaultGetIp,\n detect: { bruteForce: true, rateAbuse: true, pathTraversal: true, xss: true, scanDetection: true, geoVelocity: true },\n autoBlock: { enabled: true, communityThreshold: 85 },\n };\n this.buffer = new EventBuffer({ appId: \"\", apiKey: \"\", ingestUrl: DEFAULT_INGEST_URL, maxBatchSize: 0, flushIntervalMs: 999_999_999, maxRetries: 0, debug: false });\n return;\n }\n\n this.config = {\n apiKey: config.apiKey,\n appId: config.appId,\n ingestUrl: config.ingestUrl ?? DEFAULT_INGEST_URL,\n geoLookupUrl: config.geoLookupUrl ?? \"\",\n maxBatchSize: config.maxBatchSize ?? DEFAULT_BATCH_SIZE,\n flushIntervalMs: config.flushIntervalMs ?? DEFAULT_FLUSH_MS,\n maxRetries: config.maxRetries ?? DEFAULT_MAX_RETRIES,\n debug: config.debug ?? false,\n captureConsole: config.captureConsole ?? false,\n service: config.service ?? \"app\",\n getUserId: config.getUserId ?? defaultGetUserId,\n getIp: config.getIp ?? defaultGetIp,\n detect: {\n bruteForce: config.detect?.bruteForce ?? true,\n rateAbuse: config.detect?.rateAbuse ?? true,\n pathTraversal: config.detect?.pathTraversal ?? true,\n xss: config.detect?.xss ?? true,\n scanDetection: config.detect?.scanDetection ?? true,\n geoVelocity: config.detect?.geoVelocity ?? true,\n },\n autoBlock: {\n enabled: config.autoBlock?.enabled ?? true,\n communityThreshold: config.autoBlock?.communityThreshold ?? 85,\n },\n };\n\n this.buffer = new EventBuffer({\n appId: this.config.appId,\n apiKey: this.config.apiKey,\n ingestUrl: this.config.ingestUrl,\n maxBatchSize: this.config.maxBatchSize,\n flushIntervalMs:this.config.flushIntervalMs,\n maxRetries: this.config.maxRetries,\n debug: this.config.debug,\n });\n\n void this.#validateCredentials();\n\n // Fetch blocked-IP list immediately, then refresh every 60 s\n void this.#refreshBlocklist();\n this.blocklistTimer = setInterval(() => { void this.#refreshBlocklist(); }, 60_000);\n if (this.blocklistTimer.unref) this.blocklistTimer.unref();\n\n // Fetch firewall rules immediately, then refresh every 60 s\n void this.#refreshFirewallRules();\n this.firewallTimer = setInterval(() => { void this.#refreshFirewallRules(); }, 60_000);\n if (this.firewallTimer.unref) this.firewallTimer.unref();\n\n // Fetch honeypot paths immediately, then refresh every 60 s.\n void this.#refreshHoneypotPaths();\n const honeypotTimer = setInterval(() => { void this.#refreshHoneypotPaths(); }, 60_000);\n if ((honeypotTimer as { unref?: () => void }).unref) (honeypotTimer as { unref: () => void }).unref();\n\n // Fetch canary tokens (harvested credential strings to detect in requests).\n void this.#refreshCanaryTokens();\n const canaryTimer = setInterval(() => { void this.#refreshCanaryTokens(); }, 60_000);\n if ((canaryTimer as { unref?: () => void }).unref) (canaryTimer as { unref: () => void }).unref();\n\n // Fetch community threat intelligence immediately, then refresh every 60 s.\n // This is the \"invisible security\" layer — IPs from Anomira's federated\n // network are auto-blocked when their confidence score meets the threshold,\n // regardless of whether a human has manually reviewed them.\n void this.#refreshCommunityThreats();\n this.communityThreatTimer = setInterval(() => { void this.#refreshCommunityThreats(); }, 60_000);\n if (this.communityThreatTimer.unref) this.communityThreatTimer.unref();\n\n // Flush logs every 10s (separate from events buffer)\n this.logFlushTimer = setInterval(() => { void this.#flushLogs(); }, 10_000);\n if (this.logFlushTimer.unref) this.logFlushTimer.unref();\n\n if (this.config.captureConsole) this.#interceptConsole();\n }\n\n #interceptConsole(): void {\n const map: Array<[keyof Console, \"debug\" | \"info\" | \"warn\" | \"error\"]> = [\n [\"debug\", \"debug\"],\n [\"log\", \"info\"],\n [\"info\", \"info\"],\n [\"warn\", \"warn\"],\n [\"error\", \"error\"],\n ];\n for (const [method, level] of map) {\n const original = (console[method] as (...a: unknown[]) => void).bind(console);\n (console as unknown as Record<string, unknown>)[method] = (...args: unknown[]) => {\n original(...args); // still prints to terminal\n // Skip SDK's own internal messages to avoid noise in the Logs dashboard\n const first = args[0];\n if (typeof first === \"string\" && first.startsWith(\"[Anomira]\")) return;\n const message = args\n .map((a) => (typeof a === \"string\" ? a : JSON.stringify(a)))\n .join(\" \");\n const ctx = requestContext.getStore();\n this.log(level, message, {\n service: this.config.service,\n ...(ctx ? { endpoint: ctx.endpoint, method: ctx.method, ip: ctx.ip } : {}),\n });\n };\n }\n }\n\n async #refreshBlocklist(): Promise<void> {\n const syncUrl = this.config.ingestUrl.replace(/\\/v1\\/events$/, \"/v1/blocked-ips/sync\");\n try {\n const res = await fetch(syncUrl, {\n headers: {\n Authorization: `Bearer ${this.config.apiKey}`,\n \"User-Agent\": `@anomira/node-sdk/0.2.2`,\n },\n signal: AbortSignal.timeout(5_000),\n });\n if (!res.ok) return;\n const data = await res.json() as { ips?: string[] };\n this.blockedIpCache = new Set(data.ips ?? []);\n if (this.config.debug && this.blockedIpCache.size > 0) {\n this._origLog(`[Anomira] blocklist refreshed — ${this.blockedIpCache.size} blocked IPs`);\n }\n } catch {\n // Network error: keep the existing cache, never throw\n }\n }\n\n async #refreshCommunityThreats(): Promise<void> {\n const syncUrl = this.config.ingestUrl.replace(/\\/v1\\/events$/, \"/v1/community-threats/sync\");\n try {\n const res = await fetch(syncUrl, {\n headers: {\n Authorization: `Bearer ${this.config.apiKey}`,\n \"User-Agent\": `@anomira/node-sdk/0.2.2`,\n },\n signal: AbortSignal.timeout(5_000),\n });\n if (!res.ok) return;\n\n const data = await res.json() as {\n threats?: Array<{ ip: string; score: number; topAttack: string }>;\n };\n\n const newCache = new Map<string, { score: number; topAttack: string }>();\n for (const t of data.threats ?? []) {\n newCache.set(t.ip, { score: t.score, topAttack: t.topAttack });\n }\n this.communityThreatCache = newCache;\n\n if (this.config.debug && newCache.size > 0) {\n this._origLog(`[Anomira] community threats refreshed — ${newCache.size} known threat IPs`);\n }\n } catch {\n // Network error: keep the existing cache, never throw\n }\n }\n\n async #refreshHoneypotPaths(): Promise<void> {\n const syncUrl = this.config.ingestUrl.replace(/\\/v1\\/events$/, \"/v1/honeypots/sync\");\n try {\n const res = await fetch(syncUrl, {\n headers: { Authorization: `Bearer ${this.config.apiKey}`, \"User-Agent\": \"@anomira/node-sdk/0.2.2\" },\n signal: AbortSignal.timeout(5_000),\n });\n if (!res.ok) return;\n const data = await res.json() as { paths?: string[] };\n this.honeypotPaths = new Set((data.paths ?? []).map((p) => p.toLowerCase()));\n if (this.config.debug && this.honeypotPaths.size > 0) {\n this._origLog(`[Anomira] honeypot paths refreshed — ${this.honeypotPaths.size} traps active`);\n }\n } catch { /* keep existing cache on error */ }\n }\n\n async #refreshCanaryTokens(): Promise<void> {\n const syncUrl = this.config.ingestUrl.replace(/\\/v1\\/events$/, \"/v1/canary-tokens/sync\");\n try {\n const res = await fetch(syncUrl, {\n headers: { Authorization: `Bearer ${this.config.apiKey}`, \"User-Agent\": \"@anomira/node-sdk/0.2.2\" },\n signal: AbortSignal.timeout(5_000),\n });\n if (!res.ok) return;\n const data = await res.json() as { tokens?: string[] };\n this.canaryTokenCache = new Set(data.tokens ?? []);\n } catch { /* keep existing cache on error */ }\n }\n\n async #refreshFirewallRules(): Promise<void> {\n const syncUrl = this.config.ingestUrl.replace(/\\/v1\\/events$/, \"/v1/firewall-rules/sync\");\n try {\n const res = await fetch(syncUrl, {\n headers: {\n Authorization: `Bearer ${this.config.apiKey}`,\n \"User-Agent\": `@anomira/node-sdk/0.1.0`,\n },\n signal: AbortSignal.timeout(5_000),\n });\n if (!res.ok) return;\n const data = await res.json() as { rules?: FirewallRule[] };\n this.compiledRules = (data.rules ?? []).map((rule) => ({\n rule,\n re: rule.operator === \"regex\" ? (() => { try { return new RegExp(rule.value, \"i\"); } catch { return undefined; } })() : undefined,\n }));\n if (this.config.debug && this.compiledRules.length > 0) {\n this._origLog(`[Anomira] firewall rules refreshed — ${this.compiledRules.length} active rules`);\n }\n } catch {\n // Network error: keep existing cache, never throw\n }\n }\n\n /** Evaluate all cached firewall rules against the current request.\n * Returns the first matching rule, or null if none match. */\n #matchFirewallRule(req: {\n url: string;\n body: unknown;\n headers: Record<string, string | undefined>;\n ip: string;\n }): { rule: FirewallRule } | null {\n for (const { rule, re } of this.compiledRules) {\n let target: string;\n switch (rule.field) {\n case \"url\": target = req.url; break;\n case \"body\": target = typeof req.body === \"string\" ? req.body : JSON.stringify(req.body ?? \"\"); break;\n case \"header\": target = req.headers[(rule.headerName ?? \"\").toLowerCase()] ?? \"\"; break;\n case \"user_agent\": target = req.headers[\"user-agent\"] ?? \"\"; break;\n case \"ip\": target = req.ip; break;\n default: continue;\n }\n\n const matched = rule.operator === \"regex\"\n ? (re?.test(target) ?? false)\n : rule.operator === \"contains\" ? target.includes(rule.value)\n : rule.operator === \"equals\" ? target === rule.value\n : rule.operator === \"starts_with\" ? target.startsWith(rule.value)\n : rule.operator === \"ends_with\" ? target.endsWith(rule.value)\n : false;\n\n if (matched) return { rule };\n }\n return null;\n }\n\n async #flushLogs(): Promise<void> {\n if (this.logBuffer.length === 0) return;\n const batch = this.logBuffer.splice(0, 500);\n const logsUrl = this.config.ingestUrl.replace(/\\/v1\\/events$/, \"/v1/logs\");\n try {\n const res = await fetch(logsUrl, {\n method: \"POST\",\n redirect: \"manual\",\n headers: {\n Authorization: `Bearer ${this.config.apiKey}`,\n \"Content-Type\": \"application/json\",\n \"User-Agent\": `@anomira/node-sdk/0.1.0`,\n },\n body: JSON.stringify({ appId: this.config.appId, logs: batch }),\n signal: AbortSignal.timeout(8_000),\n });\n if (this.config.debug) {\n this._origLog(`[Anomira] [logs] ✅ sent ${batch.length} log entries (${res.status})`);\n }\n } catch {\n // Re-queue on failure — put them back at the front\n this.logBuffer.unshift(...batch);\n }\n }\n\n async #validateCredentials(): Promise<void> {\n // Derive the ping URL from the ingest URL: swap /v1/events → /v1/ping\n const pingUrl = this.config.ingestUrl.replace(/\\/v1\\/events$/, \"/v1/ping\")\n + `?appId=${encodeURIComponent(this.config.appId)}`;\n\n try {\n const res = await fetch(pingUrl, {\n method: \"GET\",\n redirect: \"manual\",\n headers: {\n Authorization: `Bearer ${this.config.apiKey}`,\n \"User-Agent\": `@anomira/node-sdk/0.1.0`,\n },\n signal: AbortSignal.timeout(5_000),\n });\n\n if (res.status >= 300 && res.status < 400) {\n this._origWarn(`[Anomira] ❌ Wrong ingest URL — got redirect to ${res.headers.get(\"location\")}. Check SENTINEL_INGEST_URL.`);\n return;\n }\n if (res.ok) {\n this._origLog(`[Anomira] ✅ Connected (appId: ${this.config.appId.slice(0, 8)}…)`);\n return;\n }\n if (res.status === 401) {\n this._origWarn(\"[Anomira] ❌ Invalid API key — check your SENTINEL_API_KEY\");\n return;\n }\n if (res.status === 403) {\n this._origWarn(\"[Anomira] ❌ App not found or appId mismatch — check your SENTINEL_APP_ID\");\n return;\n }\n this._origWarn(`[Anomira] ⚠️ Ingest returned HTTP ${res.status} — check your configuration`);\n } catch {\n this._origWarn(\"[Anomira] ⚠️ Could not reach ingest endpoint — check SENTINEL_INGEST_URL (current: \" + this.config.ingestUrl + \")\");\n }\n }\n\n // ─── Public API ────────────────────────────────────────────────────────────\n\n /**\n * Track a custom security event.\n *\n * ```ts\n * // Track a failed OTP attempt\n * sentinel.track(EventName.OTP_FAILED, {\n * ip: req.ip,\n * userId: req.body.phone,\n * meta: { endpoint: \"/api/verify-otp\", attempts: 3 },\n * });\n * ```\n */\n /**\n * Returns true if the IP should be blocked. Synchronous — no network call.\n *\n * Checks two independent sources:\n * 1. Manual blocklist — IPs explicitly blocked by the customer or via playbooks.\n * 2. Community threats — IPs from Anomira's federated network whose confidence\n * score meets the autoBlock.communityThreshold (default 85).\n * Only active when autoBlock.enabled is true (the default).\n *\n * If a customer sets autoBlock.enabled = false, only the manual blocklist is checked.\n */\n isBlocked(ip: string): boolean {\n if (this.disabled) return false;\n\n // Check manual blocklist first (always active)\n if (this.blockedIpCache.has(ip)) return true;\n\n // Check community threat network (active when autoBlock.enabled = true)\n if (this.config.autoBlock.enabled) {\n const threat = this.communityThreatCache.get(ip);\n if (threat && threat.score >= this.config.autoBlock.communityThreshold) return true;\n }\n\n return false;\n }\n\n /**\n * Returns the block reason for an IP — useful for logging or custom responses.\n * Returns null if the IP is not blocked.\n */\n blockReason(ip: string): { source: \"manual\" | \"community\"; score?: number; topAttack?: string } | null {\n if (this.disabled) return null;\n if (this.blockedIpCache.has(ip)) return { source: \"manual\" };\n if (this.config.autoBlock.enabled) {\n const threat = this.communityThreatCache.get(ip);\n if (threat && threat.score >= this.config.autoBlock.communityThreshold) {\n return { source: \"community\", score: threat.score, topAttack: threat.topAttack };\n }\n }\n return null;\n }\n\n /**\n * Fire-and-forget: report a blocked-IP attempt to the ingest server so the\n * dashboard can show that the block is actively working.\n * Called automatically by the Express/Fastify middleware — no manual call needed.\n */\n reportBlockedHit(ip: string, meta: { method: string; url: string; userAgent: string }): void {\n if (this.disabled) return;\n const blockedHitUrl = this.config.ingestUrl.replace(/\\/v1\\/events$/, \"/v1/blocked-hit\");\n fetch(blockedHitUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Authorization\": `Bearer ${this.config.apiKey}`,\n },\n body: JSON.stringify({\n appId: this.config.appId,\n ip,\n method: meta.method,\n endpoint: meta.url,\n userAgent: meta.userAgent,\n ts: Date.now(),\n }),\n }).catch(() => null);\n }\n\n /** Evaluate firewall rules against a request. Returns the matched rule or null. Synchronous. */\n matchFirewallRule(req: {\n url: string;\n body: unknown;\n headers: Record<string, string | undefined>;\n ip: string;\n }): { rule: FirewallRule } | null {\n return this.#matchFirewallRule(req);\n }\n\n track(\n eventName: string,\n data: {\n /**\n * Client IP address. Optional — when omitted or empty the SDK\n * automatically reads it from the active request context set by the\n * Express/Fastify middleware. This means developers calling track()\n * inside a request handler get the correct IP for free, with no extra\n * configuration required.\n *\n * Only pass this explicitly when calling track() outside a request\n * context (e.g. from a background job or a webhook handler that has\n * the IP available separately).\n */\n ip?: string;\n userId?: string;\n meta?: Record<string, unknown>;\n },\n ): void {\n if (this.disabled) return;\n\n // ── IP auto-resolution ────────────────────────────────────────────────────\n // Priority order:\n // 1. Explicitly passed ip (non-empty, non-zero)\n // 2. IP from the active request context (set by Express/Fastify middleware)\n // 3. Fall back to \"0.0.0.0\" so the event is still recorded\n const ctx = requestContext.getStore();\n const resolvedIp =\n (data.ip && data.ip !== \"0.0.0.0\" && data.ip !== \"\")\n ? data.ip\n : (ctx?.ip ?? \"0.0.0.0\");\n\n // ── Endpoint auto-resolution ──────────────────────────────────────────────\n // If the caller didn't include an endpoint in meta, inject it from context.\n // This ensures REQUEST SAMPLE shows a real endpoint, not \"POST —\".\n const resolvedMeta: Record<string, unknown> | undefined =\n ctx?.endpoint && !data.meta?.[\"endpoint\"]\n ? { endpoint: ctx.endpoint, method: ctx.method, ...data.meta }\n : data.meta;\n\n const event: SdkEvent = {\n name: eventName,\n ts: Date.now(),\n ip: resolvedIp,\n userId: data.userId,\n meta: resolvedMeta,\n };\n this.buffer.push(event);\n }\n\n /**\n * Track a successful login AND run geo-velocity check.\n * If impossible travel is detected, automatically fires an additional\n * `auth.login.geo_velocity` event with full context.\n *\n * ```ts\n * await sentinel.trackLogin({\n * ip: req.ip,\n * userId: user.id,\n * });\n * ```\n */\n async trackLogin(data: {\n ip?: string;\n userId: string;\n meta?: Record<string, unknown>;\n }): Promise<void> {\n if (this.disabled) return;\n const tsMs = Date.now();\n\n // Resolve IP the same way track() does — from context when not passed\n const ctx = requestContext.getStore();\n const resolvedIp = (data.ip && data.ip !== \"0.0.0.0\") ? data.ip : (ctx?.ip ?? \"0.0.0.0\");\n\n // Record the successful login\n this.track(EventName.LOGIN_SUCCESS, { ...data, ip: resolvedIp, meta: { ...data.meta } });\n\n // Check for geo-velocity if enabled\n if (!this.config.detect.geoVelocity) return;\n\n try {\n const result = await checkGeoVelocity(data.userId, resolvedIp, tsMs, this.config.geoLookupUrl || undefined);\n if (!result) return;\n\n this.track(EventName.GEO_VELOCITY, {\n ip: resolvedIp,\n userId: data.userId,\n meta: {\n distanceKm: result.distanceKm,\n speedKmH: result.speedKmH,\n fromIp: result.from.ip,\n fromCity: result.from.city,\n fromCountry: result.from.country,\n toCity: result.to.city,\n toCountry: result.to.country,\n minutesDiff: Math.round((result.to.tsMs - result.from.tsMs) / 60_000),\n ...data.meta,\n },\n });\n } catch {\n // Geo-velocity check failures are always silent\n }\n }\n\n /**\n * Track phone-based authentication (OTP via SMS, WhatsApp, or call).\n * The ingest service uses this to detect SIM swap patterns:\n * if the same userId authenticates via phone but then appears on a new\n * device/IP shortly after, it's flagged as a suspected SIM swap.\n *\n * ```ts\n * await sentinel.trackPhoneAuth({\n * ip: req.ip,\n * userId: user.id,\n * phone: user.phoneNumber,\n * });\n * ```\n */\n trackPhoneAuth(data: {\n ip?: string;\n userId: string;\n phone: string;\n meta?: Record<string, unknown>;\n }): void {\n if (this.disabled) return;\n this.track(EventName.PHONE_AUTH, {\n ip: data.ip, // track() auto-resolves from context if empty\n userId: data.userId,\n meta: { phone: data.phone, ...data.meta },\n });\n }\n\n /**\n * Send a structured log entry to the Anomira Logs dashboard.\n *\n * ```ts\n * sentinel.log(\"info\", \"User registered\", { userId: user.id });\n * sentinel.log(\"warn\", \"Slow DB query detected\", { queryMs: 1240 });\n * sentinel.log(\"error\", \"Payment failed\", { reason: err.message });\n * ```\n */\n log(\n level: \"debug\" | \"info\" | \"warn\" | \"error\" | \"fatal\",\n message: string,\n meta?: Record<string, unknown> & { service?: string },\n ): void {\n if (this.disabled) return;\n const { service, ...rest } = meta ?? {};\n\n const leaks = scanForLeaks(message);\n if (leaks.length > 0) {\n rest[\"sensitiveLeaks\"] = leaks.map((l) => l.type);\n if (this.config.debug) {\n this._origWarn(\n `[Anomira] ⚠️ Sensitive data in log (${leaks.map((l) => l.label).join(\", \")}): \"${message.slice(0, 60)}…\"`,\n );\n }\n }\n\n this.logBuffer.push({ level, service: service ?? this.config.service, message, meta: rest, ts: Date.now() });\n if (this.config.debug && leaks.length === 0) {\n this._origLog(`[Anomira] log:${level} ${message}`);\n }\n if (this.logBuffer.length >= 50) void this.#flushLogs();\n }\n\n /**\n * Declare your API's known endpoints so Anomira can flag undiscovered\n * traffic as shadow endpoints.\n *\n * Call this once on startup after your routes are registered:\n *\n * ```ts\n * await sentinel.declareEndpoints([\n * { method: \"GET\", path: \"/api/users/:id\", auth: true },\n * { method: \"POST\", path: \"/api/orders\", auth: true },\n * { method: \"GET\", path: \"/api/health\", auth: false },\n * ]);\n * ```\n */\n async declareEndpoints(endpoints: EndpointDeclaration[]): Promise<void> {\n if (this.disabled || endpoints.length === 0) return;\n const url = this.config.ingestUrl.replace(/\\/v1\\/events$/, \"/v1/declare-endpoints\");\n try {\n await fetch(url, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.config.apiKey}`,\n \"Content-Type\": \"application/json\",\n \"User-Agent\": `@anomira/node-sdk/0.1.0`,\n },\n body: JSON.stringify({ appId: this.config.appId, endpoints }),\n signal: AbortSignal.timeout(10_000),\n });\n } catch {\n // Network errors must never crash the host app\n }\n }\n\n /**\n * Flush all pending events immediately.\n * Useful before a graceful shutdown outside of the process lifecycle hooks.\n */\n async flush(): Promise<void> {\n if (this.disabled) return;\n // Clear all refresh timers so they don't fire after shutdown\n if (this.blocklistTimer) { clearInterval(this.blocklistTimer); this.blocklistTimer = null; }\n if (this.firewallTimer) { clearInterval(this.firewallTimer); this.firewallTimer = null; }\n if (this.communityThreatTimer) { clearInterval(this.communityThreatTimer); this.communityThreatTimer = null; }\n if (this.logFlushTimer) { clearInterval(this.logFlushTimer); this.logFlushTimer = null; }\n await Promise.all([this.buffer.flush(), this.#flushLogs()]);\n }\n\n /**\n * Express middleware — auto-instruments all routes.\n *\n * ```ts\n * app.use(sentinel.express());\n * ```\n */\n express() {\n return createExpressMiddleware(this);\n }\n\n /**\n * Fastify plugin — auto-instruments all routes.\n *\n * ```ts\n * await app.register(sentinel.fastify());\n * ```\n */\n fastify() {\n return createFastifyPlugin(this);\n }\n}\n\n// ─── Default extractors ──────────────────────────────────────────────────────\n\n/**\n * Automatic userId extraction — tries every common Node.js auth pattern\n * in priority order so the SDK works with zero configuration for most apps.\n *\n * Tier 1: req.user.*\n * Set by Passport.js, express-jwt v6, Firebase Admin SDK, @fastify/passport,\n * @fastify/jwt, most JWT-verification middleware.\n * Fields tried: id, userId, sub (JWT standard), _id (MongoDB), uid (Firebase),\n * user_id (snake_case), accountId, account_id, customerId, customer_id.\n *\n * Tier 2: req.auth.*\n * express-jwt v7+ moved from req.user to req.auth. Same field list as Tier 1.\n *\n * Tier 3: Direct on req\n * Some custom middleware sets req.userId / req.accountId directly.\n *\n * Tier 4: req.session.*\n * express-session with req.session.userId or req.session.user.id.\n *\n * Tier 5: JWT payload decode from Authorization header ← LAST RESORT\n * Decodes the JWT token in the Authorization: Bearer header WITHOUT verifying\n * the signature (verification is the app's auth middleware's job — not ours).\n * Used purely to extract the userId claim from the payload.\n * Works even when the developer forgot to register their auth middleware before\n * the Anomira middleware, or uses a custom auth system that doesn't set req.user.\n * Claims tried: sub, id, userId, user_id, uid, accountId, account_id.\n *\n * If none of these find a userId, returns undefined — the event is recorded as\n * anonymous traffic and is excluded from per-user behavioral analysis.\n *\n * Customers can always override this with getUserId: (req) => req.myCustomField.\n */\nfunction defaultGetUserId(req: unknown): string | undefined {\n const r = req as Record<string, unknown>;\n\n // ── Tier 1: req.user.* ────────────────────────────────────────────────────\n const user = r[\"user\"] as Record<string, unknown> | undefined;\n if (user && typeof user === \"object\") {\n const id = pickId(user);\n if (id) return id;\n }\n\n // ── Tier 2: req.auth.* (express-jwt v7+) ─────────────────────────────────\n const auth = r[\"auth\"] as Record<string, unknown> | undefined;\n if (auth && typeof auth === \"object\") {\n const id = pickId(auth);\n if (id) return id;\n }\n\n // ── Tier 3: Direct on req ─────────────────────────────────────────────────\n const direct =\n (r[\"userId\"] as string | undefined) ??\n (r[\"user_id\"] as string | undefined) ??\n (r[\"accountId\"] as string | undefined) ??\n (r[\"account_id\"] as string | undefined) ??\n (r[\"customerId\"] as string | undefined);\n if (direct && typeof direct === \"string\") return direct;\n\n // ── Tier 4: req.session.* (express-session) ───────────────────────────────\n const session = r[\"session\"] as Record<string, unknown> | undefined;\n if (session && typeof session === \"object\") {\n const sessionDirect =\n (session[\"userId\"] as string | undefined) ??\n (session[\"user_id\"] as string | undefined);\n if (sessionDirect) return sessionDirect;\n\n const sessionUser = session[\"user\"] as Record<string, unknown> | undefined;\n if (sessionUser && typeof sessionUser === \"object\") {\n const id = pickId(sessionUser);\n if (id) return id;\n }\n }\n\n // ── Tier 5: JWT decode from Authorization header ──────────────────────────\n // Decode without verifying — we only want the userId claim, not to authenticate.\n const headers = r[\"headers\"] as Record<string, string | string[] | undefined> | undefined;\n const rawAuth = headers?.[\"authorization\"];\n const authStr = Array.isArray(rawAuth) ? rawAuth[0] : rawAuth;\n if (authStr?.startsWith(\"Bearer \")) {\n const token = authStr.slice(7);\n try {\n const parts = token.split(\".\");\n if (parts.length === 3) {\n // Base64URL → Base64 (replace - with + and _ with /) then pad to 4-byte boundary\n const b64 = parts[1]!.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padded = b64 + \"=\".repeat((4 - (b64.length % 4)) % 4);\n const payload = JSON.parse(\n Buffer.from(padded, \"base64\").toString(\"utf8\"),\n ) as Record<string, unknown>;\n const jwtId = pickId(payload);\n if (jwtId) return jwtId;\n // Some JWTs wrap user data one level deep: { data: { id: \"...\" } }\n for (const val of Object.values(payload)) {\n if (val && typeof val === \"object\" && !Array.isArray(val)) {\n const nested = pickId(val as Record<string, unknown>);\n if (nested) return nested;\n }\n }\n }\n } catch {\n // Not a valid JWT or decode failed — move on silently\n }\n }\n\n return undefined;\n}\n\n/**\n * Extract the most likely userId field from a plain object.\n * Returns the first non-empty string found among the standard field names,\n * or undefined if nothing useful is found.\n */\nfunction pickId(obj: Record<string, unknown>): string | undefined {\n const id =\n (obj[\"id\"] as string | undefined) ??\n (obj[\"sub\"] as string | undefined) ?? // JWT standard claim\n (obj[\"userId\"] as string | undefined) ??\n (obj[\"user_id\"] as string | undefined) ??\n (obj[\"uid\"] as string | undefined) ?? // Firebase\n (obj[\"_id\"] as string | undefined) ?? // MongoDB\n (obj[\"accountId\"] as string | undefined) ??\n (obj[\"account_id\"] as string | undefined) ??\n (obj[\"customerId\"] as string | undefined) ??\n (obj[\"customer_id\"] as string | undefined);\n // Only return if it's a non-empty string (guards against null, 0, undefined)\n return typeof id === \"string\" && id.length > 0 ? id : undefined;\n}\n\nfunction normalizeIp(raw: string): string {\n if (raw === \"::1\") return \"127.0.0.1\";\n if (raw === \"::ffff:127.0.0.1\") return \"127.0.0.1\";\n if (raw.startsWith(\"::ffff:\")) return raw.slice(7);\n return raw;\n}\n\nfunction defaultGetIp(req: unknown): string {\n const r = req as Record<string, unknown>;\n const fwd = r[\"headers\"] as Record<string, string | string[] | undefined> | undefined;\n\n // Pull the first non-empty value from a header (handles comma-lists like XFF)\n function firstHdr(name: string): string | undefined {\n const v = fwd?.[name];\n if (!v) return undefined;\n const s = (Array.isArray(v) ? v[0] : v.split(\",\")[0])?.trim();\n return s || undefined;\n }\n\n // Priority order: CDN/cloud-specific headers first (harder to spoof when behind\n // those services), then standard XFF, then socket fallback.\n const ip =\n firstHdr(\"cf-connecting-ip\") ?? // Cloudflare\n firstHdr(\"true-client-ip\") ?? // Cloudflare Enterprise / Akamai\n firstHdr(\"x-forwarded-for\") ?? // Nginx, AWS ALB, GCP LB, most proxies\n firstHdr(\"x-real-ip\") ?? // Nginx (single-IP alternative to XFF)\n firstHdr(\"fastly-client-ip\") ?? // Fastly CDN\n firstHdr(\"x-client-ip\") ?? // Generic reverse proxies\n firstHdr(\"x-cluster-client-ip\") ?? // Cluster / k8s ingress\n (r[\"socket\"] as { remoteAddress?: string } | undefined)?.remoteAddress ??\n \"0.0.0.0\";\n\n return normalizeIp(ip);\n}\n\n// ─── Fake response sender (shared by honeypot + credential handlers) ─────────\n\nimport type { HoneypotResponse } from \"./honeypot-responses.js\";\n\nfunction sendFakeResponse(res: Record<string, unknown>, fakeResponse: HoneypotResponse): void {\n const allHeaders: Record<string, string> = {\n \"Content-Type\": fakeResponse.contentType,\n ...fakeResponse.headers,\n };\n if (typeof res[\"set\"] === \"function\") {\n (res[\"set\"] as (h: Record<string, string>) => void)(allHeaders);\n } else if (typeof res[\"setHeader\"] === \"function\") {\n for (const [k, v] of Object.entries(allHeaders)) {\n (res[\"setHeader\"] as (k: string, v: string) => void)(k, v);\n }\n }\n if (typeof res[\"status\"] === \"function\") {\n (res[\"status\"] as (c: number) => unknown)(fakeResponse.statusCode);\n } else {\n res[\"statusCode\"] = fakeResponse.statusCode;\n }\n if (typeof res[\"end\"] === \"function\") {\n (res[\"end\"] as (b: string) => void)(fakeResponse.body);\n }\n}\n\n// ─── Lazy imports for middleware factories ───────────────────────────────────\n// Imported lazily so the SDK has no hard dependency on express/fastify at runtime\n\nfunction isLoopbackIp(ip: string): boolean {\n return ip === \"127.0.0.1\" || ip === \"0.0.0.0\" || ip === \"::1\";\n}\n\nfunction createExpressMiddleware(client: AnomiraClient) {\n let ipCheckCount = 0;\n let ipLoopCount = 0;\n let ipWarnFired = false;\n\n let userIdCheckCount = 0;\n let userIdMissCount = 0;\n let userIdWarnFired = false;\n\n return async function sentinelMiddleware(\n req: Record<string, unknown>,\n res: Record<string, unknown>,\n next: () => void,\n ) {\n const startMs = Date.now();\n const ip = client.config.getIp(req);\n\n // Warn once if the first 20 requests are consistently loopback — almost\n // always means the reverse proxy isn't forwarding X-Forwarded-For.\n if (!ipWarnFired && ipCheckCount < 20) {\n ipCheckCount++;\n if (isLoopbackIp(ip)) ipLoopCount++;\n if (ipCheckCount === 20 && ipLoopCount >= 16) {\n ipWarnFired = true;\n console.warn(\n \"[Anomira] WARNING: client IP not captured on 80%+ of requests.\\n\" +\n \" Your app is likely behind a reverse proxy (Nginx, Cloudflare, AWS ALB)\\n\" +\n \" that is not forwarding client IP headers. Alerts will have no IP attribution.\\n\\n\" +\n \" Nginx fix: proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\\n\" +\n \" proxy_set_header X-Real-IP $remote_addr;\\n\" +\n \" Express fix: app.set('trust proxy', 1);\\n\" +\n \" Fastify fix: Fastify({ trustProxy: true })\\n\" +\n \" Docs: https://docs.anomira.io/sdk/ip-capture\"\n );\n }\n }\n\n // ── Block check — synchronous, zero latency ──\n if (client.isBlocked(ip)) {\n const method_ = (req[\"method\"] as string | undefined)?.toUpperCase() ?? \"GET\";\n const url_ = (req[\"originalUrl\"] as string | undefined) ?? (req[\"url\"] as string | undefined) ?? \"/\";\n const ua_ = (req[\"headers\"] as Record<string, string | undefined> | undefined)?.[\"user-agent\"] ?? \"\";\n const reason_ = client.blockReason(ip);\n client.reportBlockedHit(ip, { method: method_, url: url_, userAgent: ua_ });\n\n // Emit a tracking event so the community auto-block is visible in the dashboard\n if (reason_?.source === \"community\") {\n client.track(\"http.community_threat_blocked\", {\n ip,\n meta: {\n endpoint: url_,\n method: method_,\n score: reason_.score,\n topAttack: reason_.topAttack,\n source: \"anomira_network\",\n },\n });\n }\n\n const res_ = res as Record<string, unknown>;\n if (typeof res_[\"status\"] === \"function\") {\n (res_[\"status\"] as (c: number) => unknown)(403);\n } else {\n res_[\"statusCode\"] = 403;\n }\n if (typeof res_[\"end\"] === \"function\") (res_[\"end\"] as (b: string) => void)('{\"error\":\"Forbidden\"}');\n return;\n }\n\n const userId = client.config.getUserId(req);\n const method = (req[\"method\"] as string | undefined)?.toUpperCase() ?? \"GET\";\n const url = (req[\"originalUrl\"] as string | undefined) ?? (req[\"url\"] as string | undefined) ?? \"/\";\n\n const headers = req[\"headers\"] as Record<string, string | string[] | undefined> | undefined;\n const headersStr = headers as Record<string, string | undefined> | undefined;\n const ua = (typeof headers?.[\"user-agent\"] === \"string\" ? headers[\"user-agent\"] : \"\") ?? \"\";\n const agentInfo = detectAgent(headers ?? {});\n const fingerprint = computeBrowserFingerprint(headers ?? {}, ua);\n const h2settings = extractHttp2Settings(req);\n const upstreamTls = extractUpstreamTls(headers ?? {});\n\n // ── Canary token detection ─────────────────────────────────────────────────\n // Scan the Authorization header for canary JWTs (JWT `jti` claim contains\n // the canary token). If found, fire a CRITICAL canary_triggered event —\n // this means the attacker obtained a credential from our honeypot and is\n // now using it against the real API.\n if (client[\"canaryTokenCache\"] && (client[\"canaryTokenCache\"] as Set<string>).size > 0) {\n const authHeader = (typeof headers?.[\"authorization\"] === \"string\" ? headers[\"authorization\"] : \"\") ?? \"\";\n if (authHeader.startsWith(\"Bearer \")) {\n const bearerToken = authHeader.slice(7);\n // Check if the bearer token itself IS a canary token\n if ((client[\"canaryTokenCache\"] as Set<string>).has(bearerToken)) {\n client.track(\"http.canary.triggered\", {\n ip, userId,\n meta: { endpoint: url, method, token: bearerToken.slice(0, 16), source: \"authorization_header\" },\n });\n }\n // Also decode JWT and check jti/kid against canary cache\n try {\n const parts = bearerToken.split(\".\");\n if (parts.length === 3) {\n const b64h = (parts[0] ?? \"\").replace(/-/g, \"+\").replace(/_/g, \"/\");\n const hdr = JSON.parse(Buffer.from(b64h + \"==\", \"base64\").toString()) as Record<string, unknown>;\n const b64p = (parts[1] ?? \"\").replace(/-/g, \"+\").replace(/_/g, \"/\");\n const pay = JSON.parse(Buffer.from(b64p + \"==\", \"base64\").toString()) as Record<string, unknown>;\n const jti = (pay[\"jti\"] as string | undefined) ?? \"\";\n // jti is the raw canary token hex (32 chars) — check directly against the cache\n const cache = client[\"canaryTokenCache\"] as Set<string>;\n if (jti && cache.has(jti)) {\n client.track(\"http.canary.triggered\", {\n ip, userId,\n meta: { endpoint: url, method, token: jti.slice(0, 16), source: \"canary_jwt\" },\n });\n }\n }\n } catch { /* not a valid JWT */ }\n }\n }\n\n // ── Enhanced honeypot interception ─────────────────────────────────────────\n // If this request matches a registered honeypot path, return realistic fake\n // content INSTEAD of forwarding to the customer's route handlers.\n // This is done BEFORE next() so the customer's routes are never involved.\n const urlPath = url.split(\"?\")[0]?.toLowerCase() ?? url.toLowerCase();\n const isHoneypot = (client[\"honeypotPaths\"] as Set<string>).size > 0 &&\n [...(client[\"honeypotPaths\"] as Set<string>)].some((hp) =>\n urlPath === hp || urlPath.startsWith(hp + \"/\") || urlPath.startsWith(hp + \".\")\n );\n\n if (isHoneypot) {\n const honeypotType = detectHoneypotType(url);\n const callbackBase = client.config.ingestUrl.replace(/\\/v1\\/events$/, \"\");\n const orgId = \"\";\n\n // ── POST to admin portal: capture credential attempt ───────────────────\n // When an attacker fills in the fake login form and submits, we capture\n // what credentials they tried (username + password hint) as threat intel.\n // We store the username in full (useful to know which accounts are targeted)\n // and the password length + first 2 chars so we can identify patterns\n // (e.g., \"admin\", \"password123\", empty) without storing the full credential.\n if (method === \"POST\" && honeypotType === \"admin_portal\") {\n const body_ = req[\"body\"] as Record<string, unknown> | undefined;\n const username = String(\n body_?.[\"username\"] ?? body_?.[\"email\"] ?? body_?.[\"user\"] ?? body_?.[\"login\"] ?? \"\"\n );\n const password = String(\n body_?.[\"password\"] ?? body_?.[\"pass\"] ?? body_?.[\"pwd\"] ?? body_?.[\"passwd\"] ?? \"\"\n );\n\n if (username || password) {\n // Password hint: first 2 chars + \"***\" + length — enough to spot patterns\n const passwordHint = password.length > 0\n ? `${password.slice(0, 2)}***[${password.length}]`\n : \"(empty)\";\n\n client.track(\"http.honeypot.credential_attempt\", {\n ip, userId,\n meta: {\n endpoint: url,\n method,\n userAgent: ua,\n honeypotType,\n username,\n passwordHint,\n // Hidden _token field — if this is the canary token we issued\n // in the GET response, we know this is the same attacker session\n formToken: String(body_?.[\"_token\"] ?? \"\"),\n },\n });\n }\n\n // Return \"invalid credentials\" response — lures further attempts\n const failed = makeAdminLoginFailed();\n sendFakeResponse(res, failed);\n return;\n }\n\n // ── GET or other method: serve the fake content ────────────────────────\n const canaryToken = randomBytes(16).toString(\"hex\");\n const fakeResponse = generateHoneypotResponse(honeypotType, canaryToken, callbackBase, orgId);\n\n client.track(\"http.honeypot.hit\", {\n ip, userId,\n meta: {\n endpoint: url,\n method,\n userAgent: ua,\n honeypotType,\n canaryToken,\n responseType: \"enhanced\",\n },\n });\n\n sendFakeResponse(res, fakeResponse);\n return;\n }\n\n // ── Firewall rule evaluation ──\n const fwMatch = client.matchFirewallRule({ url, body: req[\"body\"], headers: headersStr ?? {}, ip });\n if (fwMatch) {\n client.track(\"http.firewall.\" + fwMatch.rule.action, {\n ip, userId,\n meta: { url, method, ruleId: fwMatch.rule.id, attackType: fwMatch.rule.attackType },\n });\n if (fwMatch.rule.action === \"block\") {\n const res_ = res as Record<string, unknown>;\n if (typeof res_[\"status\"] === \"function\") (res_[\"status\"] as (c: number) => unknown)(403);\n else res_[\"statusCode\"] = 403;\n if (typeof res_[\"end\"] === \"function\") (res_[\"end\"] as (b: string) => void)('{\"error\":\"Blocked by firewall rule\"}');\n return;\n }\n }\n\n // ── Detect path traversal before the request reaches the handler ──\n if (client.config.detect.pathTraversal && (url.includes(\"../\") || url.includes(\"..%2F\"))) {\n client.track(EventName.PATH_TRAVERSAL, { ip, userId, meta: { url, method } });\n }\n\n // ── Detect XSS in body (only on POST/PUT/PATCH) ──\n if (client.config.detect.xss && [\"POST\", \"PUT\", \"PATCH\"].includes(method)) {\n const body = JSON.stringify(req[\"body\"]);\n if (/<script|javascript:|on\\w+=/i.test(body)) {\n client.track(EventName.XSS_DETECTED, { ip, userId, meta: { url, method } });\n }\n }\n\n // ── Hook into response finish ──\n const onFinish = () => {\n // Re-read userId here — auth middleware has now run, so req.user is populated.\n // This is the fix for apps that mount Anomira before their auth middleware,\n // which is the standard setup order (block check needs to run first).\n const lateUserId = client.config.getUserId(req);\n\n // Warn once if userId is STILL missing even after the full request cycle.\n // This means neither auto-detection nor auth middleware populated it.\n if (!userIdWarnFired && userIdCheckCount < 20) {\n userIdCheckCount++;\n if (!lateUserId) userIdMissCount++;\n if (userIdCheckCount === 20 && userIdMissCount >= 18) {\n userIdWarnFired = true;\n console.warn(\n \"[Anomira] WARNING: userId not captured on 90%+ of requests.\\n\" +\n \" EWS Evidence Package, geo-velocity, and account takeover detection\\n\" +\n \" silently stop working without it. The SDK tried 5 auto-detection tiers\\n\" +\n \" (Passport / express-jwt, req.auth, direct req.userId, session, JWT Bearer)\\n\" +\n \" — none matched your auth setup.\\n\\n\" +\n \" Fix: pass a getUserId resolver that matches your auth middleware:\\n\" +\n \" new Anomira({ ..., getUserId: (req) => req.user?.id })\"\n );\n }\n }\n\n const status = (res[\"statusCode\"] as number | undefined) ?? 0;\n const latencyMs = Date.now() - startMs;\n const getHeader = (res as Record<string, unknown>)[\"getHeader\"];\n const bytes = typeof getHeader === \"function\"\n ? parseInt((getHeader as (h: string) => string | undefined).call(res, \"content-length\") ?? \"0\", 10) || 0\n : 0;\n\n client.track(EventName.REQUEST, {\n ip, userId: lateUserId,\n meta: {\n method, endpoint: url, status, latencyMs, userAgent: ua, bytes,\n _fp: fingerprint.score,\n _fpSig: fingerprint.signals.join(\",\"),\n ...(fingerprint.knownClient ? { _fpClient: fingerprint.knownClient } : {}),\n ...(h2settings ? {\n _h2Window: h2settings.initialWindowSize,\n _h2HdrTable: h2settings.headerTableSize,\n _h2Score: h2settings.score,\n _h2Signals: h2settings.signals.join(\",\"),\n } : {}),\n ...(upstreamTls.ja3 ? { _ja3: upstreamTls.ja3 } : {}),\n ...(upstreamTls.ja4 ? { _ja4: upstreamTls.ja4 } : {}),\n ...(agentInfo.isAgent ? {\n agentDetected: true,\n agentType: agentInfo.agentType,\n agentName: agentInfo.agentName,\n agentConfidence: agentInfo.confidence,\n mcpSessionId: agentInfo.sessionId,\n isMcp: agentInfo.isMcp,\n agentSignals: agentInfo.signals.join(\",\"),\n } : {}),\n },\n });\n\n if (agentInfo.isAgent) {\n client.track(\"http.agent_detected\", {\n ip, userId: lateUserId,\n meta: {\n endpoint: url,\n method,\n agentType: agentInfo.agentType,\n agentName: agentInfo.agentName,\n agentConfidence: agentInfo.confidence,\n mcpSessionId: agentInfo.sessionId,\n isMcp: agentInfo.isMcp,\n agentSignals: agentInfo.signals.join(\",\"),\n status,\n userAgent: ua,\n },\n });\n }\n\n if (client.config.detect.rateAbuse && status === 429) {\n client.track(EventName.RATE_LIMIT, { ip, userId: lateUserId, meta: { url, method, statusCode: status } });\n }\n\n if (client.config.detect.bruteForce && status === 401) {\n if (/\\/(login|signin|auth|token|session)/i.test(url)) {\n client.track(EventName.LOGIN_FAILED, { ip, userId: lateUserId, meta: { url, method, statusCode: status } });\n }\n }\n\n if (client.config.detect.scanDetection && status === 404) {\n const looksLikeScanner = !ua || /curl|wget|python|go-http|nuclei|sqlmap|nikto/i.test(ua);\n if (looksLikeScanner) {\n client.track(EventName.SCAN_DETECTED, { ip, userId: lateUserId, meta: { url, method, userAgent: ua } });\n }\n }\n\n cleanup();\n };\n\n const cleanup = () => {\n (res as unknown as { off: (e: string, fn: () => void) => void }).off?.(\"finish\", onFinish);\n };\n\n (res as unknown as { on: (e: string, fn: () => void) => void }).on?.(\"finish\", onFinish);\n\n // Run the downstream handler inside a context so console logs get endpoint tagged\n requestContext.run({ endpoint: url, method, ip }, next);\n };\n}\n\nfunction createFastifyPlugin(client: AnomiraClient) {\n let ipCheckCount = 0;\n let ipLoopCount = 0;\n let ipWarnFired = false;\n\n let userIdCheckCount = 0;\n let userIdMissCount = 0;\n let userIdWarnFired = false;\n\n return async function sentinelFastifyPlugin(\n fastify: {\n addHook: (\n event: string,\n fn: (req: Record<string, unknown>, reply: Record<string, unknown>) => void,\n ) => void;\n },\n ) {\n fastify.addHook(\"onRequest\", (req, reply) => {\n const ip = client.config.getIp(req);\n\n if (!ipWarnFired && ipCheckCount < 20) {\n ipCheckCount++;\n if (isLoopbackIp(ip)) ipLoopCount++;\n if (ipCheckCount === 20 && ipLoopCount >= 16) {\n ipWarnFired = true;\n console.warn(\n \"[Anomira] WARNING: client IP not captured on 80%+ of requests.\\n\" +\n \" Your app is likely behind a reverse proxy (Nginx, Cloudflare, AWS ALB)\\n\" +\n \" that is not forwarding client IP headers. Alerts will have no IP attribution.\\n\\n\" +\n \" Nginx fix: proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\\n\" +\n \" proxy_set_header X-Real-IP $remote_addr;\\n\" +\n \" Fastify fix: Fastify({ trustProxy: true })\\n\" +\n \" Docs: https://docs.anomira.io/sdk/ip-capture\"\n );\n }\n }\n const url_ = (req[\"url\"] as string | undefined) ?? \"/\";\n const method_ = (req[\"method\"] as string | undefined)?.toUpperCase() ?? \"GET\";\n const hdrs_ = (req as Record<string, unknown>)[\"headers\"] as Record<string, string | undefined> | undefined;\n\n // ── Block check — synchronous, zero latency ──\n if (client.isBlocked(ip)) {\n const reason_ = client.blockReason(ip);\n client.reportBlockedHit(ip, { method: method_, url: url_, userAgent: hdrs_?.[\"user-agent\"] ?? \"\" });\n\n if (reason_?.source === \"community\") {\n client.track(\"http.community_threat_blocked\", {\n ip,\n meta: {\n endpoint: url_,\n method: method_,\n score: reason_.score,\n topAttack: reason_.topAttack,\n source: \"anomira_network\",\n },\n });\n }\n\n const rep = reply as Record<string, unknown>;\n if (typeof rep[\"code\"] === \"function\") {\n const chained = (rep[\"code\"] as (c: number) => Record<string, unknown>)(403);\n if (chained && typeof chained[\"send\"] === \"function\") {\n (chained[\"send\"] as (b: unknown) => void)({ error: \"Forbidden\" });\n }\n }\n return;\n }\n\n const url = (req[\"url\"] as string | undefined) ?? \"/\";\n const method = (req[\"method\"] as string | undefined)?.toUpperCase() ?? \"GET\";\n const userId = client.config.getUserId(req);\n\n if (client.config.detect.pathTraversal && (url.includes(\"../\") || url.includes(\"..%2F\"))) {\n client.track(EventName.PATH_TRAVERSAL, { ip, userId, meta: { url, method } });\n }\n });\n\n fastify.addHook(\"preHandler\", (req, reply) => {\n const ip = client.config.getIp(req);\n const url = (req[\"url\"] as string | undefined) ?? \"/\";\n const method = (req[\"method\"] as string | undefined)?.toUpperCase() ?? \"GET\";\n const userId = client.config.getUserId(req);\n const headers = (req as Record<string, unknown>)[\"headers\"] as Record<string, string | undefined> | undefined;\n\n // ── Firewall rule evaluation ──\n const fwMatch = client.matchFirewallRule({ url, body: (req as Record<string, unknown>)[\"body\"], headers: headers ?? {}, ip });\n if (fwMatch) {\n client.track(\"http.firewall.\" + fwMatch.rule.action, {\n ip, userId,\n meta: { url, method, ruleId: fwMatch.rule.id, attackType: fwMatch.rule.attackType },\n });\n if (fwMatch.rule.action === \"block\") {\n const rep = reply as Record<string, unknown>;\n if (typeof rep[\"code\"] === \"function\") {\n const chained = (rep[\"code\"] as (c: number) => Record<string, unknown>)(403);\n if (chained && typeof chained[\"send\"] === \"function\") {\n (chained[\"send\"] as (b: unknown) => void)({ error: \"Blocked by firewall rule\" });\n }\n }\n return;\n }\n }\n\n if (client.config.detect.xss && [\"POST\", \"PUT\", \"PATCH\"].includes(method)) {\n const body = JSON.stringify((req as Record<string, unknown>)[\"body\"]);\n if (/<script|javascript:|on\\w+=/i.test(body)) {\n client.track(EventName.XSS_DETECTED, { ip, userId, meta: { url, method } });\n }\n }\n });\n\n fastify.addHook(\"onResponse\", (req, reply) => {\n const ip = client.config.getIp(req);\n const url = (req[\"url\"] as string | undefined) ?? \"/\";\n const method = (req[\"method\"] as string | undefined)?.toUpperCase() ?? \"GET\";\n // onResponse fires after all hooks — auth has run, req.user is populated.\n const userId = client.config.getUserId(req);\n const status = (reply[\"statusCode\"] as number | undefined) ?? 0;\n\n // Warn once if userId is still missing after the full request cycle.\n if (!userIdWarnFired && userIdCheckCount < 20) {\n userIdCheckCount++;\n if (!userId) userIdMissCount++;\n if (userIdCheckCount === 20 && userIdMissCount >= 18) {\n userIdWarnFired = true;\n console.warn(\n \"[Anomira] WARNING: userId not captured on 90%+ of requests.\\n\" +\n \" EWS Evidence Package, geo-velocity, and account takeover detection\\n\" +\n \" silently stop working without it. The SDK tried 5 auto-detection tiers\\n\" +\n \" (Passport / @fastify/jwt, req.auth, direct req.userId, session, JWT Bearer)\\n\" +\n \" — none matched your auth setup.\\n\\n\" +\n \" Fix: pass a getUserId resolver that matches your auth middleware:\\n\" +\n \" new Anomira({ ..., getUserId: (req) => (req as any).user?.id })\"\n );\n }\n }\n const headers = (req as Record<string, unknown>)[\"headers\"] as Record<string, string | string[] | undefined> | undefined;\n const ua = (typeof headers?.[\"user-agent\"] === \"string\" ? headers[\"user-agent\"] : \"\") ?? \"\";\n const latencyMs = (reply as Record<string, unknown>)[\"elapsedTime\"] as number | undefined ?? 0;\n const agentInfo = detectAgent(headers ?? {});\n const fingerprint = computeBrowserFingerprint(headers ?? {}, ua);\n const h2settings = extractHttp2Settings(req as Record<string, unknown>);\n const upstreamTls = extractUpstreamTls(headers ?? {});\n\n // Always emit HTTP access log entry for the Events dashboard.\n client.track(EventName.REQUEST, {\n ip, userId,\n meta: {\n method, endpoint: url, status, latencyMs: Math.round(latencyMs), userAgent: ua, bytes: 0,\n _fp: fingerprint.score,\n _fpSig: fingerprint.signals.join(\",\"),\n ...(fingerprint.knownClient ? { _fpClient: fingerprint.knownClient } : {}),\n ...(h2settings ? {\n _h2Window: h2settings.initialWindowSize,\n _h2HdrTable: h2settings.headerTableSize,\n _h2Score: h2settings.score,\n _h2Signals: h2settings.signals.join(\",\"),\n } : {}),\n ...(upstreamTls.ja3 ? { _ja3: upstreamTls.ja3 } : {}),\n ...(upstreamTls.ja4 ? { _ja4: upstreamTls.ja4 } : {}),\n ...(agentInfo.isAgent ? {\n agentDetected: true,\n agentType: agentInfo.agentType,\n agentName: agentInfo.agentName,\n agentConfidence: agentInfo.confidence,\n mcpSessionId: agentInfo.sessionId,\n isMcp: agentInfo.isMcp,\n agentSignals: agentInfo.signals.join(\",\"),\n } : {}),\n },\n });\n\n if (agentInfo.isAgent) {\n client.track(\"http.agent_detected\", {\n ip, userId,\n meta: {\n endpoint: url,\n method,\n agentType: agentInfo.agentType,\n agentName: agentInfo.agentName,\n agentConfidence: agentInfo.confidence,\n mcpSessionId: agentInfo.sessionId,\n isMcp: agentInfo.isMcp,\n agentSignals: agentInfo.signals.join(\",\"),\n status,\n userAgent: ua,\n },\n });\n }\n\n if (client.config.detect.rateAbuse && status === 429) {\n client.track(EventName.RATE_LIMIT, { ip, userId, meta: { url, method, statusCode: status } });\n }\n\n if (client.config.detect.bruteForce && status === 401 && /\\/(login|signin|auth|token)/i.test(url)) {\n client.track(EventName.LOGIN_FAILED, { ip, userId, meta: { url, method, statusCode: status } });\n }\n\n if (client.config.detect.scanDetection && status === 404) {\n if (!ua || /curl|wget|python|go-http|nuclei|sqlmap|nikto/i.test(ua)) {\n client.track(EventName.SCAN_DETECTED, { ip, userId, meta: { url, method, userAgent: ua } });\n }\n }\n\n if (client.config.detect.bruteForce && status === 200 && /\\/(login|signin|auth|token)/i.test(url)) {\n if (userId) {\n void client.trackLogin({ ip, userId, meta: { url, method } });\n }\n }\n });\n };\n}\n","/**\n * Typed Express middleware — exported separately for projects that want\n * to import the middleware type explicitly.\n *\n * Most users will use `sentinel.express()` instead of importing this directly.\n */\n\nimport type { Request, Response, NextFunction } from \"express\";\nimport type { AnomiraClient } from \"../client.js\";\nimport { EventName } from \"../types.js\";\n\n/**\n * Returns fully-typed Express middleware.\n *\n * ```ts\n * import express from \"express\";\n * import { Anomira } from \"@sentinelapi/node-sdk\";\n *\n * const app = express();\n * const sentinel = new Anomira({ apiKey: \"...\", appId: \"...\" });\n *\n * app.use(sentinel.express()); // auto-instrument all routes\n *\n * // Or import the typed version directly:\n * import { createExpressMiddleware } from \"@sentinelapi/node-sdk/middleware/express\";\n * app.use(createExpressMiddleware(sentinel));\n * ```\n */\nexport function createExpressMiddleware(client: AnomiraClient) {\n return function sentinelMiddleware(req: Request, res: Response, next: NextFunction): void {\n const ip = client.config.getIp(req as unknown);\n const userId = client.config.getUserId(req as unknown);\n const { method, originalUrl: url } = req;\n\n // ── Block check — synchronous in-memory lookup, zero latency ──────────\n if (client.isBlocked(ip)) {\n client.reportBlockedHit(ip, { method, url, userAgent: req.headers[\"user-agent\"] ?? \"\" });\n res.status(403).json({ error: \"Forbidden\" });\n return;\n }\n\n // ── Path traversal check (pre-handler) ────────────────────────────────\n if (client.config.detect.pathTraversal && (url.includes(\"../\") || url.includes(\"..%2F\"))) {\n client.track(EventName.PATH_TRAVERSAL, {\n ip, userId,\n meta: { url, method },\n });\n }\n\n // ── XSS body check (pre-handler) ──────────────────────────────────────\n if (client.config.detect.xss && [\"POST\", \"PUT\", \"PATCH\"].includes(method)) {\n try {\n const body = JSON.stringify(req.body as unknown);\n if (/<script|javascript:|on\\w+=/i.test(body)) {\n client.track(EventName.XSS_DETECTED, {\n ip, userId,\n meta: { url, method },\n });\n }\n } catch {\n // body may not be parseable yet — ignore\n }\n }\n\n // ── Response-finish hook (post-handler) ───────────────────────────────\n const onFinish = () => {\n res.off(\"finish\", onFinish);\n const { statusCode } = res;\n\n if (client.config.detect.rateAbuse && statusCode === 429) {\n client.track(EventName.RATE_LIMIT, {\n ip, userId,\n meta: { url, method, statusCode },\n });\n }\n\n if (client.config.detect.bruteForce && statusCode === 401) {\n if (/\\/(login|signin|auth|token|session)/i.test(url)) {\n client.track(EventName.LOGIN_FAILED, {\n ip, userId,\n meta: { url, method, statusCode },\n });\n }\n }\n\n if (client.config.detect.scanDetection && statusCode === 404) {\n const ua = req.headers[\"user-agent\"] ?? \"\";\n if (!ua || /curl|wget|python|go-http|nuclei|sqlmap|nikto/i.test(ua)) {\n client.track(EventName.SCAN_DETECTED, {\n ip, userId,\n meta: { url, method, userAgent: ua },\n });\n }\n }\n\n // Auto geo-velocity on successful login\n if (\n client.config.detect.geoVelocity &&\n statusCode === 200 &&\n /\\/(login|signin|auth|token)/i.test(url) &&\n method === \"POST\"\n ) {\n // userId may now be available after auth (read from req.user set by auth middleware)\n const resolvedUserId = client.config.getUserId(req as unknown);\n if (resolvedUserId) {\n void client.trackLogin({ ip, userId: resolvedUserId, meta: { url } });\n }\n }\n };\n\n res.on(\"finish\", onFinish);\n next();\n };\n}\n","/**\n * Typed Fastify plugin — exported separately for projects that want\n * to import the plugin type explicitly.\n *\n * Most users will use `sentinel.fastify()` instead of importing this directly.\n */\n\nimport type { FastifyPluginAsync, FastifyRequest, FastifyReply } from \"fastify\";\nimport type { AnomiraClient } from \"../client.js\";\nimport { EventName } from \"../types.js\";\n\n/**\n * Returns a Fastify plugin for auto-instrumentation.\n *\n * ```ts\n * import Fastify from \"fastify\";\n * import { Anomira } from \"@sentinelapi/node-sdk\";\n *\n * const app = Fastify();\n * const sentinel = new Anomira({ apiKey: \"...\", appId: \"...\" });\n *\n * await app.register(sentinel.fastify());\n * ```\n */\nexport function createFastifyPlugin(client: AnomiraClient): FastifyPluginAsync {\n return async function sentinelPlugin(fastify) {\n\n // ── onRequest: block check + path traversal ───────────────────────────\n fastify.addHook(\"onRequest\", async (req: FastifyRequest, reply: FastifyReply) => {\n const ip = client.config.getIp(req);\n const userId = client.config.getUserId(req);\n const url = req.url;\n const method = req.method.toUpperCase();\n\n // ── Block check — synchronous in-memory lookup, zero latency ────────\n if (client.isBlocked(ip)) {\n client.reportBlockedHit(ip, { method, url, userAgent: req.headers[\"user-agent\"] ?? \"\" });\n return reply.code(403).send({ error: \"Forbidden\" });\n }\n\n if (client.config.detect.pathTraversal && (url.includes(\"../\") || url.includes(\"..%2F\"))) {\n client.track(EventName.PATH_TRAVERSAL, { ip, userId, meta: { url, method } });\n }\n });\n\n // ── preHandler: XSS body check ────────────────────────────────────────\n fastify.addHook(\"preHandler\", async (req: FastifyRequest, _reply: FastifyReply) => {\n const ip = client.config.getIp(req);\n const userId = client.config.getUserId(req);\n const url = req.url;\n const method = req.method.toUpperCase();\n\n if (client.config.detect.xss && [\"POST\", \"PUT\", \"PATCH\"].includes(method)) {\n try {\n const body = JSON.stringify(req.body);\n if (/<script|javascript:|on\\w+=/i.test(body)) {\n client.track(EventName.XSS_DETECTED, { ip, userId, meta: { url, method } });\n }\n } catch { /* ignore */ }\n }\n });\n\n // ── onResponse: status-code based detection ───────────────────────────\n fastify.addHook(\"onResponse\", async (req: FastifyRequest, reply: FastifyReply) => {\n const ip = client.config.getIp(req);\n const userId = client.config.getUserId(req);\n const url = req.url;\n const method = req.method.toUpperCase();\n const statusCode = reply.statusCode;\n\n if (client.config.detect.rateAbuse && statusCode === 429) {\n client.track(EventName.RATE_LIMIT, { ip, userId, meta: { url, method, statusCode } });\n }\n\n if (client.config.detect.bruteForce && statusCode === 401) {\n if (/\\/(login|signin|auth|token|session)/i.test(url)) {\n client.track(EventName.LOGIN_FAILED, { ip, userId, meta: { url, method, statusCode } });\n }\n }\n\n if (client.config.detect.scanDetection && statusCode === 404) {\n const ua = req.headers[\"user-agent\"] ?? \"\";\n if (!ua || /curl|wget|python|go-http|nuclei|sqlmap|nikto/i.test(ua)) {\n client.track(EventName.SCAN_DETECTED, { ip, userId, meta: { url, method, userAgent: ua } });\n }\n }\n\n // Auto geo-velocity on successful POST login\n if (\n client.config.detect.geoVelocity &&\n statusCode === 200 &&\n method === \"POST\" &&\n /\\/(login|signin|auth|token)/i.test(url)\n ) {\n const resolvedUserId = client.config.getUserId(req);\n if (resolvedUserId) {\n void client.trackLogin({ ip, userId: resolvedUserId, meta: { url } });\n }\n }\n });\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/buffer.ts","../src/geo-velocity.ts","../src/sensitive.ts","../src/types.ts","../src/agent-detection.ts","../src/behavioral-fingerprint.ts","../src/honeypot-responses.ts","../src/client.ts","../src/middleware/express.ts","../src/middleware/fastify.ts"],"names":["isPrivateIp","randomBytes","createExpressMiddleware","createFastifyPlugin"],"mappings":"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","file":"index.js","sourcesContent":["import type { SdkEvent, IngestPayload } from \"./types.js\";\n\ninterface BufferOptions {\n appId: string;\n apiKey: string;\n ingestUrl: string;\n maxBatchSize: number;\n flushIntervalMs: number;\n maxRetries: number;\n debug: boolean;\n}\n\n/**\n * EventBuffer — collects SDK events and flushes them in batches.\n *\n * Flush is triggered by whichever comes first:\n * • Buffer reaches maxBatchSize events\n * • flushIntervalMs timer fires\n * • process exits (SIGTERM / SIGINT)\n *\n * On flush failure: exponential backoff up to maxRetries.\n * After maxRetries failures: events are dropped with a warning (never crash).\n */\nexport class EventBuffer {\n private queue: SdkEvent[] = [];\n private timer: ReturnType<typeof setInterval> | null = null;\n private flushing = false;\n private readonly opts: BufferOptions;\n\n constructor(opts: BufferOptions) {\n this.opts = opts;\n this.startTimer();\n this.registerShutdownHook();\n }\n\n // ─── Public API ────────────────────────────────────────────────────────────\n\n push(event: SdkEvent): void {\n this.queue.push(event);\n this.log(`[buffer] +1 event → ${this.queue.length} queued (${event.name})`);\n if (this.queue.length >= this.opts.maxBatchSize) {\n this.log(\"[buffer] batch size reached — flushing immediately\");\n void this.flush();\n }\n }\n\n async flush(): Promise<void> {\n if (this.flushing || this.queue.length === 0) return;\n this.flushing = true;\n\n // Drain the queue atomically\n const batch = this.queue.splice(0, this.opts.maxBatchSize);\n this.log(`[buffer] flushing ${batch.length} events`);\n\n try {\n await this.sendWithRetry(batch);\n } catch (err) {\n // Last-resort: log and drop. Never propagate.\n this.warn(`[buffer] dropped ${batch.length} events after max retries:`, err);\n } finally {\n this.flushing = false;\n }\n }\n\n /** Call on graceful shutdown to flush remaining events synchronously. */\n async shutdown(): Promise<void> {\n if (this.timer) { clearInterval(this.timer); this.timer = null; }\n await this.flush();\n this.log(\"[buffer] shutdown complete\");\n }\n\n // ─── Internals ─────────────────────────────────────────────────────────────\n\n private startTimer(): void {\n this.timer = setInterval(() => {\n void this.flush();\n }, this.opts.flushIntervalMs);\n\n // Don't keep the Node process alive just for this timer\n if (this.timer.unref) this.timer.unref();\n }\n\n private registerShutdownHook(): void {\n // Increase the limit to avoid MaxListenersExceededWarning when many buffers\n // are instantiated in the same process (e.g. during tests).\n const current = process.getMaxListeners();\n process.setMaxListeners(current + 3);\n\n const handler = () => { void this.shutdown(); };\n process.once(\"SIGTERM\", handler);\n process.once(\"SIGINT\", handler);\n process.once(\"beforeExit\", handler);\n }\n\n private async sendWithRetry(events: SdkEvent[], attempt = 1): Promise<void> {\n const { appId, apiKey, ingestUrl, maxRetries } = this.opts;\n\n const payload: IngestPayload = { appId, events };\n\n try {\n const res = await fetch(ingestUrl, {\n method: \"POST\",\n redirect: \"manual\",\n headers: {\n Authorization: `Bearer ${apiKey}`,\n \"Content-Type\": \"application/json\",\n \"User-Agent\": `@sentinelapi/node-sdk/0.1.0`,\n },\n body: JSON.stringify(payload),\n signal: AbortSignal.timeout(8_000),\n });\n\n if (res.ok) {\n this.log(`[buffer] ✅ sent ${events.length} events (attempt ${attempt})`);\n return;\n }\n\n if (res.status >= 300 && res.status < 400) {\n this.warn(`[buffer] ❌ Wrong ingest URL — got redirect to ${res.headers.get(\"location\")}. Check SENTINEL_INGEST_URL.`);\n return;\n }\n\n // 4xx errors are not retryable (bad API key, schema error)\n if (res.status === 401) {\n this.warn(\"[buffer] ❌ Invalid API key — check your SENTINEL_API_KEY environment variable\");\n return;\n }\n if (res.status === 403) {\n this.warn(\"[buffer] ❌ App not found — check your SENTINEL_APP_ID environment variable\");\n return;\n }\n if (res.status >= 400 && res.status < 500) {\n this.warn(`[buffer] ⚠️ ingest rejected ${res.status} — dropping batch`);\n return;\n }\n\n throw new Error(`Ingest HTTP ${res.status}`);\n } catch (err) {\n if (attempt >= maxRetries) throw err;\n\n const delayMs = Math.min(1000 * 2 ** (attempt - 1), 16_000); // 1s, 2s, 4s, …, 16s cap\n this.log(`[buffer] retry ${attempt}/${maxRetries} in ${delayMs}ms`);\n await sleep(delayMs);\n return this.sendWithRetry(events, attempt + 1);\n }\n }\n\n private log(...args: unknown[]): void {\n if (this.opts.debug) console.log(\"[Anomira]\", ...args);\n }\n\n private warn(...args: unknown[]): void {\n console.warn(\"[Anomira]\", ...args);\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n","/**\n * Geo-velocity detector\n * ─────────────────────\n * Tracks the last known IP location per userId.\n * On each successful login, checks if the new login would require\n * physically impossible travel from the previous login location.\n *\n * Geo-lookup is delegated to the Anomira ingest service (server-side MaxMind)\n * via the configurable `geoLookupUrl`. Falls back gracefully if unreachable.\n *\n * Threshold: 900 km/h (commercial jet cruising speed).\n */\n\nexport interface GeoPoint {\n lat: number;\n lng: number;\n ip: string;\n tsMs: number;\n city?: string;\n country?: string;\n}\n\nexport interface GeoVelocityResult {\n isImpossible: boolean;\n distanceKm: number;\n speedKmH: number;\n from: GeoPoint;\n to: GeoPoint;\n}\n\n// In-memory last-login state per userId\nconst lastSeen = new Map<string, GeoPoint>();\n\n// Geo-lookup response cache: IP → GeoPoint (1h TTL)\nconst geoCache = new Map<string, { point: Omit<GeoPoint, \"tsMs\">; expiresAt: number }>();\n\nconst MAX_SPEED_KMH = 900;\n\nconst PRIVATE_RANGES = [\n /^10\\./,\n /^172\\.(1[6-9]|2\\d|3[01])\\./,\n /^192\\.168\\./,\n /^127\\./,\n /^::1$/,\n /^0\\.0\\.0\\.0$/,\n /^fc00:/i,\n /^fe80:/i,\n];\n\nfunction isPrivateIp(ip: string): boolean {\n return PRIVATE_RANGES.some((r) => r.test(ip));\n}\n\n/**\n * Lookup geo coordinates for an IP via the Anomira geo endpoint.\n * The ingest service uses MaxMind (no rate limits).\n * Returns null for private/loopback IPs or on any network failure.\n */\nasync function lookupGeo(\n ip: string,\n lookupUrl?: string,\n): Promise<Omit<GeoPoint, \"tsMs\"> | null> {\n if (!ip || isPrivateIp(ip)) return null;\n\n const cached = geoCache.get(ip);\n if (cached && cached.expiresAt > Date.now()) return cached.point;\n\n // Use the Anomira geo endpoint (MaxMind-backed, no rate limits)\n // Falls back to null on any failure — never crashes the SDK\n const url = lookupUrl ? `${lookupUrl}?ip=${encodeURIComponent(ip)}` : null;\n if (!url) return null;\n\n try {\n const res = await fetch(url, { signal: AbortSignal.timeout(2_000) });\n if (!res.ok) return null;\n\n const data = await res.json() as {\n lat?: number; lng?: number; country?: string; city?: string;\n };\n\n if (data.lat == null || data.lng == null) return null;\n\n const point: Omit<GeoPoint, \"tsMs\"> = {\n ip,\n lat: data.lat,\n lng: data.lng,\n country: data.country,\n city: data.city,\n };\n\n geoCache.set(ip, { point, expiresAt: Date.now() + 3_600_000 });\n return point;\n } catch {\n return null;\n }\n}\n\n/** Haversine formula — great-circle distance in km */\nfunction haversineKm(lat1: number, lng1: number, lat2: number, lng2: number): number {\n const R = 6371;\n const dLat = toRad(lat2 - lat1);\n const dLng = toRad(lng2 - lng1);\n const a =\n Math.sin(dLat / 2) ** 2 +\n Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) * Math.sin(dLng / 2) ** 2;\n return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));\n}\n\nfunction toRad(deg: number): number { return (deg * Math.PI) / 180; }\n\n/**\n * Check for impossible travel on a successful login event.\n *\n * @param userId - The authenticated user's ID\n * @param ip - The IP from which they just logged in\n * @param tsMs - Timestamp of this login (ms since epoch)\n * @param lookupUrl - Base URL for geo lookup endpoint (optional)\n */\nexport async function checkGeoVelocity(\n userId: string,\n ip: string,\n tsMs: number,\n lookupUrl?: string,\n): Promise<GeoVelocityResult | null> {\n const geo = await lookupGeo(ip, lookupUrl);\n if (!geo) return null;\n\n const currentPoint: GeoPoint = { ...geo, tsMs };\n const prev = lastSeen.get(userId);\n\n lastSeen.set(userId, currentPoint);\n\n // Evict entries older than 24 hours\n if (lastSeen.size > 10_000) {\n const cutoff = Date.now() - 86_400_000;\n for (const [key, val] of lastSeen) {\n if (val.tsMs < cutoff) lastSeen.delete(key);\n }\n }\n\n if (!prev) return null;\n if (prev.ip === ip) return null;\n\n const distanceKm = haversineKm(prev.lat, prev.lng, currentPoint.lat, currentPoint.lng);\n const hours = Math.max((tsMs - prev.tsMs) / 3_600_000, 0.001);\n const speedKmH = distanceKm / hours;\n\n if (speedKmH < MAX_SPEED_KMH) return null;\n\n return {\n isImpossible: true,\n distanceKm: Math.round(distanceKm),\n speedKmH: Math.round(speedKmH),\n from: prev,\n to: currentPoint,\n };\n}\n\nexport function resetGeoState(): void {\n lastSeen.clear();\n geoCache.clear();\n}\n","// ─── Credential / PII leak scanner ───────────────────────────────────────────\n// Scans log messages and source files for sensitive data patterns.\n// Nigeria-aware: includes BVN/NIN (11-digit), Nigerian phone numbers, and\n// fintech credential patterns alongside universal secret formats.\n\nexport interface LeakMatch {\n type: string;\n label: string;\n}\n\ninterface Pattern {\n type: string;\n label: string;\n regex: RegExp;\n}\n\nconst PATTERNS: Pattern[] = [\n\n // ── Cryptographic private keys ────────────────────────────────────────────\n // NOTE: -----BEGIN CERTIFICATE----- is intentionally excluded — public\n // certificates are designed to be public and committing them is correct.\n // Only PRIVATE keys are dangerous.\n {\n type: \"private_key\",\n label: \"Private Key\",\n regex: /-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/,\n },\n\n // ── Cloud provider credentials ────────────────────────────────────────────\n {\n // AWS access key ID — highly specific, almost no false positives\n type: \"aws_key\",\n label: \"AWS Access Key\",\n regex: /\\bAKIA[0-9A-Z]{16}\\b/,\n },\n {\n // AWS secret access key — 40-char base64 string after keyword\n type: \"aws_secret\",\n label: \"AWS Secret Key\",\n regex: /\\b(?:aws[_-]?secret|AWS_SECRET_ACCESS_KEY)\\s*[:=]\\s*[A-Za-z0-9+/]{40}\\b/i,\n },\n {\n // Google API key\n type: \"google_key\",\n label: \"Google API Key\",\n regex: /\\bAIza[0-9A-Za-z\\-_]{35}\\b/,\n },\n {\n // Google OAuth client secret\n type: \"google_oauth\",\n label: \"Google OAuth Secret\",\n regex: /\\bGOCSP[A-Za-z0-9\\-_]{28}\\b/,\n },\n {\n // Firebase server key\n type: \"firebase_key\",\n label: \"Firebase Server Key\",\n regex: /\\bAAAA[A-Za-z0-9_-]{7}:[A-Za-z0-9_-]{140}\\b/,\n },\n {\n // Azure storage/connection string\n type: \"azure_key\",\n label: \"Azure Key\",\n regex: /\\bDefaultEndpointsProtocol=https;AccountName=[^;]+;AccountKey=[A-Za-z0-9+/=]{88}/,\n },\n\n // ── Source control & CI tokens ────────────────────────────────────────────\n {\n // GitHub personal access tokens (classic and fine-grained)\n type: \"github_token\",\n label: \"GitHub Token\",\n regex: /\\b(?:ghp|gho|ghu|ghs|ghr|github_pat)_[A-Za-z0-9_]{36,255}\\b/,\n },\n {\n // GitLab personal/project/group tokens\n type: \"gitlab_token\",\n label: \"GitLab Token\",\n regex: /\\bglpat-[A-Za-z0-9\\-_]{20}\\b/,\n },\n {\n // NPM access tokens\n type: \"npm_token\",\n label: \"NPM Token\",\n regex: /\\bnpm_[A-Za-z0-9]{36}\\b/,\n },\n\n // ── Payment providers ─────────────────────────────────────────────────────\n {\n // Stripe — secret, restricted, webhook keys\n type: \"stripe_key\",\n label: \"Stripe Key\",\n regex: /\\b(?:sk|rk|whsec)_(?:live|test)_[A-Za-z0-9]{24,}\\b/,\n },\n {\n // Paystack secret/public keys\n type: \"paystack_key\",\n label: \"Paystack Key\",\n regex: /\\b(?:sk|pk)_(?:live|test)_[A-Za-z0-9]{40,}\\b/,\n },\n {\n // Card PANs: Visa (4), Mastercard (51-55), Amex (34/37), Discover (6011/65)\n type: \"card_pan\",\n label: \"Card PAN\",\n regex: /\\b(?:4[0-9]{15}|5[1-5][0-9]{14}|3[47][0-9]{13}|6(?:011|5[0-9]{2})[0-9]{12})\\b/,\n },\n\n // ── Communication & messaging ─────────────────────────────────────────────\n {\n // Slack bot/user/app tokens\n type: \"slack_token\",\n label: \"Slack Token\",\n regex: /\\bxox[baprs]-[0-9A-Za-z]{10,48}\\b/,\n },\n {\n // Slack webhook URL\n type: \"slack_webhook\",\n label: \"Slack Webhook URL\",\n regex: /https:\\/\\/hooks\\.slack\\.com\\/services\\/T[A-Z0-9]+\\/B[A-Z0-9]+\\/[A-Za-z0-9]+/,\n },\n {\n // Twilio account SID and auth token\n type: \"twilio\",\n label: \"Twilio Credential\",\n regex: /\\bAC[a-z0-9]{32}\\b|\\bSK[a-z0-9]{32}\\b/,\n },\n {\n // SendGrid / Brevo / Mailgun API keys\n type: \"email_provider_key\",\n label: \"Email Provider API Key\",\n regex: /\\bSG\\.[A-Za-z0-9._-]{66}\\b|\\bkey-[0-9a-zA-Z]{32}\\b/,\n },\n\n // ── Database connection strings with embedded credentials ─────────────────\n {\n type: \"db_connection\",\n label: \"Database Connection String\",\n regex: /(?:postgresql|postgres|mysql|mongodb(?:\\+srv)?|redis|amqp(?:s)?):\\/\\/[^:]+:[^@\\s]{3,}@/i,\n },\n\n // ── Auth tokens ───────────────────────────────────────────────────────────\n {\n // JWT — three base64url segments separated by dots\n type: \"jwt\",\n label: \"JWT Token\",\n regex: /eyJ[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]{10,}/,\n },\n {\n // Generic API key / token — only flag QUOTED literals, not variable/env references.\n // Skips: api_key = process.env.KEY, token = myVar\n // Matches: api_key = \"sk-abc123...\", bearer: \"eyJhb...\"\n type: \"api_key\",\n label: \"API Key / Token\",\n regex: /\\b(?:api[_-]?key|access[_-]?token|auth[_-]?token|bearer|client[_-]?secret)\\s*[:=]\\s*[\"'][A-Za-z0-9_.\\/+\\-]{20,}[\"']/i,\n },\n\n // ── Password fields ───────────────────────────────────────────────────────\n {\n // Only flag QUOTED string literals after a password/secret keyword.\n // Skips: process.env.*, variable references, undefined/null, template literals.\n // Matches: password: \"hunter2\", secret: 'abc123def', pass=\"hardcoded!\"\n type: \"password\",\n label: \"Hardcoded Password\",\n regex: /\\b(?:password|passwd|pwd|pass|secret|credentials?)\\s*[:=]\\s*[\"'][^\"'$\\s]{6,}[\"']/i,\n },\n\n // ── Nigeria-specific PII ──────────────────────────────────────────────────\n {\n // BVN / NIN: 11 digits, first digit 1-9 (not a phone number starting with 0)\n // Exclude: inside URLs (preceded by / : @ - %), hex strings (followed by a-f),\n // and UUIDs/hashes (surrounded by alphanumeric chars)\n type: \"bvn\",\n label: \"BVN / NIN (11-digit identifier)\",\n regex: /(?<![/\\-:@%=a-fA-F\\w])[1-9]\\d{10}(?![a-fA-F\\d])/,\n },\n {\n // Nigerian phone numbers: 080x, 081x, 070x, 090x, 091x — or with +234 prefix\n type: \"ng_phone\",\n label: \"Nigerian Phone Number\",\n regex: /\\b(?:\\+?234|0)(?:7[0-9]|8[0-1]|9[0-1])\\d{8}\\b/,\n },\n\n // ── PII in credential context ─────────────────────────────────────────────\n {\n // Email only flagged when adjacent to a password/credential keyword\n // Prevents false positives on normal email references in code\n type: \"email_credential\",\n label: \"Email + Password Combo\",\n regex: /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}\\s+(?:password|passwd|pwd|secret|pass)\\s*[:=]?\\s*\\S+/i,\n },\n];\n\n// Returns which sensitive patterns matched, deduplicated by type.\nexport function scanForLeaks(message: string): LeakMatch[] {\n const seen = new Set<string>();\n const found: LeakMatch[] = [];\n for (const p of PATTERNS) {\n p.regex.lastIndex = 0;\n if (p.regex.test(message) && !seen.has(p.type)) {\n seen.add(p.type);\n found.push({ type: p.type, label: p.label });\n }\n }\n return found;\n}\n\n// Returns a copy of the message with sensitive values partially masked.\nexport function redactMessage(message: string): string {\n return message\n // Private keys\n .replace(/-----BEGIN (?:[A-Z ]+ )?PRIVATE KEY-----[\\s\\S]+?-----END (?:[A-Z ]+ )?PRIVATE KEY-----/g,\n \"[PRIVATE KEY REDACTED]\")\n // AWS access key\n .replace(/\\bAKIA[0-9A-Z]{16}\\b/g, \"AKIA••••••••••••••••\")\n // Google API key\n .replace(/\\bAIza[0-9A-Za-z\\-_]{35}\\b/g, \"AIza••••••••••••••••••••••••••••••••\")\n // GitHub tokens\n .replace(/\\b(ghp|gho|ghu|ghs|ghr|github_pat)_[A-Za-z0-9_]{4}[A-Za-z0-9_]{32,251}\\b/g,\n (_, prefix) => `${prefix}_••••[REDACTED]`)\n // Stripe keys\n .replace(/\\b((?:sk|rk|whsec)_(?:live|test)_)[A-Za-z0-9]{4}[A-Za-z0-9]{20,}/g,\n (_, prefix) => `${prefix}••••[REDACTED]`)\n // JWT tokens\n .replace(/eyJ[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]{10,}/g, \"[JWT REDACTED]\")\n // Database URLs — redact password part\n .replace(/((?:postgresql|postgres|mysql|mongodb(?:\\+srv)?|redis):\\/\\/[^:]+:)[^@\\s]{3,}(@)/gi,\n \"$1••••••••$2\")\n // API keys after keyword\n .replace(/(\\b(?:api[_-]?key|access[_-]?token|auth[_-]?token|bearer|client[_-]?secret)\\s*[:=]\\s*[\"']?)([A-Za-z0-9_.\\/+\\-]{4})([A-Za-z0-9_.\\/+\\-]{16,})[\"']?/gi,\n (_, prefix, first4) => `${prefix}${first4}••••[REDACTED]`)\n // Passwords after keyword\n .replace(/(\\b(?:passwd|pwd|pass|secret|credentials?|password)\\s*[:=]\\s*[\"']?)(\\S{2})(\\S{4,})/gi,\n (_, prefix, first2) => `${prefix}${first2}••••••`)\n // Card PANs — show first 6 + last 4\n .replace(/\\b(4[0-9]{5}|5[1-5][0-9]{4}|3[47][0-9]{4}|6(?:011|5[0-9]{2})[0-9]{1})[0-9]{6,9}([0-9]{4})\\b/g,\n (_, first6, last4) => `${first6}••••••${last4}`)\n // BVN/NIN — show first 2 + last 2\n .replace(/(?<!\\d)([1-9]\\d)(\\d{7})(\\d{2})(?!\\d)/g, (_, f2, _mid, l2) => `${f2}•••••••${l2}`)\n // Nigerian phone numbers\n .replace(/\\b((?:\\+?234|0)(?:7[0-9]|8[0-1]|9[0-1])\\d{2})\\d{6}\\b/g, \"$1••••••\");\n}\n","// ─── SDK configuration ────────────────────────────────────────────────────────\n\nexport interface AnomiraConfig {\n /** SDK API key — get this from the Anomira dashboard */\n apiKey: string;\n\n /** Your app ID from the Anomira dashboard */\n appId: string;\n\n /**\n * Ingest endpoint URL.\n * @default \"https://ingest.anomira.io/v1/events\"\n */\n ingestUrl?: string;\n\n /**\n * Geo-lookup endpoint URL for client-side geo-velocity checks.\n * Should point to your Anomira ingest service geo endpoint.\n * If not provided, client-side geo-velocity is skipped (server-side still runs).\n * @example \"https://ingest.anomira.io/v1/geo\"\n */\n geoLookupUrl?: string;\n\n /**\n * Max events to buffer before forcing a flush.\n * @default 100\n */\n maxBatchSize?: number;\n\n /**\n * Max milliseconds to hold events before flushing.\n * @default 5000\n */\n flushIntervalMs?: number;\n\n /**\n * Max retry attempts on ingest failure.\n * @default 3\n */\n maxRetries?: number;\n\n /**\n * If true, log SDK activity to console (useful during integration).\n * @default false\n */\n debug?: boolean;\n\n /**\n * If true, automatically intercept console.log/info/warn/error/debug\n * and forward them to the Anomira Logs dashboard.\n * Existing console output is preserved — logs still print to your terminal.\n * @default false\n */\n captureConsole?: boolean;\n\n /**\n * Service name tag applied to all captured console logs.\n * @default \"app\"\n */\n service?: string;\n\n /**\n * Auto-detection features to enable in the middleware.\n * All default to true.\n */\n detect?: {\n /** Flag repeated login failures from the same IP as brute_force */\n bruteForce?: boolean;\n /** Flag 429 responses as rate_abuse */\n rateAbuse?: boolean;\n /** Flag path traversal patterns in the URL */\n pathTraversal?: boolean;\n /** Flag XSS patterns in request body */\n xss?: boolean;\n /** Flag suspicious scan patterns (many 404s) */\n scanDetection?: boolean;\n /** Detect impossible travel between login events */\n geoVelocity?: boolean;\n };\n\n /**\n * Custom function to extract userId from the request.\n * Default: reads req.user?.id || req.user?.userId || req.user?.sub\n */\n getUserId?: (req: unknown) => string | undefined;\n\n /**\n * Custom function to extract the client IP from the request.\n * Default: reads X-Forwarded-For → X-Real-IP → socket.remoteAddress\n */\n getIp?: (req: unknown) => string;\n\n /**\n * Invisible security — automatic blocking from Anomira's community\n * threat intelligence network.\n *\n * When enabled (the default), the SDK periodically fetches high-confidence\n * attacker IPs from the Anomira network — IPs that have been seen attacking\n * multiple customers — and blocks them automatically, without any manual\n * decision required.\n *\n * This is the \"invisible\" part: your API is protected from known bad actors\n * the moment they appear in the network, before they even reach your handlers.\n *\n * Set `enabled: false` to disable auto-blocking and use threat data for\n * alerting only (the data is still fetched and logged).\n */\n autoBlock?: {\n /**\n * Whether to automatically block IPs from the community threat network.\n * @default true\n */\n enabled?: boolean;\n\n /**\n * Minimum community confidence score (0-100) to trigger an auto-block.\n * Only IPs above this threshold are blocked automatically.\n *\n * @default 85\n *\n * Guidance:\n * 85 (default) — confirmed malicious across multiple customers, very low false-positive risk\n * 70 — broader coverage, slightly higher false-positive risk\n * 95 — maximum precision, only the most confirmed threats\n *\n * Do not set below 60 — scores below that reflect limited data and carry\n * meaningful false-positive risk.\n */\n communityThreshold?: number;\n };\n}\n\n// ─── SDK event ────────────────────────────────────────────────────────────────\n\nexport interface SdkEvent {\n name: string;\n ts: number;\n ip: string;\n userId?: string;\n meta?: Record<string, unknown>;\n}\n\n// ─── Internal buffer event ────────────────────────────────────────────────────\n\nexport interface BufferedEvent extends SdkEvent {\n _retries: number;\n}\n\n// ─── Ingest request body ─────────────────────────────────────────────────────\n\nexport interface IngestPayload {\n appId: string;\n events: SdkEvent[];\n}\n\n// ─── Well-known event names ───────────────────────────────────────────────────\n\nexport const EventName = {\n // Auth\n LOGIN_SUCCESS: \"auth.login.success\",\n LOGIN_FAILED: \"auth.login.failed\",\n LOGOUT: \"auth.logout\",\n OTP_FAILED: \"auth.otp.failed\",\n OTP_SUCCESS: \"auth.otp.success\",\n BVN_LOOKUP: \"auth.bvn.lookup\",\n NIN_LOOKUP: \"auth.nin.lookup\", // NIN enumeration detection\n GEO_VELOCITY: \"auth.login.geo_velocity\",\n CREDENTIAL_STUFF: \"auth.credential.stuffing\",\n SIM_SWAP: \"auth.sim_swap.suspected\", // SIM swap fraud signal\n PHONE_AUTH: \"auth.phone.verified\", // Phone-based auth (OTP/2FA via phone)\n\n // HTTP layer (auto-detected by middleware)\n REQUEST: \"http.request\", // every request — feeds the Events dashboard\n RATE_LIMIT: \"http.ratelimit.exceeded\",\n XSS_DETECTED: \"http.xss.detected\",\n PATH_TRAVERSAL: \"http.path.traversal\",\n SCAN_DETECTED: \"http.scan.detected\",\n IDOR_ATTEMPT: \"user.idor.attempt\",\n SQL_ERROR: \"db.sql.error\",\n\n // Firewall (emitted when a custom request filtering rule fires)\n FIREWALL_BLOCK: \"http.firewall.block\",\n FIREWALL_FLAG: \"http.firewall.flag\",\n} as const;\n\nexport type EventNameValue = typeof EventName[keyof typeof EventName];\n\n// ─── Firewall rule (synced from ingest, cached in SDK) ────────────────────────\n\nexport type FirewallField = \"url\" | \"body\" | \"header\" | \"user_agent\" | \"ip\";\nexport type FirewallOperator = \"contains\" | \"equals\" | \"starts_with\" | \"ends_with\" | \"regex\";\nexport type FirewallAction = \"block\" | \"flag\";\n\n// ─── Declared endpoint ────────────────────────────────────────────────────────\n\nexport interface EndpointDeclaration {\n /** HTTP method, e.g. \"GET\". Use \"*\" to match any method. */\n method: string;\n /** Express-style path, e.g. \"/api/users/:id\" — colons are normalized automatically. */\n path: string;\n /** Whether this endpoint requires authentication. Defaults to true. */\n auth?: boolean;\n}\n\n// ─── Firewall rule ────────────────────────────────────────────────────────────\n\nexport interface FirewallRule {\n id: string;\n field: FirewallField;\n headerName?: string | null;\n operator: FirewallOperator;\n value: string;\n action: FirewallAction;\n attackType: string;\n}\n","/**\n * AI Agent & MCP Traffic Detection\n *\n * Identifies requests originating from AI agents, LLM tool-callers, and\n * Model Context Protocol (MCP) clients using only confirmed, primary-source\n * verified signals.\n *\n * Signal sources:\n * - MCP Streamable HTTP spec (2025-03-26):\n * https://modelcontextprotocol.io/specification/2025-03-26/basic/transports\n * - OpenAI bot documentation:\n * https://developers.openai.com/api/docs/bots\n * - Anthropic crawler documentation:\n * https://support.claude.com/en/articles/8896518-does-anthropic-crawl-data\n * - Perplexity bot documentation:\n * https://docs.perplexity.ai/guides/bots\n * - RFC 9421 HTTP Message Signatures (ChatGPT Agent + Google Agent):\n * https://blog.castle.io/how-to-authenticate-openai-operator-requests-using-http-message-signatures/\n */\n\nexport type AgentType =\n | \"openai\" // GPTBot, ChatGPT-User, OAI-SearchBot, ChatGPT Agent\n | \"anthropic\" // ClaudeBot, Claude-User, Claude-SearchBot\n | \"perplexity\" // PerplexityBot, Perplexity-User\n | \"mcp_client\" // Generic MCP client (no provider-specific UA)\n | \"rfc9421_agent\" // Signed agent via RFC 9421 HTTP Message Signatures\n | \"unknown_agent\"; // Has agent signals but no provider identified\n\nexport type AgentConfidence = \"high\" | \"medium\";\n\nexport interface AgentDetectionResult {\n isAgent: boolean;\n confidence: AgentConfidence;\n agentType: AgentType | null;\n /** The matched UA token, e.g. \"GPTBot/1.3\" or \"ClaudeBot/1.0\" */\n agentName: string | null;\n /** Mcp-Session-Id header value when MCP protocol is confirmed */\n sessionId: string | null;\n /** True when Mcp-Session-Id or MCP Accept header is confirmed present */\n isMcp: boolean;\n /** Which specific signals fired — for observability and tuning */\n signals: string[];\n}\n\n// ─── Confirmed user-agent substrings (official documentation only) ─────────────\n//\n// Each entry: [substring_to_match (case-insensitive), agentType, display_name]\n// Only UA patterns confirmed from first-party sources are listed here.\n// Do NOT add patterns based on community aggregators or unverified reports.\nconst CONFIRMED_UA_PATTERNS: [RegExp, AgentType, string][] = [\n // OpenAI — https://developers.openai.com/api/docs/bots\n [/GPTBot\\//i, \"openai\", \"GPTBot\"],\n [/ChatGPT-User\\//i, \"openai\", \"ChatGPT-User\"],\n [/OAI-SearchBot\\//i, \"openai\", \"OAI-SearchBot\"],\n [/OAI-AdsBot\\//i, \"openai\", \"OAI-AdsBot\"],\n\n // Anthropic — https://support.claude.com/en/articles/8896518\n [/ClaudeBot\\//i, \"anthropic\", \"ClaudeBot\"],\n [/Claude-User\\//i, \"anthropic\", \"Claude-User\"],\n [/Claude-SearchBot\\//i, \"anthropic\", \"Claude-SearchBot\"],\n\n // Perplexity — https://docs.perplexity.ai/guides/bots\n [/PerplexityBot\\//i, \"perplexity\", \"PerplexityBot\"],\n [/Perplexity-User\\//i, \"perplexity\", \"Perplexity-User\"],\n];\n\n// ─── MCP Protocol headers (confirmed from MCP spec 2025-03-26) ─────────────────\n//\n// Node.js normalises all incoming HTTP header names to lowercase, so we\n// match the lowercase form here.\nconst MCP_SESSION_HEADER = \"mcp-session-id\"; // Mcp-Session-Id (normalised)\nconst RFC9421_SIG_HEADER = \"signature-agent\"; // Signature-Agent (RFC 9421)\n\n// The MCP spec mandates clients send EXACTLY this Accept header value.\n// Source: spec + confirmed via multiple bug reports about HTTP 406 errors\n// when this exact value is missing.\nconst MCP_ACCEPT_EXACT = \"application/json, text/event-stream\";\n\n// ─── Detection function ─────────────────────────────────────────────────────────\n\n/**\n * Analyse request headers and identify AI agent / MCP client traffic.\n * Accepts a plain header map (as returned by Node.js `req.headers` — all\n * keys lowercase, values are string or string[]).\n */\nexport function detectAgent(\n headers: Record<string, string | string[] | undefined>,\n): AgentDetectionResult {\n const signals: string[] = [];\n let agentType: AgentType | null = null;\n let agentName: string | null = null;\n let sessionId: string | null = null;\n let isMcp = false;\n let confidence: AgentConfidence | null = null;\n\n // ── 1. RFC 9421 Signature-Agent (HIGH confidence) ──────────────────────────\n // Confirmed: OpenAI ChatGPT Agent and Google Agent (Mariner) both send this.\n // Source: https://blog.castle.io/how-to-authenticate-openai-operator-requests\n const sigAgent = headerStr(headers[RFC9421_SIG_HEADER]);\n if (sigAgent) {\n signals.push(\"rfc9421_signature_agent\");\n confidence = \"high\";\n agentType = sigAgent.includes(\"chatgpt.com\") ? \"openai\" : \"rfc9421_agent\";\n agentName = `rfc9421:${sigAgent.slice(0, 60)}`;\n }\n\n // ── 2. MCP Session ID header (HIGH confidence) ─────────────────────────────\n const mcpSession = headerStr(headers[MCP_SESSION_HEADER]);\n if (mcpSession) {\n signals.push(\"mcp_session_id\");\n isMcp = true;\n sessionId = mcpSession;\n confidence = \"high\";\n if (!agentType) agentType = \"mcp_client\";\n }\n\n // ── 3. MCP mandatory Accept header (HIGH confidence when alone) ────────────\n // The exact value \"application/json, text/event-stream\" is mandated by the\n // MCP Streamable HTTP spec. No standard browser sends this exact combination.\n const accept = headerStr(headers[\"accept\"]);\n if (accept === MCP_ACCEPT_EXACT) {\n signals.push(\"mcp_accept_header\");\n isMcp = true;\n if (!confidence) confidence = \"high\";\n if (!agentType) agentType = \"mcp_client\";\n }\n\n // ── 4. Known AI agent User-Agent strings (HIGH confidence) ─────────────────\n const ua = headerStr(headers[\"user-agent\"]) ?? \"\";\n for (const [pattern, type, name] of CONFIRMED_UA_PATTERNS) {\n const match = ua.match(pattern);\n if (match) {\n signals.push(`ua_${name.toLowerCase().replace(/[^a-z0-9]/g, \"_\")}`);\n confidence = \"high\";\n // Extract the full token with version e.g. \"GPTBot/1.3\"\n const tokenStart = ua.indexOf(match[0]);\n const tokenEnd = ua.indexOf(\" \", tokenStart);\n agentName = tokenEnd > -1 ? ua.slice(tokenStart, tokenEnd) : match[0];\n agentType = type;\n break; // First match wins — patterns are mutually exclusive\n }\n }\n\n // ── 5. python-httpx User-Agent (MEDIUM confidence) ─────────────────────────\n // The MCP Python SDK (modelcontextprotocol/python-sdk) uses httpx as its\n // HTTP client and sets no custom User-Agent, resulting in \"python-httpx/<v>\".\n // Source: https://github.com/modelcontextprotocol/python-sdk/issues/1641\n // NOTE: Any Python service using httpx will produce this UA, so we only\n // treat it as a signal when combined with MCP-specific headers above.\n if (!agentType && /^python-httpx\\//i.test(ua) && isMcp) {\n signals.push(\"ua_python_httpx_with_mcp\");\n confidence = \"medium\";\n agentType = \"mcp_client\";\n agentName = ua.split(\" \")[0] ?? \"python-httpx\";\n }\n\n const isAgent = confidence !== null;\n\n return {\n isAgent,\n confidence: confidence ?? \"medium\",\n agentType: isAgent ? (agentType ?? \"unknown_agent\") : null,\n agentName: isAgent ? agentName : null,\n sessionId,\n isMcp,\n signals,\n };\n}\n\n// ─── Helper ─────────────────────────────────────────────────────────────────────\n\nfunction headerStr(v: string | string[] | undefined): string | undefined {\n if (v === undefined) return undefined;\n return Array.isArray(v) ? v[0] : v;\n}\n","/**\n * Behavioral Browser Fingerprinting\n *\n * Computes a 0-100 automation score from HTTP request headers.\n * Higher score = more likely to be a programmatic client (script, agent, bot).\n *\n * Three tiers of signals, all from confirmed primary sources:\n *\n * Tier 1 — Definitive: User-Agent strings that are unambiguously automated.\n * Sources: python-requests source (psf/requests utils.py), httpx test suite\n * (encode/httpx tests/client/test_headers.py), undici issue #1305,\n * curl documentation (everything.curl.dev), aiohttp docs, Go stdlib.\n *\n * Tier 2 — Strong: Absence of browser-only headers.\n * Sources: W3C Fetch Metadata spec (w3.org/TR/fetch-metadata/),\n * MDN Sec-Fetch-Mode, undici issue #1305, SearXNG bot detection source.\n *\n * Tier 3 — Corroborating: Incoherent header combinations.\n * Source: undici issue #1305 (sends Sec-Fetch-Mode without Sec-Fetch-Site),\n * MDN Sec-CH-UA (Chromium only — Firefox/Safari do not send it).\n */\n\nexport interface FingerprintResult {\n /** 0-100. ≥55 = likely automated. 100 = definitive known bot UA. */\n score: number;\n /** Human-readable signals that contributed to the score. */\n signals: string[];\n /** True when the User-Agent matches a confirmed automated client string. */\n isDefiniteBot: boolean;\n /** The specific known client name when isDefiniteBot is true, e.g. \"python-requests\". */\n knownClient: string | null;\n}\n\n/**\n * HTTP/2 SETTINGS values extracted from the client's connection preface.\n * Populated only when the customer's Node.js app handles HTTP/2 directly\n * (no reverse proxy in between for TLS termination).\n *\n * Source for expected values:\n * - Chrome 119+: initialWindowSize=6,291,456 (6 MB), headerTableSize=65,536\n * - python-requests / httpx / curl: initialWindowSize=65,535 (HTTP/2 default)\n * Reference: Akamai Black Hat EU 2017 whitepaper; scrapfly.io HTTP/2 guide\n */\nexport interface Http2SettingsResult {\n /** INITIAL_WINDOW_SIZE from client SETTINGS frame (bytes). */\n initialWindowSize: number;\n /** HEADER_TABLE_SIZE from client SETTINGS frame. */\n headerTableSize: number;\n /** ENABLE_PUSH from client SETTINGS frame. */\n enablePush: boolean;\n /**\n * Automation score contribution (0-20) derived from SETTINGS values.\n * Chrome/modern browsers use ~6 MB window; all Python/curl clients use ~65 KB.\n */\n score: number;\n signals: string[];\n}\n\n/**\n * JA3 or JA4 hash passed by an upstream proxy (customer's Nginx with the\n * ngx_ssl_fingerprint module, or a Cloudflare JA3 Worker).\n * The ingest includes this in event meta for dashboard display and future\n * mismatch-based scoring when a lookup database is available.\n */\nexport interface UpstreamTlsResult {\n ja3: string | null; // X-JA3-Hash header (HanadaLee / phuslu modules)\n ja4: string | null; // X-JA4 header\n}\n\n// ─── Tier 1: Confirmed known-automated User-Agent prefixes/patterns ────────────\n//\n// Each entry is [regex, display_name].\n// Only patterns confirmed from primary sources (source code, official docs).\n// Do NOT add speculative patterns.\nconst KNOWN_BOT_UA: [RegExp, string][] = [\n // Confirmed: psf/requests utils.py (default_headers)\n [/^python-requests\\//i, \"python-requests\"],\n // Confirmed: encode/httpx tests/client/test_headers.py\n [/^python-httpx\\//i, \"python-httpx\"],\n // Confirmed: aiohttp docs (format: \"Python/3.x aiohttp/3.x.x\")\n [/\\baiohttp\\//i, \"aiohttp\"],\n // Confirmed: nodejs/undici issue #1305\n [/^undici$/i, \"undici\"],\n // Confirmed: everything.curl.dev\n [/^curl\\//i, \"curl\"],\n // Confirmed: GNU wget docs\n [/^Wget\\//i, \"wget\"],\n // Confirmed: Go net/http DefaultClient (golang.org/pkg/net/http)\n [/^Go-http-client\\//i, \"Go-http-client\"],\n // Confirmed: Java HttpURLConnection default\n [/^Java\\//i, \"Java\"],\n // Confirmed: Scrapy docs (scrapy.org)\n [/^Scrapy\\//i, \"Scrapy\"],\n // Confirmed: OkHttp (square.github.io/okhttp)\n [/\\bokhttp\\//i, \"okhttp\"],\n // Confirmed: libwww-perl (metacpan.org/pod/LWP)\n [/^libwww-perl\\//i, \"libwww-perl\"],\n // Confirmed: node-fetch (github.com/node-fetch/node-fetch README)\n [/^node-fetch\\//i, \"node-fetch\"],\n // Confirmed: axios docs (axios-http.com/docs/config_defaults)\n [/^axios\\//i, \"axios\"],\n // Confirmed: Ruby net/http default (ruby-doc.org)\n [/^Ruby$/i, \"Ruby\"],\n];\n\n// ─── Tier 1: axios-specific Accept header ─────────────────────────────────────\n//\n// Confirmed from axios documentation: axios sets\n// `Accept: application/json, text/plain, */*` by default.\n// NOTE: this is also sent by axios running as a frontend library inside a real\n// browser (React, Vue, Angular apps). It is only a definitive bot signal when\n// combined with a non-browser User-Agent. With a browser UA, treat it as a\n// corroborating signal (score 15) rather than a definitive match.\nconst AXIOS_ACCEPT = \"application/json, text/plain, */*\";\n\n// Returns true when the UA is clearly a real browser running on the user's OS.\n// Presence of \"Mozilla/5.0\" combined with a known browser engine name is the\n// standard browser UA format. Non-browser clients (server-side axios, curl, etc.)\n// either have their own UA or send no UA at all.\nfunction isBrowserUa(ua: string): boolean {\n return (\n /Mozilla\\/5\\.0/.test(ua) &&\n /(?:Chrome|Firefox|Safari|OPR|Edg(?:e|HTML)?|Trident)\\/[\\d.]+/.test(ua)\n );\n}\n\n// ─── Detection function ────────────────────────────────────────────────────────\n\n/**\n * Compute a browser fingerprint score from a Node.js request's headers.\n *\n * @param headers Node.js normalises incoming headers to lowercase — pass\n * `req.headers` directly.\n * @param ua The User-Agent string (extracted separately for clarity).\n */\nexport function computeBrowserFingerprint(\n headers: Record<string, string | string[] | undefined>,\n ua: string,\n): FingerprintResult {\n const signals: string[] = [];\n let score = 0;\n let isDefiniteBot = false;\n let knownClient: string | null = null;\n\n // ── Tier 1a: Known automated User-Agent ──────────────────────────────────────\n for (const [pattern, name] of KNOWN_BOT_UA) {\n if (pattern.test(ua)) {\n isDefiniteBot = true;\n knownClient = name;\n score = 100;\n signals.push(`known_ua:${name}`);\n break;\n }\n }\n\n // ── Tier 1b: axios Accept header ─────────────────────────────────────────────\n // Definitive ONLY when the UA is not a real browser. If it is a browser UA,\n // this just means the web app uses axios as its frontend HTTP client — that\n // is completely normal (React/Vue/Angular apps all do this). Score it weakly.\n const accept = str(headers[\"accept\"]);\n if (!isDefiniteBot && accept === AXIOS_ACCEPT) {\n if (!isBrowserUa(ua)) {\n isDefiniteBot = true;\n knownClient = \"axios\";\n score = 100;\n signals.push(\"known_accept:axios\");\n } else {\n score += 15;\n signals.push(\"browser_axios_accept\");\n }\n }\n\n // If already definitive, no need to score further\n if (isDefiniteBot) return { score, signals, isDefiniteBot, knownClient };\n\n // ── Tier 2: Absence of browser-mandatory headers ──────────────────────────────\n //\n // Sec-Fetch-Site and Sec-Fetch-Mode are sent on EVERY fetch()/XHR call by\n // Chrome (Chromium), Firefox (since 2023), and most modern browsers.\n // They are W3C \"forbidden\" headers — JavaScript cannot set or modify them.\n // Absence = 100% certainty the caller is not a standard browser fetch call.\n // Source: https://www.w3.org/TR/fetch-metadata/\n const hasSecFetchSite = \"sec-fetch-site\" in headers;\n const hasSecFetchMode = \"sec-fetch-mode\" in headers;\n\n if (!hasSecFetchSite && !hasSecFetchMode) {\n score += 30;\n signals.push(\"missing_sec_fetch\");\n }\n\n // Accept-Language: all browsers send this. python-requests, httpx, curl,\n // aiohttp, and Node.js undici do NOT send it by default.\n // Source: SearXNG bot detection; confirmed by examining default headers of\n // each library above.\n // Exception: undici sends `accept-language: *` — we handle that separately.\n const acceptLang = str(headers[\"accept-language\"]);\n if (!acceptLang) {\n score += 25;\n signals.push(\"missing_accept_language\");\n }\n\n // Accept-Encoding: curl sends NO Accept-Encoding by default (unlike all others).\n // Its absence alone is a strong curl signal. Not a general \"bot\" signal,\n // but useful combined with other indicators.\n const acceptEnc = str(headers[\"accept-encoding\"]);\n if (!acceptEnc) {\n score += 10;\n signals.push(\"missing_accept_encoding\");\n }\n\n // ── Tier 3: Incoherent header combinations ────────────────────────────────────\n //\n // Node.js undici (built-in fetch) sends `sec-fetch-mode: cors` but does NOT\n // send `sec-fetch-site` or `sec-fetch-dest`. Real browsers always send all\n // three together. This pattern is unique to undici.\n // Source: nodejs/undici GitHub issue #1305\n if (hasSecFetchMode && !hasSecFetchSite) {\n score += 20;\n signals.push(\"undici_pattern:sec_fetch_mode_without_site\");\n knownClient = \"undici (Node.js fetch)\";\n }\n\n // undici also sends `accept-language: *` (a bare wildcard) — not a valid\n // BCP 47 language tag, which browsers always send (e.g. \"en-US,en;q=0.9\").\n // Source: nodejs/undici issue #1305\n if (acceptLang === \"*\") {\n score += 15;\n signals.push(\"undici_pattern:accept_language_wildcard\");\n }\n\n // Sec-CH-UA is sent by Chromium browsers on ALL requests (including fetch())\n // without any server opt-in. If the UA claims to be Chrome but Sec-CH-UA\n // is absent, the Chrome UA is likely spoofed (or it's Firefox/Safari, which\n // never send Sec-CH-UA). Source: MDN Sec-CH-UA; Corbado blog.\n const claimsChrome = /Chrome\\//i.test(ua) && !/Chromium/i.test(ua);\n const hasSecChUa = \"sec-ch-ua\" in headers;\n if (claimsChrome && !hasSecChUa) {\n score += 10;\n signals.push(\"chrome_ua_without_sec_ch_ua\");\n }\n\n // Cap at 100\n score = Math.min(100, score);\n\n return { score, signals, isDefiniteBot, knownClient };\n}\n\n// ─── HTTP/2 SETTINGS extraction ───────────────────────────────────────────────\n\n/**\n * Extract HTTP/2 SETTINGS from the Node.js `http2.Http2Session` that handled\n * this request, if available. Works only when the customer's Node.js app is\n * the TLS endpoint (no reverse proxy terminating TLS before Node.js).\n *\n * The function is safe to call on any request — it returns null for HTTP/1.1\n * connections or when the session is not accessible.\n *\n * @param req The raw Node.js IncomingMessage (Express: `req`, Fastify: `req.raw`)\n *\n * Reference values (Akamai Black Hat EU 2017 + scrapfly.io HTTP/2 guide):\n * Chrome 119+ : initialWindowSize = 6,291,456 | headerTableSize = 65,536\n * Firefox : initialWindowSize = 65,535 | headerTableSize = 65,536\n * python libs : initialWindowSize = 65,535 | headerTableSize = 4,096\n * curl : initialWindowSize = 65,535 | headerTableSize = 4,096\n */\nexport function extractHttp2Settings(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n req: Record<string, any>,\n): Http2SettingsResult | null {\n // Access the http2.ServerHttp2Stream → http2.Http2Session → remoteSettings.\n // Express: req.stream (available when using http2.createServer)\n // Fastify: req.raw.stream (same path via req.raw which is the Node.js request)\n const stream = req[\"stream\"] ?? req[\"raw\"]?.[\"stream\"];\n if (!stream) return null;\n\n const session = stream[\"session\"];\n if (!session) return null;\n\n // remoteSettings is the Http2Settings object sent by the CLIENT in its\n // SETTINGS frame during the HTTP/2 connection preface.\n // Node.js docs: https://nodejs.org/api/http2.html#http2sessionremotesettings\n const remote = session[\"remoteSettings\"] as {\n headerTableSize?: number;\n enablePush?: boolean;\n initialWindowSize?: number;\n } | undefined;\n\n if (!remote) return null;\n\n const initialWindowSize = remote.initialWindowSize ?? 65535;\n const headerTableSize = remote.headerTableSize ?? 4096;\n const enablePush = remote.enablePush ?? true;\n\n const signals: string[] = [];\n let score = 0;\n\n // The most discriminating signal: Chrome uses ~6 MB (6,291,456) while ALL\n // Python/curl HTTP/2 clients use the protocol default of 65,535 bytes.\n // Threshold of 131,072 (128 KB) gives a 50× margin above the default —\n // no realistic browser sends a value this low.\n // Source: Akamai Passive Fingerprinting of HTTP/2 Clients (Black Hat EU 2017)\n if (initialWindowSize <= 131_072) {\n score += 20;\n signals.push(`h2_window_${initialWindowSize}`);\n }\n\n // Chrome also uses a much larger HEADER_TABLE_SIZE (65,536 vs default 4,096).\n // Python libs leave this at the default.\n if (headerTableSize <= 4096 && initialWindowSize <= 131_072) {\n score += 5;\n signals.push(\"h2_header_table_default\");\n }\n\n return { initialWindowSize, headerTableSize, enablePush, score, signals };\n}\n\n// ─── Upstream TLS fingerprint headers ─────────────────────────────────────────\n\n/**\n * Read JA3/JA4 hashes injected by an upstream proxy that has TLS fingerprinting\n * enabled. Supports:\n * - HanadaLee/ngx_ssl_fingerprint_module → X-JA3-Hash, X-JA4\n * - phuslu/nginx-ssl-fingerprint → X-JA3-Hash, X-JA4\n * - Custom Nginx configs → X-JA3, X-JA4 (alternate names)\n *\n * These values are included in event meta as `_ja3` / `_ja4` for:\n * a) Display in the AI Agent Sessions dashboard\n * b) Future mismatch-based scoring (UA claims Chrome but JA3 is python-requests)\n *\n * @param headers Node.js lowercase-normalised header map\n */\nexport function extractUpstreamTls(\n headers: Record<string, string | string[] | undefined>,\n): UpstreamTlsResult {\n // Try both common header name conventions\n const ja3 = str(headers[\"x-ja3-hash\"]) || str(headers[\"x-ja3\"]) || null;\n const ja4 = str(headers[\"x-ja4\"]) || null;\n return { ja3: ja3 || null, ja4: ja4 || null };\n}\n\n// ─── Helper ────────────────────────────────────────────────────────────────────\n\nfunction str(v: string | string[] | undefined): string {\n if (v === undefined) return \"\";\n return Array.isArray(v) ? (v[0] ?? \"\") : v;\n}\n","/**\n * Honeypot Response Engine\n * ─────────────────────────\n * Generates convincing fake HTTP responses for honeypot traps.\n *\n * Design principles (based on security research into attacker behaviour):\n *\n * 1. Always return HTTP 200 with realistic content — 403/404 tells the attacker\n * the path exists but is protected; 200 with plausible fake data makes them\n * believe they succeeded and linger long enough to reveal their tooling.\n * Source: Thinkst blog \"Creating REST API Canary Endpoints\" (2022)\n *\n * 2. Every response embeds a unique canary token in a realistic position.\n * The token appears as a legitimate-looking credential value — NOT as a\n * special prefix or obvious marker — so automated scanners do not filter it.\n * Source: Thinkst canarytokens.org documentation\n *\n * 3. Credentials must follow the exact format of real credentials:\n * - AWS Access Key ID: `AKIA` + `I` (pos 5) + 14 base32 chars + `A` = 20 chars\n * Source: AWS docs + awsteele.com/blog/2020/09/26/aws-access-key-format\n * - AWS Secret Key: 40 base64-standard chars (A-Z, a-z, 0-9, +, /)\n * Source: summitroute.com/blog/2018/06/20/aws_security_credential_formats\n * - JWT: valid HS256 signature, `exp` set 24h in the past, `kid` contains\n * the canary token for detection. Source: Auth0 JWT docs + canarytokens.org\n *\n * 4. HTTP headers must match the technology being impersonated — e.g., Apache\n * headers on /.env not Express headers, since attackers cross-reference them.\n *\n * 5. Callback URLs embedded in fake webhook fields point to the Anomira ingest.\n * When an attacker configures their system with the fake webhook and it fires,\n * the ingest records the canary_triggered event immediately.\n * Source: Thinkst \"A Safety Net for AWS Canarytokens\" blog (2022)\n */\n\nimport { randomBytes, createHmac } from \"node:crypto\";\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type HoneypotType =\n | \"env_file\" // .env, .env.production, .env.local\n | \"aws_credentials\" // .aws/credentials\n | \"git_config\" // .git/config\n | \"graphql\" // /graphql with introspection\n | \"spring_actuator\" // /actuator/env (Spring Boot)\n | \"json_config\" // config.json, settings.json, secrets.json\n | \"htpasswd\" // .htpasswd — Apache password file with hashed credentials\n | \"s3_bucket\" // S3-style XML responses for bucket listing paths\n | \"admin_portal\" // HTML admin login form — captures credential attempts\n | \"generic\"; // all other honeypot paths\n\nexport interface HoneypotResponse {\n statusCode: number;\n contentType: string;\n headers: Record<string, string>;\n body: string;\n canaryToken: string; // stored in ingest for subsequent detection\n}\n\n// ─── Path → type detection ────────────────────────────────────────────────────\n\n/**\n * Determine the honeypot type from the request path so the right fake\n * response can be generated.\n */\nexport function detectHoneypotType(path: string): HoneypotType {\n const p = path.toLowerCase().split(\"?\")[0] ?? path.toLowerCase();\n\n if (/\\/\\.env(\\.[\\w]+)?$/.test(p) || p.endsWith(\"/.env\")) return \"env_file\";\n if (p.includes(\".aws/credentials\") || p.includes(\"aws/credentials\")) return \"aws_credentials\";\n if (p.includes(\".git/config\") || p.endsWith(\"/git/config\")) return \"git_config\";\n if (p.includes(\"/graphql\") || p.includes(\"/graphiql\")) return \"graphql\";\n if (p.includes(\"/actuator/env\") || p.includes(\"/actuator/\")) return \"spring_actuator\";\n if (/\\/(config|settings|secrets?)(\\.json)?$/.test(p)) return \"json_config\";\n // .htpasswd — Apache password file\n if (p.endsWith(\"/.htpasswd\") || p.endsWith(\"/.htaccess\")) return \"htpasswd\";\n // S3 bucket listing paths\n if (p.includes(\"/.s3cfg\") || p.endsWith(\"/s3\") || p.includes(\"s3cfg\")) return \"s3_bucket\";\n // Admin portals — match common admin paths\n if (/\\/(admin|wp-admin|administrator|panel|cpanel|dashboard|manage|backend)(\\/|$)/.test(p)) return \"admin_portal\";\n return \"generic\";\n}\n\n// ─── Main generator ───────────────────────────────────────────────────────────\n\n/**\n * Generate a convincing fake HTTP response for a honeypot trap.\n *\n * @param type The type of honeypot (derived from path)\n * @param canaryToken Unique token to embed in the response for later detection\n * @param callbackBase Base URL of the Anomira ingest, used for webhook canaries\n * (e.g. \"https://ingest.anomira.io\")\n * @param orgId Organisation ID — used in callback URLs and canary key generation\n */\nexport function generateHoneypotResponse(\n type: HoneypotType,\n canaryToken: string,\n callbackBase: string,\n orgId: string,\n): HoneypotResponse {\n switch (type) {\n case \"env_file\": return makeEnvFile(canaryToken, callbackBase, orgId);\n case \"aws_credentials\": return makeAwsCredentials(canaryToken);\n case \"git_config\": return makeGitConfig(canaryToken, callbackBase, orgId);\n case \"graphql\": return makeGraphQL();\n case \"spring_actuator\": return makeSpringActuator(canaryToken);\n case \"json_config\": return makeJsonConfig(canaryToken, callbackBase, orgId);\n case \"htpasswd\": return makeHtpasswd(canaryToken);\n case \"s3_bucket\": return makeS3Bucket(canaryToken);\n case \"admin_portal\": return makeAdminPortal(canaryToken);\n default: return makeGeneric(canaryToken);\n }\n}\n\n// ─── .env file ────────────────────────────────────────────────────────────────\n\nfunction makeEnvFile(canaryToken: string, _callbackBase: string, _orgId: string): HoneypotResponse {\n const jwtSecret = genHex(32);\n const dbPassword = genAlphanumeric(20);\n const redisPassword = genAlphanumeric(16);\n const apiKey = genHex(32);\n\n // DNS canary hostnames — fire detection the instant an attacker's tool\n // resolves the domain, before any TCP connection is attempted.\n // The DNS server at srv.anomira.io receives the query, extracts the canary\n // token from the subdomain, and fires the alert via our ingest API.\n const dnsDb = `db.${canaryToken}.srv.anomira.io`;\n const dnsCache = `cache.${canaryToken}.srv.anomira.io`;\n const dnsMonitoring = `hooks.monitoring.${canaryToken}.srv.anomira.io`;\n const dnsAlerts = `hooks.alerts.${canaryToken}.srv.anomira.io`;\n const dnsDeploy = `deploy.internal.${canaryToken}.srv.anomira.io`;\n\n // JWT token: valid format, expired 24h ago. Detection is via jti claim.\n const fakeJwt = generateCanaryJwt(canaryToken, jwtSecret);\n\n const body = `# Production Environment Configuration\n# Last updated: ${new Date(Date.now() - 7 * 86400000).toISOString().split(\"T\")[0]}\n\nNODE_ENV=production\nPORT=3000\n\n# ── Database ──────────────────────────────────────────────────────────────\nDATABASE_URL=postgresql://db_prod:${dbPassword}@${dnsDb}:5432/app_production\nDATABASE_REPLICA_URL=postgresql://db_prod:${dbPassword}@${dnsDb}:5433/app_production\nDATABASE_POOL_MIN=2\nDATABASE_POOL_MAX=10\n\n# ── Cache ─────────────────────────────────────────────────────────────────\nREDIS_URL=redis://:${redisPassword}@${dnsCache}:6379/0\n\n# ── Authentication ────────────────────────────────────────────────────────\nJWT_SECRET=${jwtSecret}\nJWT_EXPIRES_IN=15m\nREFRESH_TOKEN_SECRET=${genHex(32)}\nSESSION_SECRET=${genHex(32)}\n\n# ── Sample authenticated token (service account) ─────────────────────────\nSERVICE_ACCOUNT_TOKEN=${fakeJwt}\n\n# ── AWS ───────────────────────────────────────────────────────────────────\nAWS_REGION=eu-west-1\nAWS_S3_BUCKET=app-production-${genHex(4)}\n\n# ── Monitoring / Webhooks ─────────────────────────────────────────────────\nMONITORING_WEBHOOK=https://${dnsMonitoring}/v1/events/ingest\nALERT_WEBHOOK=https://${dnsAlerts}/services/${genHex(7).toUpperCase()}/notify\nDEPLOY_HOOK=https://${dnsDeploy}/hooks/deploy/${genHex(6)}\n\n# ── External APIs ─────────────────────────────────────────────────────────\nINTERNAL_API_KEY=${apiKey}\n`;\n\n return {\n statusCode: 200,\n contentType: \"text/plain; charset=utf-8\",\n headers: {\n \"Cache-Control\": \"no-store\",\n \"Last-Modified\": new Date(Date.now() - 7 * 86400000).toUTCString(),\n \"ETag\": `\"${genHex(8)}-${genHex(4)}\"`,\n // Apache-style headers — most exposed .env files are on PHP/Apache stacks\n \"Server\": \"Apache/2.4.41 (Ubuntu)\",\n \"X-Content-Type-Options\": \"nosniff\",\n },\n body,\n canaryToken,\n };\n}\n\n// ─── .aws/credentials ─────────────────────────────────────────────────────────\n\nfunction makeAwsCredentials(canaryToken: string): HoneypotResponse {\n // AWS Access Key ID format (confirmed from awsteele.com + summitroute.com):\n // - Exactly 20 characters\n // - Character set: A-Z and 2-7 (base32 alphabet, no 0,1,8,9)\n // - Position 5 (first char after \"AKIA\"): always I or J (per statistical analysis)\n // - Last character: A or Q\n const fakeKeyId = makeAwsKeyId();\n const fakeSecret = makeAwsSecret();\n // Second profile uses ASIA prefix (temporary/STS credentials — more enticing)\n const stsPrefixKey = \"ASIA\" + makeAwsKeyId().slice(4);\n const stsSecret = makeAwsSecret();\n const stsToken = genBase64(300);\n\n const body = `[default]\naws_access_key_id=${fakeKeyId}\naws_secret_access_key=${fakeSecret}\nregion=eu-west-1\n\n[production]\naws_access_key_id=${fakeKeyId}\naws_secret_access_key=${fakeSecret}\nregion=eu-west-1\n\n[staging]\naws_access_key_id=${stsPrefixKey}\naws_secret_access_key=${stsSecret}\naws_session_token=${stsToken}\nregion=eu-west-1\n`;\n\n return {\n statusCode: 200,\n contentType: \"text/plain; charset=utf-8\",\n headers: {\n \"Cache-Control\": \"no-store\",\n \"Last-Modified\": new Date(Date.now() - 14 * 86400000).toUTCString(),\n // Match the Server header an AWS EC2 instance metadata endpoint might have\n \"Server\": \"EC2ws\",\n \"X-Content-Type-Options\": \"nosniff\",\n },\n body,\n canaryToken,\n };\n}\n\n// ─── .git/config ─────────────────────────────────────────────────────────────\n\nfunction makeGitConfig(canaryToken: string, callbackBase: string, orgId: string): HoneypotResponse {\n // GitHub PAT format: ghp_ + 36 alphanumeric chars (confirmed from GitHub docs)\n const ghpToken = `ghp_${genAlphanumeric(36)}`;\n const glpatToken = `glpat-${genAlphanumeric(20)}`;\n const webhookUrl = `${callbackBase}/v1/canary/${orgId}/${canaryToken}`;\n // DNS canary in the git remote URL — fires on `git fetch` or `git clone`\n const dnsGit = `git.${canaryToken}.srv.anomira.io`;\n\n const body = `[core]\n\trepositoryformatversion = 0\n\tfilemode = true\n\tbare = false\n\tlogallrefupdates = true\n\n[remote \"origin\"]\n\turl = https://oauth2:${ghpToken}@github.com/company/app.git\n\tfetch = +refs/heads/*:refs/remotes/origin/*\n\n[remote \"deploy\"]\n\turl = https://deploy-token:${glpatToken}@${dnsGit}/backend/app.git\n\tfetch = +refs/heads/*:refs/remotes/deploy/*\n\n[remote \"notify\"]\n\turl = ${webhookUrl}\n\n[branch \"main\"]\n\tremote = origin\n\tmerge = refs/heads/main\n\n[branch \"production\"]\n\tremote = deploy\n\tmerge = refs/heads/production\n\n[credential \"https://github.com\"]\n\tusername = deploy-bot\n`;\n\n return {\n statusCode: 200,\n contentType: \"text/plain; charset=utf-8\",\n headers: {\n \"Cache-Control\": \"no-store\",\n \"Last-Modified\": new Date(Date.now() - 30 * 86400000).toUTCString(),\n \"Server\": \"Apache/2.4.41 (Ubuntu)\",\n },\n body,\n canaryToken,\n };\n}\n\n// ─── GraphQL introspection ────────────────────────────────────────────────────\n\nfunction makeGraphQL(): HoneypotResponse {\n // Minimal but realistic introspection response.\n // Includes an \"AdminMutation\" type with enticing-sounding fields to\n // fingerprint any tool that auto-generates exploit queries after introspection.\n const body = JSON.stringify({\n data: {\n __schema: {\n queryType: { name: \"Query\" },\n mutationType: { name: \"Mutation\" },\n types: [\n { kind: \"OBJECT\", name: \"Query\", fields: [{ name: \"user\" }, { name: \"users\" }, { name: \"posts\" }] },\n { kind: \"OBJECT\", name: \"Mutation\", fields: [{ name: \"login\" }, { name: \"createUser\" }, { name: \"adminReset\" }, { name: \"exportData\" }] },\n { kind: \"OBJECT\", name: \"User\", fields: [{ name: \"id\" }, { name: \"email\" }, { name: \"role\" }, { name: \"createdAt\" }] },\n { kind: \"OBJECT\", name: \"AuthPayload\", fields: [{ name: \"token\" }, { name: \"user\" }] },\n { kind: \"SCALAR\", name: \"String\", fields: null },\n { kind: \"SCALAR\", name: \"Boolean\", fields: null },\n { kind: \"SCALAR\", name: \"Int\", fields: null },\n { kind: \"SCALAR\", name: \"ID\", fields: null },\n ],\n },\n },\n });\n\n return {\n statusCode: 200,\n contentType: \"application/json; charset=utf-8\",\n headers: {\n \"X-Powered-By\": \"Express\",\n \"X-Request-Id\": `req_${genHex(16)}`,\n },\n body,\n canaryToken: \"\",\n };\n}\n\n// ─── Spring Boot actuator ─────────────────────────────────────────────────────\n\nfunction makeSpringActuator(canaryToken: string): HoneypotResponse {\n // Spring Boot /actuator/env format confirmed from official docs.\n // Sensitive values are shown UNMASKED (simulating misconfigured `show-values=ALWAYS`)\n // — the most dangerous configuration, which is what attackers specifically look for.\n const dbPassword = genAlphanumeric(20);\n const secretKey = genHex(32);\n\n const body = JSON.stringify({\n activeProfiles: [\"production\"],\n defaultProfiles: [\"default\"],\n propertySources: [\n {\n name: \"systemProperties\",\n properties: {\n \"java.runtime.version\": { value: \"17.0.9+9\" },\n \"server.port\": { value: \"8080\" },\n \"user.home\": { value: \"/home/appuser\" },\n },\n },\n {\n name: \"applicationConfig: [classpath:/application-production.properties]\",\n properties: {\n \"spring.datasource.url\": { origin: \"class path resource [application-production.properties] - 3:1\", value: \"jdbc:postgresql://db.internal:5432/myapp\" },\n \"spring.datasource.username\": { origin: \"class path resource [application-production.properties] - 4:1\", value: \"dbadmin\" },\n \"spring.datasource.password\": { origin: \"class path resource [application-production.properties] - 5:1\", value: dbPassword },\n \"app.jwt.secret\": { origin: \"class path resource [application-production.properties] - 8:1\", value: secretKey },\n \"app.canary.token\": { value: canaryToken },\n \"management.endpoints.web.exposure.include\": { value: \"health,info,env,metrics,loggers\" },\n },\n },\n ],\n }, null, 2);\n\n return {\n statusCode: 200,\n contentType: \"application/vnd.spring-boot.actuator.v3+json\",\n headers: {\n \"X-Application-Context\": \"myapp:production:8080\",\n \"X-Content-Type-Options\": \"nosniff\",\n \"X-XSS-Protection\": \"1; mode=block\",\n },\n body,\n canaryToken,\n };\n}\n\n// ─── JSON config ──────────────────────────────────────────────────────────────\n\nfunction makeJsonConfig(canaryToken: string, callbackBase: string, orgId: string): HoneypotResponse {\n const webhookUrl = `${callbackBase}/v1/canary/${orgId}/${canaryToken}`;\n\n const body = JSON.stringify({\n environment: \"production\",\n version: \"2.4.1\",\n database: {\n host: \"db.internal.company.com\",\n port: 5432,\n name: \"app_production\",\n user: \"db_prod\",\n password: genAlphanumeric(20),\n },\n jwt: {\n secret: genHex(32),\n expiresIn: \"15m\",\n },\n webhooks: {\n events: webhookUrl,\n alerts: `${callbackBase}/v1/canary/${orgId}/${canaryToken}`,\n },\n internalApiKey: genHex(32),\n }, null, 2);\n\n return {\n statusCode: 200,\n contentType: \"application/json; charset=utf-8\",\n headers: {\n \"Cache-Control\": \"no-store\",\n \"ETag\": `\"${genHex(8)}\"`,\n \"X-Powered-By\": \"Express\",\n },\n body,\n canaryToken,\n };\n}\n\n// ─── .htpasswd ────────────────────────────────────────────────────────────────\n//\n// Apache password file format:\n// username:$apr1$salt$hash ← APR1-MD5 (most common)\n// username:$2y$10$... ← bcrypt (newer systems)\n//\n// The canary token is embedded as part of one of the password hashes so that\n// any attacker who cracks it offline and tries to use it will be detected.\n// Format confirmed: Apache httpd documentation + htpasswd man page.\n\nfunction makeHtpasswd(canaryToken: string): HoneypotResponse {\n // APR1-MD5: $apr1$ + 8-char salt + $ + 22-char base64-like hash\n // Character set for APR1: A-Z, a-z, 0-9, /, .\n const apr1Salt = genAlphanumeric(8).toLowerCase();\n const apr1Hash = genBase64(22).replace(/[+=/]/g, (c) =>\n ({ \"+\": \".\", \"=\": \"/\", \"/\": \"X\" }[c] ?? c)\n );\n\n // bcrypt: $2y$10$ + 53-char base64url hash (standard bcrypt output)\n const bcryptHash = genBase64(53).replace(/[+=]/g, (c) =>\n ({ \"+\": \".\", \"=\": \"/\" }[c] ?? c)\n );\n\n // Third entry uses the canary token woven into the hash — if cracked and used:\n const canaryApr1 = `$apr1$${canaryToken.slice(0, 8)}$${genBase64(22).slice(0, 22)}`;\n\n const body = `# Apache HTTP Server password file\nadmin:$apr1$${apr1Salt}$${apr1Hash}\ndeploy:$2y$10$${bcryptHash}\nroot:${canaryApr1}\nbackup-user:$apr1$${genAlphanumeric(8).toLowerCase()}$${genBase64(22).slice(0, 22)}\n`;\n\n return {\n statusCode: 200,\n contentType: \"text/plain; charset=utf-8\",\n headers: {\n \"Cache-Control\": \"no-store\",\n \"Last-Modified\": new Date(Date.now() - 45 * 86400000).toUTCString(),\n \"Server\": \"Apache/2.4.41 (Ubuntu)\",\n \"Content-Disposition\": \"inline\",\n },\n body,\n canaryToken,\n };\n}\n\n// ─── S3 bucket listing XML ─────────────────────────────────────────────────────\n//\n// AWS S3 ListAllMyBucketsResult XML format.\n// Source: AWS S3 REST API documentation (ListBuckets response syntax).\n// The canary token is embedded in one bucket name — if the attacker tries\n// to access that bucket on real AWS, their IP and tool are logged by CloudTrail.\n\nfunction makeS3Bucket(canaryToken: string): HoneypotResponse {\n const ownerId = genHex(32) + genHex(32); // 64 hex chars — real S3 owner IDs\n const now = new Date();\n const dateStr = (offset: number) =>\n new Date(now.getTime() - offset).toISOString().replace(/\\.\\d{3}Z/, \".000Z\");\n\n const body = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ListAllMyBucketsResult xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\">\n <Owner>\n <ID>${ownerId}</ID>\n <DisplayName>company-admin</DisplayName>\n </Owner>\n <Buckets>\n <Bucket>\n <Name>company-production-data</Name>\n <CreationDate>${dateStr(120 * 86400000)}</CreationDate>\n </Bucket>\n <Bucket>\n <Name>company-assets-${genHex(4)}</Name>\n <CreationDate>${dateStr(90 * 86400000)}</CreationDate>\n </Bucket>\n <Bucket>\n <Name>company-backups-${canaryToken.slice(0, 12)}</Name>\n <CreationDate>${dateStr(30 * 86400000)}</CreationDate>\n </Bucket>\n <Bucket>\n <Name>company-logs-archive</Name>\n <CreationDate>${dateStr(180 * 86400000)}</CreationDate>\n </Bucket>\n </Buckets>\n</ListAllMyBucketsResult>`;\n\n return {\n statusCode: 200,\n contentType: \"application/xml\",\n headers: {\n \"x-amz-request-id\": genHex(8).toUpperCase() + genHex(8).toUpperCase(),\n \"x-amz-id-2\": genBase64(60),\n \"Server\": \"AmazonS3\",\n },\n body,\n canaryToken,\n };\n}\n\n// ─── Admin portal HTML ────────────────────────────────────────────────────────\n//\n// Returns a convincing generic admin login page. The form posts back to a\n// sub-path (./login) so that POST requests with credential bodies are also\n// intercepted by the SDK's honeypot prefix-matching and captured as\n// http.honeypot.credential_attempt events.\n//\n// Design: clean, minimal, looks like a lightweight CMS admin panel.\n// The canary token is embedded in a hidden _token field — if an attacker\n// automates form submission with this token, subsequent requests carrying\n// it are detected.\n\nfunction makeAdminPortal(canaryToken: string): HoneypotResponse {\n const body = `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>Administration — Login</title>\n <style>\n *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n body {\n background: #0f1117;\n color: #c9d1d9;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Helvetica, Arial, sans-serif;\n display: flex; align-items: center; justify-content: center;\n min-height: 100vh;\n }\n .card {\n background: #161b22;\n border: 1px solid #30363d;\n border-radius: 8px;\n padding: 32px;\n width: 100%;\n max-width: 360px;\n }\n h1 { font-size: 1.1rem; font-weight: 600; color: #e6edf3; margin-bottom: 4px; }\n .subtitle { font-size: 0.8rem; color: #7d8590; margin-bottom: 24px; }\n label { display: block; font-size: 0.8rem; font-weight: 600; color: #c9d1d9; margin-bottom: 6px; }\n input[type=text], input[type=password] {\n width: 100%; padding: 8px 12px; border: 1px solid #30363d;\n border-radius: 6px; background: #0d1117; color: #c9d1d9;\n font-size: 0.875rem; outline: none; margin-bottom: 16px;\n }\n input:focus { border-color: #388bfd; box-shadow: 0 0 0 3px rgba(56,139,253,.1); }\n .row { display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px; }\n .row label { margin-bottom: 0; font-weight: normal; display: flex; align-items: center; gap: 6px; cursor: pointer; }\n button {\n width: 100%; padding: 8px 16px; background: #238636; color: #fff;\n border: none; border-radius: 6px; font-size: 0.875rem; font-weight: 600;\n cursor: pointer; transition: background .15s;\n }\n button:hover { background: #2ea043; }\n .footer { text-align: center; font-size: 0.72rem; color: #7d8590; margin-top: 20px; }\n </style>\n</head>\n<body>\n <div class=\"card\">\n <h1>Administration Panel</h1>\n <p class=\"subtitle\">Sign in to continue</p>\n <form method=\"POST\" action=\"./login\" autocomplete=\"off\">\n <input type=\"hidden\" name=\"_token\" value=\"${canaryToken}\" />\n <div>\n <label for=\"username\">Username</label>\n <input id=\"username\" name=\"username\" type=\"text\" placeholder=\"admin\" autocomplete=\"off\" />\n </div>\n <div>\n <label for=\"password\">Password</label>\n <input id=\"password\" name=\"password\" type=\"password\" placeholder=\"••••••••\" autocomplete=\"off\" />\n </div>\n <div class=\"row\">\n <label><input type=\"checkbox\" name=\"remember\" /> Remember me</label>\n <a href=\"#\" style=\"font-size:.8rem;color:#388bfd;text-decoration:none;\">Forgot password?</a>\n </div>\n <button type=\"submit\">Sign in</button>\n </form>\n <p class=\"footer\">v3.2.1 · Secure Administration Portal</p>\n </div>\n</body>\n</html>`;\n\n return {\n statusCode: 200,\n contentType: \"text/html; charset=utf-8\",\n headers: {\n \"Cache-Control\": \"no-store, no-cache\",\n \"X-Frame-Options\": \"SAMEORIGIN\",\n \"Server\": \"nginx/1.18.0 (Ubuntu)\",\n },\n body,\n canaryToken,\n };\n}\n\n/**\n * Response returned when an attacker submits credentials to an admin portal honeypot.\n * Returns a convincing \"invalid credentials\" page that lures them to try more combinations.\n */\nexport function makeAdminLoginFailed(): HoneypotResponse {\n const body = `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\" />\n <title>Administration — Login</title>\n <style>\n *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n body { background: #0f1117; color: #c9d1d9; font-family: -apple-system, sans-serif;\n display: flex; align-items: center; justify-content: center; min-height: 100vh; }\n .card { background: #161b22; border: 1px solid #30363d; border-radius: 8px; padding: 32px; width: 100%; max-width: 360px; }\n h1 { font-size: 1.1rem; font-weight: 600; color: #e6edf3; margin-bottom: 4px; }\n .error { background: #3d1010; border: 1px solid #f85149; color: #f85149; border-radius: 6px; padding: 10px 14px; font-size: .8rem; margin-bottom: 16px; }\n label { display: block; font-size: .8rem; font-weight: 600; color: #c9d1d9; margin-bottom: 6px; }\n input[type=text], input[type=password] { width: 100%; padding: 8px 12px; border: 1px solid #f85149; border-radius: 6px; background: #0d1117; color: #c9d1d9; font-size: .875rem; outline: none; margin-bottom: 16px; }\n button { width: 100%; padding: 8px 16px; background: #238636; color: #fff; border: none; border-radius: 6px; font-size: .875rem; font-weight: 600; cursor: pointer; }\n .footer { text-align: center; font-size: .72rem; color: #7d8590; margin-top: 20px; }\n </style>\n</head>\n<body>\n <div class=\"card\">\n <h1>Administration Panel</h1>\n <div class=\"error\">⚠ Invalid username or password. Please try again.</div>\n <form method=\"POST\" action=\"\" autocomplete=\"off\">\n <div><label for=\"u\">Username</label><input id=\"u\" name=\"username\" type=\"text\" autocomplete=\"off\" /></div>\n <div><label for=\"p\">Password</label><input id=\"p\" name=\"password\" type=\"password\" autocomplete=\"off\" /></div>\n <button type=\"submit\">Sign in</button>\n </form>\n <p class=\"footer\">v3.2.1 · Secure Administration Portal</p>\n </div>\n</body>\n</html>`;\n\n return {\n statusCode: 401,\n contentType: \"text/html; charset=utf-8\",\n headers: {\n \"Cache-Control\": \"no-store\",\n \"Server\": \"nginx/1.18.0 (Ubuntu)\",\n \"WWW-Authenticate\": 'Form realm=\"Administration Panel\"',\n },\n body,\n canaryToken: \"\",\n };\n}\n\n// ─── Generic ──────────────────────────────────────────────────────────────────\n\nfunction makeGeneric(canaryToken: string): HoneypotResponse {\n return {\n statusCode: 200,\n contentType: \"text/plain; charset=utf-8\",\n headers: { \"Cache-Control\": \"no-store\" },\n body: `# ${canaryToken}\\n`,\n canaryToken,\n };\n}\n\n// ─── Canary JWT generation ────────────────────────────────────────────────────\n\n/**\n * Generate a valid-format JWT that is expired (24h ago).\n * The `kid` header and `jti` claim both contain the canary token.\n * Any request arriving with this JWT (replayed or reforged with the same secret)\n * can be detected by checking `kid` against the org's canary registry.\n *\n * Format confirmed: Auth0 JWT docs + canarytokens.org JWT token implementation.\n */\nexport function generateCanaryJwt(canaryToken: string, secret: string): string {\n const now = Math.floor(Date.now() / 1000);\n // kid looks like a normal UUID-format signing key — detection uses jti instead\n const kid = `${canaryToken.slice(0,8)}-${canaryToken.slice(8,12)}-4${canaryToken.slice(13,16)}-${canaryToken.slice(16,20)}-${canaryToken.slice(20,32)}`;\n const header = { alg: \"HS256\", typ: \"JWT\", kid };\n const payload = {\n sub: \"svc_internal_7482\",\n name: \"service-account\",\n role: \"admin\",\n email: \"admin@internal.company.com\",\n iat: now - 172_800, // issued 48h ago (realistic stale credential)\n exp: now - 86_400, // expired 24h ago\n jti: canaryToken, // raw hex — no \"canary-\" prefix to leak purpose\n };\n\n const h = Buffer.from(JSON.stringify(header)).toString(\"base64url\");\n const p = Buffer.from(JSON.stringify(payload)).toString(\"base64url\");\n const sig = createHmac(\"sha256\", secret).update(`${h}.${p}`).digest(\"base64url\");\n return `${h}.${p}.${sig}`;\n}\n\n// ─── Credential helpers ───────────────────────────────────────────────────────\n\n/** AWS Access Key ID — 20 chars, AKIA prefix, position 5 = I, last = A */\nfunction makeAwsKeyId(): string {\n // Confirmed format: AKIA + I (pos 5) + 14 random [A-Z2-7] + A\n // Source: awsteele.com/blog/2020/09/26/aws-access-key-format\n const base32Chars = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567\";\n const middle = Array.from({ length: 13 }, () =>\n base32Chars[randomBytes(1)[0]! % base32Chars.length]!\n ).join(\"\");\n return `AKIAI${middle}A`; // AKIA + I + 13 chars + A = 20 total\n}\n\n/** AWS Secret Access Key — 40 chars, base64-standard charset */\nfunction makeAwsSecret(): string {\n // Confirmed: 40 chars, A-Z, a-z, 0-9, +, /\n // Source: summitroute.com/blog/2018/06/20/aws_security_credential_formats\n return randomBytes(30).toString(\"base64\").slice(0, 40);\n}\n\nfunction genHex(bytes: number): string {\n return randomBytes(bytes).toString(\"hex\");\n}\n\nfunction genAlphanumeric(len: number): string {\n const chars = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\";\n return Array.from({ length: len }, () =>\n chars[randomBytes(1)[0]! % chars.length]!\n ).join(\"\");\n}\n\nfunction genBase64(approxLen: number): string {\n return randomBytes(Math.ceil(approxLen * 3 / 4)).toString(\"base64\").slice(0, approxLen);\n}\n","import { AsyncLocalStorage } from \"node:async_hooks\";\nimport { EventBuffer } from \"./buffer.js\";\nimport { checkGeoVelocity } from \"./geo-velocity.js\";\nimport { scanForLeaks } from \"./sensitive.js\";\nimport type { AnomiraConfig, SdkEvent, FirewallRule, EndpointDeclaration } from \"./types.js\";\nimport { EventName } from \"./types.js\";\nimport { detectAgent } from \"./agent-detection.js\";\nimport { computeBrowserFingerprint, extractHttp2Settings, extractUpstreamTls } from \"./behavioral-fingerprint.js\";\nimport { detectHoneypotType, generateHoneypotResponse, generateCanaryJwt, makeAdminLoginFailed } from \"./honeypot-responses.js\";\nimport { randomBytes } from \"node:crypto\";\n\ninterface RequestContext {\n endpoint: string;\n method: string;\n ip: string;\n}\n\n// One shared ALS instance per SDK module load\nconst requestContext = new AsyncLocalStorage<RequestContext>();\n\nconst DEFAULT_INGEST_URL = \"https://ingest.anomira.io/v1/events\";\nconst DEFAULT_BATCH_SIZE = 100;\nconst DEFAULT_FLUSH_MS = 5_000;\nconst DEFAULT_MAX_RETRIES = 3;\n\n// ── Sensitive field taxonomy ──────────────────────────────────────────────────\n// Categorised so the dashboard can apply the right security action per category.\n// Field names are checked via Object.keys() — values via VALUE_PATTERNS below.\n// All detection runs in onFinish (after response sent) — zero client latency.\n\nconst SENSITIVE_FIELDS: Record<string, string[]> = {\n identity: [\n \"first_name\", \"last_name\", \"full_name\", \"name\",\n \"bvn\", \"nin\", \"ssn\", \"national_id\", \"tin\",\n \"passport\", \"passport_number\",\n \"drivers_license\", \"license_number\",\n \"voter_id\",\n \"dob\", \"date_of_birth\", \"birth_date\", \"birthdate\",\n \"gender\", \"marital_status\", \"nationality\",\n ],\n contact: [\n \"email\",\n \"phone\", \"phone_number\", \"mobile\", \"mobile_number\",\n \"address\", \"street\", \"city\", \"state\", \"postal_code\", \"zip\",\n ],\n financial: [\n \"card_number\", \"credit_card\", \"debit_card\", \"cvv\",\n \"account_number\", \"bank_account\", \"routing_number\", \"sort_code\",\n \"iban\", \"swift\", \"wallet_id\",\n ],\n authentication: [\n \"password\", \"pin\", \"otp\",\n \"secret\", \"private_key\", \"api_key\",\n \"access_token\", \"refresh_token\", \"jwt\", \"bearer_token\",\n \"security_answer\", \"mother_maiden_name\",\n ],\n biometric: [\n \"fingerprint\", \"face_id\", \"iris\", \"voiceprint\", \"biometric\",\n ],\n health: [\n \"medical_record\", \"diagnosis\", \"blood_group\", \"insurance_id\",\n ],\n fintech: [\n \"kyc_id\", \"customer_id\", \"beneficiary_account\", \"transaction_pin\",\n \"wallet_balance\", \"bank_verification_number\",\n \"virtual_account\", \"monnify_account\", \"paystack_customer\", \"flutterwave_customer\",\n ],\n};\n\n// Reverse lookup: lowercase field name → category (built once at module load)\nconst FIELD_CATEGORY_MAP = new Map<string, string>();\nfor (const [cat, fields] of Object.entries(SENSITIVE_FIELDS)) {\n for (const f of fields) FIELD_CATEGORY_MAP.set(f, cat);\n}\n\n// Value-level patterns — only applied to string values ≤ 200 chars.\n// Catches obfuscated/generic field names (\"x1\", \"identifier\", etc.).\n// Each regex is anchored to avoid partial matches on longer strings.\nconst VALUE_PATTERNS: { name: string; category: string; re: RegExp }[] = [\n { name: \"email\", category: \"contact\", re: /^[a-zA-Z0-9._%+\\-]+@[a-zA-Z0-9.\\-]+\\.[a-zA-Z]{2,}$/ },\n { name: \"bvn_nin\", category: \"identity\", re: /^\\d{11}$/ },\n { name: \"card_number\", category: \"financial\", re: /^\\d{13,19}$/ },\n { name: \"phone_ng\", category: \"contact\", re: /^(?:\\+234|0)[789][01]\\d{8}$/ },\n { name: \"jwt\", category: \"authentication\", re: /^eyJ[A-Za-z0-9_\\-]{10,}\\.[A-Za-z0-9_\\-]{10,}\\./ },\n { name: \"iban\", category: \"financial\", re: /^[A-Z]{2}\\d{2}[A-Z0-9]{4,30}$/ },\n];\n\n/**\n * AnomiraClient — the core SDK object.\n *\n * Instantiate once and reuse across your application:\n *\n * ```ts\n * import { Anomira } from \"@anomira/node-sdk\";\n *\n * export const sentinel = new Anomira({\n * apiKey: process.env.SENTINEL_API_KEY!,\n * appId: process.env.SENTINEL_APP_ID!,\n * });\n * ```\n */\nexport class AnomiraClient {\n readonly config: Required<Omit<AnomiraConfig, \"getUserId\" | \"getIp\" | \"detect\" | \"autoBlock\">> & {\n getUserId: NonNullable<AnomiraConfig[\"getUserId\"]>;\n getIp: NonNullable<AnomiraConfig[\"getIp\"]>;\n detect: Required<NonNullable<AnomiraConfig[\"detect\"]>>;\n captureConsole: boolean;\n service: string;\n autoBlock: Required<NonNullable<AnomiraConfig[\"autoBlock\"]>>;\n };\n\n private readonly buffer: EventBuffer;\n private readonly logBuffer: Array<{ level: string; service: string; message: string; meta: Record<string, unknown>; ts: number }> = [];\n private logFlushTimer: ReturnType<typeof setInterval> | null = null;\n private blocklistTimer: ReturnType<typeof setInterval> | null = null;\n private firewallTimer: ReturnType<typeof setInterval> | null = null;\n private communityThreatTimer: ReturnType<typeof setInterval> | null = null;\n /** True when credentials are missing — all operations become no-ops. */\n private disabled = false;\n /** In-process cache of manually blocked IPs — refreshed every 60 s. */\n private blockedIpCache: Set<string> = new Set();\n /** In-process cache of community threat IPs with their confidence scores.\n * Populated from Anomira's federated threat network — IPs confirmed malicious\n * across multiple customers. Refreshed every 60 s. */\n private communityThreatCache: Map<string, { score: number; topAttack: string }> = new Map();\n /**\n * Set of honeypot paths to intercept. When a request matches, the SDK returns\n * a fake response instead of forwarding to the customer's route handlers.\n * Synced from ingest every 60 s. Keys are lowercase path strings.\n */\n private honeypotPaths: Set<string> = new Set();\n /**\n * Set of active canary token strings embedded in previously-served honeypot\n * responses. When any of these appear in a request's Authorization header or\n * body, a http.canary.triggered event is fired.\n * Synced from ingest every 60 s (max 100 tokens, sliding 7-day window).\n */\n private canaryTokenCache: Set<string> = new Set();\n /** In-process cache of firewall rules with pre-compiled regex — refreshed every 60 s. */\n private compiledRules: Array<{ rule: FirewallRule; re?: RegExp }> = [];\n // Saved originals — used by SDK internals so patched console doesn't recurse\n private readonly _origLog = console.log.bind(console);\n private readonly _origWarn = console.warn.bind(console);\n private readonly _origError = console.error.bind(console);\n\n constructor(config: AnomiraConfig) {\n if (!config.apiKey || !config.appId) {\n const missing = [!config.apiKey && \"apiKey\", !config.appId && \"appId\"].filter(Boolean).join(\", \");\n console.warn(`[Anomira] SDK disabled — missing config: ${missing}. Set SENTINEL_API_KEY and SENTINEL_APP_ID to enable monitoring.`);\n this.disabled = true;\n // Provide safe defaults so the rest of the class doesn't blow up\n this.config = {\n apiKey: \"\", appId: \"\", ingestUrl: DEFAULT_INGEST_URL, geoLookupUrl: \"\",\n maxBatchSize: DEFAULT_BATCH_SIZE, flushIntervalMs: DEFAULT_FLUSH_MS,\n maxRetries: DEFAULT_MAX_RETRIES, debug: false, captureConsole: false, service: \"app\",\n getUserId: defaultGetUserId, getIp: defaultGetIp,\n detect: { bruteForce: true, rateAbuse: true, pathTraversal: true, xss: true, scanDetection: true, geoVelocity: true },\n autoBlock: { enabled: true, communityThreshold: 85 },\n };\n this.buffer = new EventBuffer({ appId: \"\", apiKey: \"\", ingestUrl: DEFAULT_INGEST_URL, maxBatchSize: 0, flushIntervalMs: 999_999_999, maxRetries: 0, debug: false });\n return;\n }\n\n this.config = {\n apiKey: config.apiKey,\n appId: config.appId,\n ingestUrl: config.ingestUrl ?? DEFAULT_INGEST_URL,\n geoLookupUrl: config.geoLookupUrl ?? \"\",\n maxBatchSize: config.maxBatchSize ?? DEFAULT_BATCH_SIZE,\n flushIntervalMs: config.flushIntervalMs ?? DEFAULT_FLUSH_MS,\n maxRetries: config.maxRetries ?? DEFAULT_MAX_RETRIES,\n debug: config.debug ?? false,\n captureConsole: config.captureConsole ?? false,\n service: config.service ?? \"app\",\n getUserId: config.getUserId ?? defaultGetUserId,\n getIp: config.getIp ?? defaultGetIp,\n detect: {\n bruteForce: config.detect?.bruteForce ?? true,\n rateAbuse: config.detect?.rateAbuse ?? true,\n pathTraversal: config.detect?.pathTraversal ?? true,\n xss: config.detect?.xss ?? true,\n scanDetection: config.detect?.scanDetection ?? true,\n geoVelocity: config.detect?.geoVelocity ?? true,\n },\n autoBlock: {\n enabled: config.autoBlock?.enabled ?? true,\n communityThreshold: config.autoBlock?.communityThreshold ?? 85,\n },\n };\n\n this.buffer = new EventBuffer({\n appId: this.config.appId,\n apiKey: this.config.apiKey,\n ingestUrl: this.config.ingestUrl,\n maxBatchSize: this.config.maxBatchSize,\n flushIntervalMs:this.config.flushIntervalMs,\n maxRetries: this.config.maxRetries,\n debug: this.config.debug,\n });\n\n void this.#validateCredentials();\n\n // Fetch blocked-IP list immediately, then refresh every 60 s\n void this.#refreshBlocklist();\n this.blocklistTimer = setInterval(() => { void this.#refreshBlocklist(); }, 60_000);\n if (this.blocklistTimer.unref) this.blocklistTimer.unref();\n\n // Fetch firewall rules immediately, then refresh every 60 s\n void this.#refreshFirewallRules();\n this.firewallTimer = setInterval(() => { void this.#refreshFirewallRules(); }, 60_000);\n if (this.firewallTimer.unref) this.firewallTimer.unref();\n\n // Fetch honeypot paths immediately, then refresh every 60 s.\n void this.#refreshHoneypotPaths();\n const honeypotTimer = setInterval(() => { void this.#refreshHoneypotPaths(); }, 60_000);\n if ((honeypotTimer as { unref?: () => void }).unref) (honeypotTimer as { unref: () => void }).unref();\n\n // Fetch canary tokens (harvested credential strings to detect in requests).\n void this.#refreshCanaryTokens();\n const canaryTimer = setInterval(() => { void this.#refreshCanaryTokens(); }, 60_000);\n if ((canaryTimer as { unref?: () => void }).unref) (canaryTimer as { unref: () => void }).unref();\n\n // Fetch community threat intelligence immediately, then refresh every 60 s.\n // This is the \"invisible security\" layer — IPs from Anomira's federated\n // network are auto-blocked when their confidence score meets the threshold,\n // regardless of whether a human has manually reviewed them.\n void this.#refreshCommunityThreats();\n this.communityThreatTimer = setInterval(() => { void this.#refreshCommunityThreats(); }, 60_000);\n if (this.communityThreatTimer.unref) this.communityThreatTimer.unref();\n\n // Flush logs every 10s (separate from events buffer)\n this.logFlushTimer = setInterval(() => { void this.#flushLogs(); }, 10_000);\n if (this.logFlushTimer.unref) this.logFlushTimer.unref();\n\n if (this.config.captureConsole) this.#interceptConsole();\n }\n\n #interceptConsole(): void {\n const map: Array<[keyof Console, \"debug\" | \"info\" | \"warn\" | \"error\"]> = [\n [\"debug\", \"debug\"],\n [\"log\", \"info\"],\n [\"info\", \"info\"],\n [\"warn\", \"warn\"],\n [\"error\", \"error\"],\n ];\n for (const [method, level] of map) {\n const original = (console[method] as (...a: unknown[]) => void).bind(console);\n (console as unknown as Record<string, unknown>)[method] = (...args: unknown[]) => {\n original(...args); // still prints to terminal\n // Skip SDK's own internal messages to avoid noise in the Logs dashboard\n const first = args[0];\n if (typeof first === \"string\" && first.startsWith(\"[Anomira]\")) return;\n const message = args\n .map((a) => (typeof a === \"string\" ? a : JSON.stringify(a)))\n .join(\" \");\n const ctx = requestContext.getStore();\n this.log(level, message, {\n service: this.config.service,\n ...(ctx ? { endpoint: ctx.endpoint, method: ctx.method, ip: ctx.ip } : {}),\n });\n };\n }\n }\n\n async #refreshBlocklist(): Promise<void> {\n const syncUrl = this.config.ingestUrl.replace(/\\/v1\\/events$/, \"/v1/blocked-ips/sync\");\n try {\n const res = await fetch(syncUrl, {\n headers: {\n Authorization: `Bearer ${this.config.apiKey}`,\n \"User-Agent\": `@anomira/node-sdk/0.2.2`,\n },\n signal: AbortSignal.timeout(5_000),\n });\n if (!res.ok) return;\n const data = await res.json() as { ips?: string[] };\n this.blockedIpCache = new Set(data.ips ?? []);\n if (this.config.debug && this.blockedIpCache.size > 0) {\n this._origLog(`[Anomira] blocklist refreshed — ${this.blockedIpCache.size} blocked IPs`);\n }\n } catch {\n // Network error: keep the existing cache, never throw\n }\n }\n\n async #refreshCommunityThreats(): Promise<void> {\n const syncUrl = this.config.ingestUrl.replace(/\\/v1\\/events$/, \"/v1/community-threats/sync\");\n try {\n const res = await fetch(syncUrl, {\n headers: {\n Authorization: `Bearer ${this.config.apiKey}`,\n \"User-Agent\": `@anomira/node-sdk/0.2.2`,\n },\n signal: AbortSignal.timeout(5_000),\n });\n if (!res.ok) return;\n\n const data = await res.json() as {\n threats?: Array<{ ip: string; score: number; topAttack: string }>;\n };\n\n const newCache = new Map<string, { score: number; topAttack: string }>();\n for (const t of data.threats ?? []) {\n newCache.set(t.ip, { score: t.score, topAttack: t.topAttack });\n }\n this.communityThreatCache = newCache;\n\n if (this.config.debug && newCache.size > 0) {\n this._origLog(`[Anomira] community threats refreshed — ${newCache.size} known threat IPs`);\n }\n } catch {\n // Network error: keep the existing cache, never throw\n }\n }\n\n async #refreshHoneypotPaths(): Promise<void> {\n const syncUrl = this.config.ingestUrl.replace(/\\/v1\\/events$/, \"/v1/honeypots/sync\");\n try {\n const res = await fetch(syncUrl, {\n headers: { Authorization: `Bearer ${this.config.apiKey}`, \"User-Agent\": \"@anomira/node-sdk/0.2.2\" },\n signal: AbortSignal.timeout(5_000),\n });\n if (!res.ok) return;\n const data = await res.json() as { paths?: string[] };\n this.honeypotPaths = new Set((data.paths ?? []).map((p) => p.toLowerCase()));\n if (this.config.debug && this.honeypotPaths.size > 0) {\n this._origLog(`[Anomira] honeypot paths refreshed — ${this.honeypotPaths.size} traps active`);\n }\n } catch { /* keep existing cache on error */ }\n }\n\n async #refreshCanaryTokens(): Promise<void> {\n const syncUrl = this.config.ingestUrl.replace(/\\/v1\\/events$/, \"/v1/canary-tokens/sync\");\n try {\n const res = await fetch(syncUrl, {\n headers: { Authorization: `Bearer ${this.config.apiKey}`, \"User-Agent\": \"@anomira/node-sdk/0.2.2\" },\n signal: AbortSignal.timeout(5_000),\n });\n if (!res.ok) return;\n const data = await res.json() as { tokens?: string[] };\n this.canaryTokenCache = new Set(data.tokens ?? []);\n } catch { /* keep existing cache on error */ }\n }\n\n async #refreshFirewallRules(): Promise<void> {\n const syncUrl = this.config.ingestUrl.replace(/\\/v1\\/events$/, \"/v1/firewall-rules/sync\");\n try {\n const res = await fetch(syncUrl, {\n headers: {\n Authorization: `Bearer ${this.config.apiKey}`,\n \"User-Agent\": `@anomira/node-sdk/0.1.0`,\n },\n signal: AbortSignal.timeout(5_000),\n });\n if (!res.ok) return;\n const data = await res.json() as { rules?: FirewallRule[] };\n this.compiledRules = (data.rules ?? []).map((rule) => ({\n rule,\n re: rule.operator === \"regex\" ? (() => { try { return new RegExp(rule.value, \"i\"); } catch { return undefined; } })() : undefined,\n }));\n if (this.config.debug && this.compiledRules.length > 0) {\n this._origLog(`[Anomira] firewall rules refreshed — ${this.compiledRules.length} active rules`);\n }\n } catch {\n // Network error: keep existing cache, never throw\n }\n }\n\n /** Evaluate all cached firewall rules against the current request.\n * Returns the first matching rule, or null if none match. */\n #matchFirewallRule(req: {\n url: string;\n body: unknown;\n headers: Record<string, string | undefined>;\n ip: string;\n }): { rule: FirewallRule } | null {\n for (const { rule, re } of this.compiledRules) {\n let target: string;\n switch (rule.field) {\n case \"url\": target = req.url; break;\n case \"body\": target = typeof req.body === \"string\" ? req.body : JSON.stringify(req.body ?? \"\"); break;\n case \"header\": target = req.headers[(rule.headerName ?? \"\").toLowerCase()] ?? \"\"; break;\n case \"user_agent\": target = req.headers[\"user-agent\"] ?? \"\"; break;\n case \"ip\": target = req.ip; break;\n default: continue;\n }\n\n const matched = rule.operator === \"regex\"\n ? (re?.test(target) ?? false)\n : rule.operator === \"contains\" ? target.includes(rule.value)\n : rule.operator === \"equals\" ? target === rule.value\n : rule.operator === \"starts_with\" ? target.startsWith(rule.value)\n : rule.operator === \"ends_with\" ? target.endsWith(rule.value)\n : false;\n\n if (matched) return { rule };\n }\n return null;\n }\n\n async #flushLogs(): Promise<void> {\n if (this.logBuffer.length === 0) return;\n const batch = this.logBuffer.splice(0, 500);\n const logsUrl = this.config.ingestUrl.replace(/\\/v1\\/events$/, \"/v1/logs\");\n try {\n const res = await fetch(logsUrl, {\n method: \"POST\",\n redirect: \"manual\",\n headers: {\n Authorization: `Bearer ${this.config.apiKey}`,\n \"Content-Type\": \"application/json\",\n \"User-Agent\": `@anomira/node-sdk/0.1.0`,\n },\n body: JSON.stringify({ appId: this.config.appId, logs: batch }),\n signal: AbortSignal.timeout(8_000),\n });\n if (this.config.debug) {\n this._origLog(`[Anomira] [logs] ✅ sent ${batch.length} log entries (${res.status})`);\n }\n } catch {\n // Re-queue on failure — put them back at the front\n this.logBuffer.unshift(...batch);\n }\n }\n\n async #validateCredentials(): Promise<void> {\n // Derive the ping URL from the ingest URL: swap /v1/events → /v1/ping\n const pingUrl = this.config.ingestUrl.replace(/\\/v1\\/events$/, \"/v1/ping\")\n + `?appId=${encodeURIComponent(this.config.appId)}`;\n\n try {\n const res = await fetch(pingUrl, {\n method: \"GET\",\n redirect: \"manual\",\n headers: {\n Authorization: `Bearer ${this.config.apiKey}`,\n \"User-Agent\": `@anomira/node-sdk/0.1.0`,\n },\n signal: AbortSignal.timeout(5_000),\n });\n\n if (res.status >= 300 && res.status < 400) {\n this._origWarn(`[Anomira] ❌ Wrong ingest URL — got redirect to ${res.headers.get(\"location\")}. Check SENTINEL_INGEST_URL.`);\n return;\n }\n if (res.ok) {\n this._origLog(`[Anomira] ✅ Connected (appId: ${this.config.appId.slice(0, 8)}…)`);\n return;\n }\n if (res.status === 401) {\n this._origWarn(\"[Anomira] ❌ Invalid API key — check your SENTINEL_API_KEY\");\n return;\n }\n if (res.status === 403) {\n this._origWarn(\"[Anomira] ❌ App not found or appId mismatch — check your SENTINEL_APP_ID\");\n return;\n }\n this._origWarn(`[Anomira] ⚠️ Ingest returned HTTP ${res.status} — check your configuration`);\n } catch {\n this._origWarn(\"[Anomira] ⚠️ Could not reach ingest endpoint — check SENTINEL_INGEST_URL (current: \" + this.config.ingestUrl + \")\");\n }\n }\n\n // ─── Public API ────────────────────────────────────────────────────────────\n\n /**\n * Extract the real client IP from a request object, reading forwarded headers\n * in the correct priority order (Cloudflare → XFF → X-Real-IP → socket).\n *\n * Use this instead of `req.ip` when tracking events manually. `req.ip` in\n * Express without `trust proxy` set returns the proxy's address (127.0.0.1\n * behind Nginx), which breaks geo-detection and IP blocking.\n *\n * ```ts\n * // ✅ Correct — reads X-Forwarded-For / CF-Connecting-IP automatically\n * sentinel.track(EventName.OTP_FAILED, {\n * ip: sentinel.getClientIp(req),\n * userId: req.body.phone,\n * meta: { endpoint: \"/api/verify-otp\" },\n * });\n *\n * // ❌ Wrong behind a proxy — req.ip is 127.0.0.1 without trust proxy configured\n * sentinel.track(EventName.OTP_FAILED, { ip: req.ip, ... });\n * ```\n */\n getClientIp(req: unknown): string {\n return this.config.getIp(req);\n }\n\n /**\n * Track a custom security event.\n *\n * ```ts\n * sentinel.track(EventName.OTP_FAILED, {\n * ip: sentinel.getClientIp(req), // use getClientIp, not req.ip\n * userId: req.body.phone,\n * meta: { endpoint: \"/api/verify-otp\", attempts: 3 },\n * });\n * ```\n */\n /**\n * Returns true if the IP should be blocked. Synchronous — no network call.\n *\n * Checks two independent sources:\n * 1. Manual blocklist — IPs explicitly blocked by the customer or via playbooks.\n * 2. Community threats — IPs from Anomira's federated network whose confidence\n * score meets the autoBlock.communityThreshold (default 85).\n * Only active when autoBlock.enabled is true (the default).\n *\n * If a customer sets autoBlock.enabled = false, only the manual blocklist is checked.\n */\n isBlocked(ip: string): boolean {\n if (this.disabled) return false;\n\n // Check manual blocklist first (always active)\n if (this.blockedIpCache.has(ip)) return true;\n\n // Check community threat network (active when autoBlock.enabled = true)\n if (this.config.autoBlock.enabled) {\n const threat = this.communityThreatCache.get(ip);\n if (threat && threat.score >= this.config.autoBlock.communityThreshold) return true;\n }\n\n return false;\n }\n\n /**\n * Returns the block reason for an IP — useful for logging or custom responses.\n * Returns null if the IP is not blocked.\n */\n blockReason(ip: string): { source: \"manual\" | \"community\"; score?: number; topAttack?: string } | null {\n if (this.disabled) return null;\n if (this.blockedIpCache.has(ip)) return { source: \"manual\" };\n if (this.config.autoBlock.enabled) {\n const threat = this.communityThreatCache.get(ip);\n if (threat && threat.score >= this.config.autoBlock.communityThreshold) {\n return { source: \"community\", score: threat.score, topAttack: threat.topAttack };\n }\n }\n return null;\n }\n\n /**\n * Fire-and-forget: report a blocked-IP attempt to the ingest server so the\n * dashboard can show that the block is actively working.\n * Called automatically by the Express/Fastify middleware — no manual call needed.\n */\n reportBlockedHit(ip: string, meta: { method: string; url: string; userAgent: string }): void {\n if (this.disabled) return;\n const blockedHitUrl = this.config.ingestUrl.replace(/\\/v1\\/events$/, \"/v1/blocked-hit\");\n fetch(blockedHitUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Authorization\": `Bearer ${this.config.apiKey}`,\n },\n body: JSON.stringify({\n appId: this.config.appId,\n ip,\n method: meta.method,\n endpoint: meta.url,\n userAgent: meta.userAgent,\n ts: Date.now(),\n }),\n }).catch(() => null);\n }\n\n /** Evaluate firewall rules against a request. Returns the matched rule or null. Synchronous. */\n matchFirewallRule(req: {\n url: string;\n body: unknown;\n headers: Record<string, string | undefined>;\n ip: string;\n }): { rule: FirewallRule } | null {\n return this.#matchFirewallRule(req);\n }\n\n track(\n eventName: string,\n data: {\n /**\n * Client IP address. Optional — when omitted or empty the SDK\n * automatically reads it from the active request context set by the\n * Express/Fastify middleware. This means developers calling track()\n * inside a request handler get the correct IP for free, with no extra\n * configuration required.\n *\n * Only pass this explicitly when calling track() outside a request\n * context (e.g. from a background job or a webhook handler that has\n * the IP available separately).\n */\n ip?: string;\n userId?: string;\n meta?: Record<string, unknown>;\n },\n ): void {\n if (this.disabled) return;\n\n // ── IP auto-resolution ────────────────────────────────────────────────────\n // Priority order:\n // 1. Explicitly passed ip (non-empty, non-zero)\n // 2. IP from the active request context (set by Express/Fastify middleware)\n // 3. Fall back to \"0.0.0.0\" so the event is still recorded\n const ctx = requestContext.getStore();\n const resolvedIp =\n (data.ip && data.ip !== \"0.0.0.0\" && data.ip !== \"\")\n ? data.ip\n : (ctx?.ip ?? \"0.0.0.0\");\n\n // Warn in debug mode when an explicitly-passed IP looks like a proxy address.\n // This catches the common mistake of passing `req.ip` behind Nginx, where\n // `req.ip` returns 127.0.0.1 unless Express trust proxy is configured.\n if (this.config.debug && data.ip && isPrivateIp(data.ip)) {\n this._origWarn(\n `[Anomira] Warning: track() received a private/loopback IP \"${data.ip}\" for event \"${eventName}\". ` +\n `Behind a reverse proxy, req.ip is the proxy's address — use sentinel.getClientIp(req) instead.`,\n );\n }\n\n // ── Endpoint auto-resolution ──────────────────────────────────────────────\n // If the caller didn't include an endpoint in meta, inject it from context.\n // This ensures REQUEST SAMPLE shows a real endpoint, not \"POST —\".\n const resolvedMeta: Record<string, unknown> | undefined =\n ctx?.endpoint && !data.meta?.[\"endpoint\"]\n ? { endpoint: ctx.endpoint, method: ctx.method, ...data.meta }\n : data.meta;\n\n const event: SdkEvent = {\n name: eventName,\n ts: Date.now(),\n ip: resolvedIp,\n userId: data.userId,\n meta: resolvedMeta,\n };\n this.buffer.push(event);\n }\n\n /**\n * Track a successful login AND run geo-velocity check.\n * If impossible travel is detected, automatically fires an additional\n * `auth.login.geo_velocity` event with full context.\n *\n * ```ts\n * await sentinel.trackLogin({\n * ip: req.ip,\n * userId: user.id,\n * });\n * ```\n */\n async trackLogin(data: {\n ip?: string;\n userId: string;\n meta?: Record<string, unknown>;\n }): Promise<void> {\n if (this.disabled) return;\n const tsMs = Date.now();\n\n // Resolve IP the same way track() does — from context when not passed\n const ctx = requestContext.getStore();\n const resolvedIp = (data.ip && data.ip !== \"0.0.0.0\") ? data.ip : (ctx?.ip ?? \"0.0.0.0\");\n\n // Record the successful login\n this.track(EventName.LOGIN_SUCCESS, { ...data, ip: resolvedIp, meta: { ...data.meta } });\n\n // Check for geo-velocity if enabled\n if (!this.config.detect.geoVelocity) return;\n\n try {\n const result = await checkGeoVelocity(data.userId, resolvedIp, tsMs, this.config.geoLookupUrl || undefined);\n if (!result) return;\n\n this.track(EventName.GEO_VELOCITY, {\n ip: resolvedIp,\n userId: data.userId,\n meta: {\n distanceKm: result.distanceKm,\n speedKmH: result.speedKmH,\n fromIp: result.from.ip,\n fromCity: result.from.city,\n fromCountry: result.from.country,\n toCity: result.to.city,\n toCountry: result.to.country,\n minutesDiff: Math.round((result.to.tsMs - result.from.tsMs) / 60_000),\n ...data.meta,\n },\n });\n } catch {\n // Geo-velocity check failures are always silent\n }\n }\n\n /**\n * Track phone-based authentication (OTP via SMS, WhatsApp, or call).\n * The ingest service uses this to detect SIM swap patterns:\n * if the same userId authenticates via phone but then appears on a new\n * device/IP shortly after, it's flagged as a suspected SIM swap.\n *\n * ```ts\n * await sentinel.trackPhoneAuth({\n * ip: req.ip,\n * userId: user.id,\n * phone: user.phoneNumber,\n * });\n * ```\n */\n trackPhoneAuth(data: {\n ip?: string;\n userId: string;\n phone: string;\n meta?: Record<string, unknown>;\n }): void {\n if (this.disabled) return;\n this.track(EventName.PHONE_AUTH, {\n ip: data.ip, // track() auto-resolves from context if empty\n userId: data.userId,\n meta: { phone: data.phone, ...data.meta },\n });\n }\n\n /**\n * Send a structured log entry to the Anomira Logs dashboard.\n *\n * ```ts\n * sentinel.log(\"info\", \"User registered\", { userId: user.id });\n * sentinel.log(\"warn\", \"Slow DB query detected\", { queryMs: 1240 });\n * sentinel.log(\"error\", \"Payment failed\", { reason: err.message });\n * ```\n */\n log(\n level: \"debug\" | \"info\" | \"warn\" | \"error\" | \"fatal\",\n message: string,\n meta?: Record<string, unknown> & { service?: string },\n ): void {\n if (this.disabled) return;\n const { service, ...rest } = meta ?? {};\n\n const leaks = scanForLeaks(message);\n if (leaks.length > 0) {\n rest[\"sensitiveLeaks\"] = leaks.map((l) => l.type);\n if (this.config.debug) {\n this._origWarn(\n `[Anomira] ⚠️ Sensitive data in log (${leaks.map((l) => l.label).join(\", \")}): \"${message.slice(0, 60)}…\"`,\n );\n }\n }\n\n this.logBuffer.push({ level, service: service ?? this.config.service, message, meta: rest, ts: Date.now() });\n if (this.config.debug && leaks.length === 0) {\n this._origLog(`[Anomira] log:${level} ${message}`);\n }\n if (this.logBuffer.length >= 50) void this.#flushLogs();\n }\n\n /**\n * Declare your API's known endpoints so Anomira can flag undiscovered\n * traffic as shadow endpoints.\n *\n * Call this once on startup after your routes are registered:\n *\n * ```ts\n * await sentinel.declareEndpoints([\n * { method: \"GET\", path: \"/api/users/:id\", auth: true },\n * { method: \"POST\", path: \"/api/orders\", auth: true },\n * { method: \"GET\", path: \"/api/health\", auth: false },\n * ]);\n * ```\n */\n async declareEndpoints(endpoints: EndpointDeclaration[]): Promise<void> {\n if (this.disabled || endpoints.length === 0) return;\n const url = this.config.ingestUrl.replace(/\\/v1\\/events$/, \"/v1/declare-endpoints\");\n try {\n await fetch(url, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.config.apiKey}`,\n \"Content-Type\": \"application/json\",\n \"User-Agent\": `@anomira/node-sdk/0.1.0`,\n },\n body: JSON.stringify({ appId: this.config.appId, endpoints }),\n signal: AbortSignal.timeout(10_000),\n });\n } catch {\n // Network errors must never crash the host app\n }\n }\n\n /**\n * Flush all pending events immediately.\n * Useful before a graceful shutdown outside of the process lifecycle hooks.\n */\n async flush(): Promise<void> {\n if (this.disabled) return;\n // Clear all refresh timers so they don't fire after shutdown\n if (this.blocklistTimer) { clearInterval(this.blocklistTimer); this.blocklistTimer = null; }\n if (this.firewallTimer) { clearInterval(this.firewallTimer); this.firewallTimer = null; }\n if (this.communityThreatTimer) { clearInterval(this.communityThreatTimer); this.communityThreatTimer = null; }\n if (this.logFlushTimer) { clearInterval(this.logFlushTimer); this.logFlushTimer = null; }\n await Promise.all([this.buffer.flush(), this.#flushLogs()]);\n }\n\n /**\n * Express middleware — auto-instruments all routes.\n *\n * ```ts\n * app.use(sentinel.express());\n * ```\n */\n express() {\n return createExpressMiddleware(this);\n }\n\n /**\n * Fastify plugin — auto-instruments all routes.\n *\n * ```ts\n * await app.register(sentinel.fastify());\n * ```\n */\n fastify() {\n return createFastifyPlugin(this);\n }\n}\n\n// ─── Default extractors ──────────────────────────────────────────────────────\n\n/**\n * Automatic userId extraction — tries every common Node.js auth pattern\n * in priority order so the SDK works with zero configuration for most apps.\n *\n * Tier 1: req.user.*\n * Set by Passport.js, express-jwt v6, Firebase Admin SDK, @fastify/passport,\n * @fastify/jwt, most JWT-verification middleware.\n * Fields tried: id, userId, sub (JWT standard), _id (MongoDB), uid (Firebase),\n * user_id (snake_case), accountId, account_id, customerId, customer_id.\n *\n * Tier 2: req.auth.*\n * express-jwt v7+ moved from req.user to req.auth. Same field list as Tier 1.\n *\n * Tier 3: Direct on req\n * Some custom middleware sets req.userId / req.accountId directly.\n *\n * Tier 4: req.session.*\n * express-session with req.session.userId or req.session.user.id.\n *\n * Tier 5: JWT payload decode from Authorization header ← LAST RESORT\n * Decodes the JWT token in the Authorization: Bearer header WITHOUT verifying\n * the signature (verification is the app's auth middleware's job — not ours).\n * Used purely to extract the userId claim from the payload.\n * Works even when the developer forgot to register their auth middleware before\n * the Anomira middleware, or uses a custom auth system that doesn't set req.user.\n * Claims tried: sub, id, userId, user_id, uid, accountId, account_id.\n *\n * If none of these find a userId, returns undefined — the event is recorded as\n * anonymous traffic and is excluded from per-user behavioral analysis.\n *\n * Customers can always override this with getUserId: (req) => req.myCustomField.\n */\nfunction defaultGetUserId(req: unknown): string | undefined {\n const r = req as Record<string, unknown>;\n\n // ── Tier 1: req.user.* ────────────────────────────────────────────────────\n const user = r[\"user\"] as Record<string, unknown> | undefined;\n if (user && typeof user === \"object\") {\n const id = pickId(user);\n if (id) return id;\n }\n\n // ── Tier 2: req.auth.* (express-jwt v7+) ─────────────────────────────────\n const auth = r[\"auth\"] as Record<string, unknown> | undefined;\n if (auth && typeof auth === \"object\") {\n const id = pickId(auth);\n if (id) return id;\n }\n\n // ── Tier 3: Direct on req ─────────────────────────────────────────────────\n const direct =\n (r[\"userId\"] as string | undefined) ??\n (r[\"user_id\"] as string | undefined) ??\n (r[\"accountId\"] as string | undefined) ??\n (r[\"account_id\"] as string | undefined) ??\n (r[\"customerId\"] as string | undefined);\n if (direct && typeof direct === \"string\") return direct;\n\n // ── Tier 4: req.session.* (express-session) ───────────────────────────────\n const session = r[\"session\"] as Record<string, unknown> | undefined;\n if (session && typeof session === \"object\") {\n const sessionDirect =\n (session[\"userId\"] as string | undefined) ??\n (session[\"user_id\"] as string | undefined);\n if (sessionDirect) return sessionDirect;\n\n const sessionUser = session[\"user\"] as Record<string, unknown> | undefined;\n if (sessionUser && typeof sessionUser === \"object\") {\n const id = pickId(sessionUser);\n if (id) return id;\n }\n }\n\n // ── Tier 5: JWT decode from Authorization header ──────────────────────────\n // Decode without verifying — we only want the userId claim, not to authenticate.\n const headers = r[\"headers\"] as Record<string, string | string[] | undefined> | undefined;\n const rawAuth = headers?.[\"authorization\"];\n const authStr = Array.isArray(rawAuth) ? rawAuth[0] : rawAuth;\n if (authStr?.startsWith(\"Bearer \")) {\n const token = authStr.slice(7);\n try {\n const parts = token.split(\".\");\n if (parts.length === 3) {\n // Base64URL → Base64 (replace - with + and _ with /) then pad to 4-byte boundary\n const b64 = parts[1]!.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padded = b64 + \"=\".repeat((4 - (b64.length % 4)) % 4);\n const payload = JSON.parse(\n Buffer.from(padded, \"base64\").toString(\"utf8\"),\n ) as Record<string, unknown>;\n const jwtId = pickId(payload);\n if (jwtId) return jwtId;\n // Some JWTs wrap user data one level deep: { data: { id: \"...\" } }\n for (const val of Object.values(payload)) {\n if (val && typeof val === \"object\" && !Array.isArray(val)) {\n const nested = pickId(val as Record<string, unknown>);\n if (nested) return nested;\n }\n }\n }\n } catch {\n // Not a valid JWT or decode failed — move on silently\n }\n }\n\n return undefined;\n}\n\n/**\n * Extract the most likely userId field from a plain object.\n * Returns the first non-empty string found among the standard field names,\n * or undefined if nothing useful is found.\n */\nfunction pickId(obj: Record<string, unknown>): string | undefined {\n const id =\n (obj[\"id\"] as string | undefined) ??\n (obj[\"sub\"] as string | undefined) ?? // JWT standard claim\n (obj[\"userId\"] as string | undefined) ??\n (obj[\"user_id\"] as string | undefined) ??\n (obj[\"uid\"] as string | undefined) ?? // Firebase\n (obj[\"_id\"] as string | undefined) ?? // MongoDB\n (obj[\"accountId\"] as string | undefined) ??\n (obj[\"account_id\"] as string | undefined) ??\n (obj[\"customerId\"] as string | undefined) ??\n (obj[\"customer_id\"] as string | undefined);\n // Only return if it's a non-empty string (guards against null, 0, undefined)\n return typeof id === \"string\" && id.length > 0 ? id : undefined;\n}\n\nfunction normalizeIp(raw: string): string {\n if (raw === \"::1\") return \"127.0.0.1\";\n if (raw === \"::ffff:127.0.0.1\") return \"127.0.0.1\";\n if (raw.startsWith(\"::ffff:\")) return raw.slice(7);\n return raw;\n}\n\nfunction isPrivateIp(ip: string): boolean {\n return (\n ip === \"127.0.0.1\" ||\n ip === \"::1\" ||\n ip === \"0.0.0.0\" ||\n ip.startsWith(\"10.\") ||\n ip.startsWith(\"192.168.\") ||\n /^172\\.(1[6-9]|2\\d|3[01])\\./.test(ip)\n );\n}\n\nfunction defaultGetIp(req: unknown): string {\n const r = req as Record<string, unknown>;\n const fwd = r[\"headers\"] as Record<string, string | string[] | undefined> | undefined;\n\n // Pull the first non-empty value from a header (handles comma-lists like XFF)\n function firstHdr(name: string): string | undefined {\n const v = fwd?.[name];\n if (!v) return undefined;\n const s = (Array.isArray(v) ? v[0] : v.split(\",\")[0])?.trim();\n return s || undefined;\n }\n\n // Priority order: CDN/cloud-specific headers first (harder to spoof when behind\n // those services), then standard XFF, then socket fallback.\n const ip =\n firstHdr(\"cf-connecting-ip\") ?? // Cloudflare\n firstHdr(\"true-client-ip\") ?? // Cloudflare Enterprise / Akamai\n firstHdr(\"x-forwarded-for\") ?? // Nginx, AWS ALB, GCP LB, most proxies\n firstHdr(\"x-real-ip\") ?? // Nginx (single-IP alternative to XFF)\n firstHdr(\"fastly-client-ip\") ?? // Fastly CDN\n firstHdr(\"x-client-ip\") ?? // Generic reverse proxies\n firstHdr(\"x-cluster-client-ip\") ?? // Cluster / k8s ingress\n (r[\"socket\"] as { remoteAddress?: string } | undefined)?.remoteAddress ??\n \"0.0.0.0\";\n\n return normalizeIp(ip);\n}\n\n// ─── Fake response sender (shared by honeypot + credential handlers) ─────────\n\nimport type { HoneypotResponse } from \"./honeypot-responses.js\";\n\nfunction sendFakeResponse(res: Record<string, unknown>, fakeResponse: HoneypotResponse): void {\n const allHeaders: Record<string, string> = {\n \"Content-Type\": fakeResponse.contentType,\n ...fakeResponse.headers,\n };\n if (typeof res[\"set\"] === \"function\") {\n (res[\"set\"] as (h: Record<string, string>) => void)(allHeaders);\n } else if (typeof res[\"setHeader\"] === \"function\") {\n for (const [k, v] of Object.entries(allHeaders)) {\n (res[\"setHeader\"] as (k: string, v: string) => void)(k, v);\n }\n }\n if (typeof res[\"status\"] === \"function\") {\n (res[\"status\"] as (c: number) => unknown)(fakeResponse.statusCode);\n } else {\n res[\"statusCode\"] = fakeResponse.statusCode;\n }\n if (typeof res[\"end\"] === \"function\") {\n (res[\"end\"] as (b: string) => void)(fakeResponse.body);\n }\n}\n\n// ─── Lazy imports for middleware factories ───────────────────────────────────\n// Imported lazily so the SDK has no hard dependency on express/fastify at runtime\n\nfunction isLoopbackIp(ip: string): boolean {\n return ip === \"127.0.0.1\" || ip === \"0.0.0.0\" || ip === \"::1\";\n}\n\nfunction createExpressMiddleware(client: AnomiraClient) {\n let ipCheckCount = 0;\n let ipLoopCount = 0;\n let ipWarnFired = false;\n\n let userIdCheckCount = 0;\n let userIdMissCount = 0;\n let userIdWarnFired = false;\n\n return async function sentinelMiddleware(\n req: Record<string, unknown>,\n res: Record<string, unknown>,\n next: () => void,\n ) {\n const startMs = Date.now();\n const ip = client.config.getIp(req);\n\n // Warn once if the first 20 requests are consistently loopback — almost\n // always means the reverse proxy isn't forwarding X-Forwarded-For.\n if (!ipWarnFired && ipCheckCount < 20) {\n ipCheckCount++;\n if (isLoopbackIp(ip)) ipLoopCount++;\n if (ipCheckCount === 20 && ipLoopCount >= 16) {\n ipWarnFired = true;\n console.warn(\n \"[Anomira] WARNING: client IP not captured on 80%+ of requests.\\n\" +\n \" Your app is likely behind a reverse proxy (Nginx, Cloudflare, AWS ALB)\\n\" +\n \" that is not forwarding client IP headers. Alerts will have no IP attribution.\\n\\n\" +\n \" Nginx fix: proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\\n\" +\n \" proxy_set_header X-Real-IP $remote_addr;\\n\" +\n \" Express fix: app.set('trust proxy', 1);\\n\" +\n \" Fastify fix: Fastify({ trustProxy: true })\\n\" +\n \" Docs: https://docs.anomira.io/sdk/ip-capture\"\n );\n }\n }\n\n // ── Block check — synchronous, zero latency ──\n if (client.isBlocked(ip)) {\n const method_ = (req[\"method\"] as string | undefined)?.toUpperCase() ?? \"GET\";\n const url_ = (req[\"originalUrl\"] as string | undefined) ?? (req[\"url\"] as string | undefined) ?? \"/\";\n const ua_ = (req[\"headers\"] as Record<string, string | undefined> | undefined)?.[\"user-agent\"] ?? \"\";\n const reason_ = client.blockReason(ip);\n client.reportBlockedHit(ip, { method: method_, url: url_, userAgent: ua_ });\n\n // Emit a tracking event so the community auto-block is visible in the dashboard\n if (reason_?.source === \"community\") {\n client.track(\"http.community_threat_blocked\", {\n ip,\n meta: {\n endpoint: url_,\n method: method_,\n score: reason_.score,\n topAttack: reason_.topAttack,\n source: \"anomira_network\",\n },\n });\n }\n\n const res_ = res as Record<string, unknown>;\n if (typeof res_[\"status\"] === \"function\") {\n (res_[\"status\"] as (c: number) => unknown)(403);\n } else {\n res_[\"statusCode\"] = 403;\n }\n if (typeof res_[\"end\"] === \"function\") (res_[\"end\"] as (b: string) => void)('{\"error\":\"Forbidden\"}');\n return;\n }\n\n const userId = client.config.getUserId(req);\n const method = (req[\"method\"] as string | undefined)?.toUpperCase() ?? \"GET\";\n const url = (req[\"originalUrl\"] as string | undefined) ?? (req[\"url\"] as string | undefined) ?? \"/\";\n\n const headers = req[\"headers\"] as Record<string, string | string[] | undefined> | undefined;\n const headersStr = headers as Record<string, string | undefined> | undefined;\n const ua = (typeof headers?.[\"user-agent\"] === \"string\" ? headers[\"user-agent\"] : \"\") ?? \"\";\n const agentInfo = detectAgent(headers ?? {});\n const fingerprint = computeBrowserFingerprint(headers ?? {}, ua);\n const h2settings = extractHttp2Settings(req);\n const upstreamTls = extractUpstreamTls(headers ?? {});\n\n // ── Canary token detection ─────────────────────────────────────────────────\n // Scan the Authorization header for canary JWTs (JWT `jti` claim contains\n // the canary token). If found, fire a CRITICAL canary_triggered event —\n // this means the attacker obtained a credential from our honeypot and is\n // now using it against the real API.\n if (client[\"canaryTokenCache\"] && (client[\"canaryTokenCache\"] as Set<string>).size > 0) {\n const authHeader = (typeof headers?.[\"authorization\"] === \"string\" ? headers[\"authorization\"] : \"\") ?? \"\";\n if (authHeader.startsWith(\"Bearer \")) {\n const bearerToken = authHeader.slice(7);\n // Check if the bearer token itself IS a canary token\n if ((client[\"canaryTokenCache\"] as Set<string>).has(bearerToken)) {\n client.track(\"http.canary.triggered\", {\n ip, userId,\n meta: { endpoint: url, method, token: bearerToken.slice(0, 16), source: \"authorization_header\" },\n });\n }\n // Also decode JWT and check jti/kid against canary cache\n try {\n const parts = bearerToken.split(\".\");\n if (parts.length === 3) {\n const b64h = (parts[0] ?? \"\").replace(/-/g, \"+\").replace(/_/g, \"/\");\n const hdr = JSON.parse(Buffer.from(b64h + \"==\", \"base64\").toString()) as Record<string, unknown>;\n const b64p = (parts[1] ?? \"\").replace(/-/g, \"+\").replace(/_/g, \"/\");\n const pay = JSON.parse(Buffer.from(b64p + \"==\", \"base64\").toString()) as Record<string, unknown>;\n const jti = (pay[\"jti\"] as string | undefined) ?? \"\";\n // jti is the raw canary token hex (32 chars) — check directly against the cache\n const cache = client[\"canaryTokenCache\"] as Set<string>;\n if (jti && cache.has(jti)) {\n client.track(\"http.canary.triggered\", {\n ip, userId,\n meta: { endpoint: url, method, token: jti.slice(0, 16), source: \"canary_jwt\" },\n });\n }\n }\n } catch { /* not a valid JWT */ }\n }\n }\n\n // ── Enhanced honeypot interception ─────────────────────────────────────────\n // If this request matches a registered honeypot path, return realistic fake\n // content INSTEAD of forwarding to the customer's route handlers.\n // This is done BEFORE next() so the customer's routes are never involved.\n const urlPath = url.split(\"?\")[0]?.toLowerCase() ?? url.toLowerCase();\n const isHoneypot = (client[\"honeypotPaths\"] as Set<string>).size > 0 &&\n [...(client[\"honeypotPaths\"] as Set<string>)].some((hp) =>\n urlPath === hp || urlPath.startsWith(hp + \"/\") || urlPath.startsWith(hp + \".\")\n );\n\n if (isHoneypot) {\n const honeypotType = detectHoneypotType(url);\n const callbackBase = client.config.ingestUrl.replace(/\\/v1\\/events$/, \"\");\n const orgId = \"\";\n\n // ── POST to admin portal: capture credential attempt ───────────────────\n // When an attacker fills in the fake login form and submits, we capture\n // what credentials they tried (username + password hint) as threat intel.\n // We store the username in full (useful to know which accounts are targeted)\n // and the password length + first 2 chars so we can identify patterns\n // (e.g., \"admin\", \"password123\", empty) without storing the full credential.\n if (method === \"POST\" && honeypotType === \"admin_portal\") {\n const body_ = req[\"body\"] as Record<string, unknown> | undefined;\n const username = String(\n body_?.[\"username\"] ?? body_?.[\"email\"] ?? body_?.[\"user\"] ?? body_?.[\"login\"] ?? \"\"\n );\n const password = String(\n body_?.[\"password\"] ?? body_?.[\"pass\"] ?? body_?.[\"pwd\"] ?? body_?.[\"passwd\"] ?? \"\"\n );\n\n if (username || password) {\n // Password hint: first 2 chars + \"***\" + length — enough to spot patterns\n const passwordHint = password.length > 0\n ? `${password.slice(0, 2)}***[${password.length}]`\n : \"(empty)\";\n\n client.track(\"http.honeypot.credential_attempt\", {\n ip, userId,\n meta: {\n endpoint: url,\n method,\n userAgent: ua,\n honeypotType,\n username,\n passwordHint,\n // Hidden _token field — if this is the canary token we issued\n // in the GET response, we know this is the same attacker session\n formToken: String(body_?.[\"_token\"] ?? \"\"),\n },\n });\n }\n\n // Return \"invalid credentials\" response — lures further attempts\n const failed = makeAdminLoginFailed();\n sendFakeResponse(res, failed);\n return;\n }\n\n // ── GET or other method: serve the fake content ────────────────────────\n const canaryToken = randomBytes(16).toString(\"hex\");\n const fakeResponse = generateHoneypotResponse(honeypotType, canaryToken, callbackBase, orgId);\n\n client.track(\"http.honeypot.hit\", {\n ip, userId,\n meta: {\n endpoint: url,\n method,\n userAgent: ua,\n honeypotType,\n canaryToken,\n responseType: \"enhanced\",\n },\n });\n\n sendFakeResponse(res, fakeResponse);\n return;\n }\n\n // ── Firewall rule evaluation ──\n const fwMatch = client.matchFirewallRule({ url, body: req[\"body\"], headers: headersStr ?? {}, ip });\n if (fwMatch) {\n client.track(\"http.firewall.\" + fwMatch.rule.action, {\n ip, userId,\n meta: { url, method, ruleId: fwMatch.rule.id, attackType: fwMatch.rule.attackType },\n });\n if (fwMatch.rule.action === \"block\") {\n const res_ = res as Record<string, unknown>;\n if (typeof res_[\"status\"] === \"function\") (res_[\"status\"] as (c: number) => unknown)(403);\n else res_[\"statusCode\"] = 403;\n if (typeof res_[\"end\"] === \"function\") (res_[\"end\"] as (b: string) => void)('{\"error\":\"Blocked by firewall rule\"}');\n return;\n }\n }\n\n // ── Detect path traversal before the request reaches the handler ──\n if (client.config.detect.pathTraversal && (url.includes(\"../\") || url.includes(\"..%2F\"))) {\n client.track(EventName.PATH_TRAVERSAL, { ip, userId, meta: { url, method } });\n }\n\n // ── Detect XSS in body (only on POST/PUT/PATCH) ──\n if (client.config.detect.xss && [\"POST\", \"PUT\", \"PATCH\"].includes(method)) {\n const body = JSON.stringify(req[\"body\"]);\n if (/<script|javascript:|on\\w+=/i.test(body)) {\n client.track(EventName.XSS_DETECTED, { ip, userId, meta: { url, method } });\n }\n }\n\n // ── Hook into response finish ──\n const onFinish = () => {\n // Re-read userId here — auth middleware has now run, so req.user is populated.\n // This is the fix for apps that mount Anomira before their auth middleware,\n // which is the standard setup order (block check needs to run first).\n const lateUserId = client.config.getUserId(req);\n\n // Warn once if userId is STILL missing even after the full request cycle.\n // This means neither auto-detection nor auth middleware populated it.\n if (!userIdWarnFired && userIdCheckCount < 20) {\n userIdCheckCount++;\n if (!lateUserId) userIdMissCount++;\n if (userIdCheckCount === 20 && userIdMissCount >= 18) {\n userIdWarnFired = true;\n console.warn(\n \"[Anomira] WARNING: userId not captured on 90%+ of requests.\\n\" +\n \" EWS Evidence Package, geo-velocity, and account takeover detection\\n\" +\n \" silently stop working without it. The SDK tried 5 auto-detection tiers\\n\" +\n \" (Passport / express-jwt, req.auth, direct req.userId, session, JWT Bearer)\\n\" +\n \" — none matched your auth setup.\\n\\n\" +\n \" Fix: pass a getUserId resolver that matches your auth middleware:\\n\" +\n \" new Anomira({ ..., getUserId: (req) => req.user?.id })\"\n );\n }\n }\n\n const status = (res[\"statusCode\"] as number | undefined) ?? 0;\n const latencyMs = Date.now() - startMs;\n const getHeader = (res as Record<string, unknown>)[\"getHeader\"];\n const bytes = typeof getHeader === \"function\"\n ? parseInt((getHeader as (h: string) => string | undefined).call(res, \"content-length\") ?? \"0\", 10) || 0\n : 0;\n\n // ── PII / sensitive-data detection ────────────────────────────────────\n // Runs after response is sent — zero latency impact on the client.\n // Layer 1: field name matching (O(keys) lookup against pre-built Map)\n // Layer 2: value regex matching (only string values ≤ 200 chars)\n const rawBody = req[\"body\"];\n const piiFields: string[] = [];\n const piiPatterns: string[] = [];\n const piiCatSet = new Set<string>();\n\n if (rawBody != null && typeof rawBody === \"object\" && !Array.isArray(rawBody)) {\n const body = rawBody as Record<string, unknown>;\n for (const key of Object.keys(body)) {\n const cat = FIELD_CATEGORY_MAP.get(key.toLowerCase());\n if (cat) { piiFields.push(key.toLowerCase()); piiCatSet.add(cat); }\n }\n for (const val of Object.values(body)) {\n if (typeof val !== \"string\" || val.length > 200) continue;\n for (const { name, category, re } of VALUE_PATTERNS) {\n if (!piiPatterns.includes(name) && re.test(val)) {\n piiPatterns.push(name); piiCatSet.add(category);\n }\n }\n }\n }\n const piiCategories = piiCatSet.size > 0 ? [...piiCatSet] : undefined;\n\n client.track(EventName.REQUEST, {\n ip, userId: lateUserId,\n meta: {\n method, endpoint: url, status, latencyMs, userAgent: ua, bytes,\n ...(piiFields.length > 0 ? { piiFields } : {}),\n ...(piiPatterns.length > 0 ? { piiPatterns } : {}),\n ...(piiCategories ? { piiCategories } : {}),\n _fp: fingerprint.score,\n _fpSig: fingerprint.signals.join(\",\"),\n ...(fingerprint.knownClient ? { _fpClient: fingerprint.knownClient } : {}),\n ...(h2settings ? {\n _h2Window: h2settings.initialWindowSize,\n _h2HdrTable: h2settings.headerTableSize,\n _h2Score: h2settings.score,\n _h2Signals: h2settings.signals.join(\",\"),\n } : {}),\n ...(upstreamTls.ja3 ? { _ja3: upstreamTls.ja3 } : {}),\n ...(upstreamTls.ja4 ? { _ja4: upstreamTls.ja4 } : {}),\n ...(agentInfo.isAgent ? {\n agentDetected: true,\n agentType: agentInfo.agentType,\n agentName: agentInfo.agentName,\n agentConfidence: agentInfo.confidence,\n mcpSessionId: agentInfo.sessionId,\n isMcp: agentInfo.isMcp,\n agentSignals: agentInfo.signals.join(\",\"),\n } : {}),\n },\n });\n\n if (agentInfo.isAgent) {\n client.track(\"http.agent_detected\", {\n ip, userId: lateUserId,\n meta: {\n endpoint: url,\n method,\n agentType: agentInfo.agentType,\n agentName: agentInfo.agentName,\n agentConfidence: agentInfo.confidence,\n mcpSessionId: agentInfo.sessionId,\n isMcp: agentInfo.isMcp,\n agentSignals: agentInfo.signals.join(\",\"),\n status,\n userAgent: ua,\n },\n });\n }\n\n if (client.config.detect.rateAbuse && status === 429) {\n client.track(EventName.RATE_LIMIT, { ip, userId: lateUserId, meta: { url, method, statusCode: status } });\n }\n\n if (client.config.detect.bruteForce && status === 401) {\n if (/\\/(login|signin|auth|token|session)/i.test(url)) {\n client.track(EventName.LOGIN_FAILED, { ip, userId: lateUserId, meta: { url, method, statusCode: status } });\n }\n }\n\n if (client.config.detect.scanDetection && status === 404) {\n const looksLikeScanner = !ua || /curl|wget|python|go-http|nuclei|sqlmap|nikto/i.test(ua);\n if (looksLikeScanner) {\n client.track(EventName.SCAN_DETECTED, { ip, userId: lateUserId, meta: { url, method, userAgent: ua } });\n }\n }\n\n cleanup();\n };\n\n const cleanup = () => {\n (res as unknown as { off: (e: string, fn: () => void) => void }).off?.(\"finish\", onFinish);\n };\n\n (res as unknown as { on: (e: string, fn: () => void) => void }).on?.(\"finish\", onFinish);\n\n // Run the downstream handler inside a context so console logs get endpoint tagged\n requestContext.run({ endpoint: url, method, ip }, next);\n };\n}\n\nfunction createFastifyPlugin(client: AnomiraClient) {\n let ipCheckCount = 0;\n let ipLoopCount = 0;\n let ipWarnFired = false;\n\n let userIdCheckCount = 0;\n let userIdMissCount = 0;\n let userIdWarnFired = false;\n\n return async function sentinelFastifyPlugin(\n fastify: {\n addHook: (\n event: string,\n fn: (req: Record<string, unknown>, reply: Record<string, unknown>) => void,\n ) => void;\n },\n ) {\n fastify.addHook(\"onRequest\", (req, reply) => {\n const ip = client.config.getIp(req);\n\n if (!ipWarnFired && ipCheckCount < 20) {\n ipCheckCount++;\n if (isLoopbackIp(ip)) ipLoopCount++;\n if (ipCheckCount === 20 && ipLoopCount >= 16) {\n ipWarnFired = true;\n console.warn(\n \"[Anomira] WARNING: client IP not captured on 80%+ of requests.\\n\" +\n \" Your app is likely behind a reverse proxy (Nginx, Cloudflare, AWS ALB)\\n\" +\n \" that is not forwarding client IP headers. Alerts will have no IP attribution.\\n\\n\" +\n \" Nginx fix: proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\\n\" +\n \" proxy_set_header X-Real-IP $remote_addr;\\n\" +\n \" Fastify fix: Fastify({ trustProxy: true })\\n\" +\n \" Docs: https://docs.anomira.io/sdk/ip-capture\"\n );\n }\n }\n const url_ = (req[\"url\"] as string | undefined) ?? \"/\";\n const method_ = (req[\"method\"] as string | undefined)?.toUpperCase() ?? \"GET\";\n const hdrs_ = (req as Record<string, unknown>)[\"headers\"] as Record<string, string | undefined> | undefined;\n\n // ── Block check — synchronous, zero latency ──\n if (client.isBlocked(ip)) {\n const reason_ = client.blockReason(ip);\n client.reportBlockedHit(ip, { method: method_, url: url_, userAgent: hdrs_?.[\"user-agent\"] ?? \"\" });\n\n if (reason_?.source === \"community\") {\n client.track(\"http.community_threat_blocked\", {\n ip,\n meta: {\n endpoint: url_,\n method: method_,\n score: reason_.score,\n topAttack: reason_.topAttack,\n source: \"anomira_network\",\n },\n });\n }\n\n const rep = reply as Record<string, unknown>;\n if (typeof rep[\"code\"] === \"function\") {\n const chained = (rep[\"code\"] as (c: number) => Record<string, unknown>)(403);\n if (chained && typeof chained[\"send\"] === \"function\") {\n (chained[\"send\"] as (b: unknown) => void)({ error: \"Forbidden\" });\n }\n }\n return;\n }\n\n const url = (req[\"url\"] as string | undefined) ?? \"/\";\n const method = (req[\"method\"] as string | undefined)?.toUpperCase() ?? \"GET\";\n const userId = client.config.getUserId(req);\n\n if (client.config.detect.pathTraversal && (url.includes(\"../\") || url.includes(\"..%2F\"))) {\n client.track(EventName.PATH_TRAVERSAL, { ip, userId, meta: { url, method } });\n }\n });\n\n fastify.addHook(\"preHandler\", (req, reply) => {\n const ip = client.config.getIp(req);\n const url = (req[\"url\"] as string | undefined) ?? \"/\";\n const method = (req[\"method\"] as string | undefined)?.toUpperCase() ?? \"GET\";\n const userId = client.config.getUserId(req);\n const headers = (req as Record<string, unknown>)[\"headers\"] as Record<string, string | undefined> | undefined;\n\n // ── Firewall rule evaluation ──\n const fwMatch = client.matchFirewallRule({ url, body: (req as Record<string, unknown>)[\"body\"], headers: headers ?? {}, ip });\n if (fwMatch) {\n client.track(\"http.firewall.\" + fwMatch.rule.action, {\n ip, userId,\n meta: { url, method, ruleId: fwMatch.rule.id, attackType: fwMatch.rule.attackType },\n });\n if (fwMatch.rule.action === \"block\") {\n const rep = reply as Record<string, unknown>;\n if (typeof rep[\"code\"] === \"function\") {\n const chained = (rep[\"code\"] as (c: number) => Record<string, unknown>)(403);\n if (chained && typeof chained[\"send\"] === \"function\") {\n (chained[\"send\"] as (b: unknown) => void)({ error: \"Blocked by firewall rule\" });\n }\n }\n return;\n }\n }\n\n if (client.config.detect.xss && [\"POST\", \"PUT\", \"PATCH\"].includes(method)) {\n const body = JSON.stringify((req as Record<string, unknown>)[\"body\"]);\n if (/<script|javascript:|on\\w+=/i.test(body)) {\n client.track(EventName.XSS_DETECTED, { ip, userId, meta: { url, method } });\n }\n }\n });\n\n fastify.addHook(\"onResponse\", (req, reply) => {\n const ip = client.config.getIp(req);\n const url = (req[\"url\"] as string | undefined) ?? \"/\";\n const method = (req[\"method\"] as string | undefined)?.toUpperCase() ?? \"GET\";\n // onResponse fires after all hooks — auth has run, req.user is populated.\n const userId = client.config.getUserId(req);\n const status = (reply[\"statusCode\"] as number | undefined) ?? 0;\n\n // Warn once if userId is still missing after the full request cycle.\n if (!userIdWarnFired && userIdCheckCount < 20) {\n userIdCheckCount++;\n if (!userId) userIdMissCount++;\n if (userIdCheckCount === 20 && userIdMissCount >= 18) {\n userIdWarnFired = true;\n console.warn(\n \"[Anomira] WARNING: userId not captured on 90%+ of requests.\\n\" +\n \" EWS Evidence Package, geo-velocity, and account takeover detection\\n\" +\n \" silently stop working without it. The SDK tried 5 auto-detection tiers\\n\" +\n \" (Passport / @fastify/jwt, req.auth, direct req.userId, session, JWT Bearer)\\n\" +\n \" — none matched your auth setup.\\n\\n\" +\n \" Fix: pass a getUserId resolver that matches your auth middleware:\\n\" +\n \" new Anomira({ ..., getUserId: (req) => (req as any).user?.id })\"\n );\n }\n }\n const headers = (req as Record<string, unknown>)[\"headers\"] as Record<string, string | string[] | undefined> | undefined;\n const ua = (typeof headers?.[\"user-agent\"] === \"string\" ? headers[\"user-agent\"] : \"\") ?? \"\";\n const latencyMs = (reply as Record<string, unknown>)[\"elapsedTime\"] as number | undefined ?? 0;\n const agentInfo = detectAgent(headers ?? {});\n const fingerprint = computeBrowserFingerprint(headers ?? {}, ua);\n const h2settings = extractHttp2Settings(req as Record<string, unknown>);\n const upstreamTls = extractUpstreamTls(headers ?? {});\n\n // Always emit HTTP access log entry for the Events dashboard.\n client.track(EventName.REQUEST, {\n ip, userId,\n meta: {\n method, endpoint: url, status, latencyMs: Math.round(latencyMs), userAgent: ua, bytes: 0,\n _fp: fingerprint.score,\n _fpSig: fingerprint.signals.join(\",\"),\n ...(fingerprint.knownClient ? { _fpClient: fingerprint.knownClient } : {}),\n ...(h2settings ? {\n _h2Window: h2settings.initialWindowSize,\n _h2HdrTable: h2settings.headerTableSize,\n _h2Score: h2settings.score,\n _h2Signals: h2settings.signals.join(\",\"),\n } : {}),\n ...(upstreamTls.ja3 ? { _ja3: upstreamTls.ja3 } : {}),\n ...(upstreamTls.ja4 ? { _ja4: upstreamTls.ja4 } : {}),\n ...(agentInfo.isAgent ? {\n agentDetected: true,\n agentType: agentInfo.agentType,\n agentName: agentInfo.agentName,\n agentConfidence: agentInfo.confidence,\n mcpSessionId: agentInfo.sessionId,\n isMcp: agentInfo.isMcp,\n agentSignals: agentInfo.signals.join(\",\"),\n } : {}),\n },\n });\n\n if (agentInfo.isAgent) {\n client.track(\"http.agent_detected\", {\n ip, userId,\n meta: {\n endpoint: url,\n method,\n agentType: agentInfo.agentType,\n agentName: agentInfo.agentName,\n agentConfidence: agentInfo.confidence,\n mcpSessionId: agentInfo.sessionId,\n isMcp: agentInfo.isMcp,\n agentSignals: agentInfo.signals.join(\",\"),\n status,\n userAgent: ua,\n },\n });\n }\n\n if (client.config.detect.rateAbuse && status === 429) {\n client.track(EventName.RATE_LIMIT, { ip, userId, meta: { url, method, statusCode: status } });\n }\n\n if (client.config.detect.bruteForce && status === 401 && /\\/(login|signin|auth|token)/i.test(url)) {\n client.track(EventName.LOGIN_FAILED, { ip, userId, meta: { url, method, statusCode: status } });\n }\n\n if (client.config.detect.scanDetection && status === 404) {\n if (!ua || /curl|wget|python|go-http|nuclei|sqlmap|nikto/i.test(ua)) {\n client.track(EventName.SCAN_DETECTED, { ip, userId, meta: { url, method, userAgent: ua } });\n }\n }\n\n if (client.config.detect.bruteForce && status === 200 && /\\/(login|signin|auth|token)/i.test(url)) {\n if (userId) {\n void client.trackLogin({ ip, userId, meta: { url, method } });\n }\n }\n });\n };\n}\n","/**\n * Typed Express middleware — exported separately for projects that want\n * to import the middleware type explicitly.\n *\n * Most users will use `sentinel.express()` instead of importing this directly.\n */\n\nimport type { Request, Response, NextFunction } from \"express\";\nimport type { AnomiraClient } from \"../client.js\";\nimport { EventName } from \"../types.js\";\n\n/**\n * Returns fully-typed Express middleware.\n *\n * ```ts\n * import express from \"express\";\n * import { Anomira } from \"@sentinelapi/node-sdk\";\n *\n * const app = express();\n * const sentinel = new Anomira({ apiKey: \"...\", appId: \"...\" });\n *\n * app.use(sentinel.express()); // auto-instrument all routes\n *\n * // Or import the typed version directly:\n * import { createExpressMiddleware } from \"@sentinelapi/node-sdk/middleware/express\";\n * app.use(createExpressMiddleware(sentinel));\n * ```\n */\nexport function createExpressMiddleware(client: AnomiraClient) {\n return function sentinelMiddleware(req: Request, res: Response, next: NextFunction): void {\n const ip = client.config.getIp(req as unknown);\n const userId = client.config.getUserId(req as unknown);\n const { method, originalUrl: url } = req;\n\n // ── Block check — synchronous in-memory lookup, zero latency ──────────\n if (client.isBlocked(ip)) {\n client.reportBlockedHit(ip, { method, url, userAgent: req.headers[\"user-agent\"] ?? \"\" });\n res.status(403).json({ error: \"Forbidden\" });\n return;\n }\n\n // ── Path traversal check (pre-handler) ────────────────────────────────\n if (client.config.detect.pathTraversal && (url.includes(\"../\") || url.includes(\"..%2F\"))) {\n client.track(EventName.PATH_TRAVERSAL, {\n ip, userId,\n meta: { url, method },\n });\n }\n\n // ── XSS body check (pre-handler) ──────────────────────────────────────\n if (client.config.detect.xss && [\"POST\", \"PUT\", \"PATCH\"].includes(method)) {\n try {\n const body = JSON.stringify(req.body as unknown);\n if (/<script|javascript:|on\\w+=/i.test(body)) {\n client.track(EventName.XSS_DETECTED, {\n ip, userId,\n meta: { url, method },\n });\n }\n } catch {\n // body may not be parseable yet — ignore\n }\n }\n\n // ── Response-finish hook (post-handler) ───────────────────────────────\n const onFinish = () => {\n res.off(\"finish\", onFinish);\n const { statusCode } = res;\n\n if (client.config.detect.rateAbuse && statusCode === 429) {\n client.track(EventName.RATE_LIMIT, {\n ip, userId,\n meta: { url, method, statusCode },\n });\n }\n\n if (client.config.detect.bruteForce && statusCode === 401) {\n if (/\\/(login|signin|auth|token|session)/i.test(url)) {\n client.track(EventName.LOGIN_FAILED, {\n ip, userId,\n meta: { url, method, statusCode },\n });\n }\n }\n\n if (client.config.detect.scanDetection && statusCode === 404) {\n const ua = req.headers[\"user-agent\"] ?? \"\";\n if (!ua || /curl|wget|python|go-http|nuclei|sqlmap|nikto/i.test(ua)) {\n client.track(EventName.SCAN_DETECTED, {\n ip, userId,\n meta: { url, method, userAgent: ua },\n });\n }\n }\n\n // Auto geo-velocity on successful login\n if (\n client.config.detect.geoVelocity &&\n statusCode === 200 &&\n /\\/(login|signin|auth|token)/i.test(url) &&\n method === \"POST\"\n ) {\n // userId may now be available after auth (read from req.user set by auth middleware)\n const resolvedUserId = client.config.getUserId(req as unknown);\n if (resolvedUserId) {\n void client.trackLogin({ ip, userId: resolvedUserId, meta: { url } });\n }\n }\n };\n\n res.on(\"finish\", onFinish);\n next();\n };\n}\n","/**\n * Typed Fastify plugin — exported separately for projects that want\n * to import the plugin type explicitly.\n *\n * Most users will use `sentinel.fastify()` instead of importing this directly.\n */\n\nimport type { FastifyPluginAsync, FastifyRequest, FastifyReply } from \"fastify\";\nimport type { AnomiraClient } from \"../client.js\";\nimport { EventName } from \"../types.js\";\n\n/**\n * Returns a Fastify plugin for auto-instrumentation.\n *\n * ```ts\n * import Fastify from \"fastify\";\n * import { Anomira } from \"@sentinelapi/node-sdk\";\n *\n * const app = Fastify();\n * const sentinel = new Anomira({ apiKey: \"...\", appId: \"...\" });\n *\n * await app.register(sentinel.fastify());\n * ```\n */\nexport function createFastifyPlugin(client: AnomiraClient): FastifyPluginAsync {\n return async function sentinelPlugin(fastify) {\n\n // ── onRequest: block check + path traversal ───────────────────────────\n fastify.addHook(\"onRequest\", async (req: FastifyRequest, reply: FastifyReply) => {\n const ip = client.config.getIp(req);\n const userId = client.config.getUserId(req);\n const url = req.url;\n const method = req.method.toUpperCase();\n\n // ── Block check — synchronous in-memory lookup, zero latency ────────\n if (client.isBlocked(ip)) {\n client.reportBlockedHit(ip, { method, url, userAgent: req.headers[\"user-agent\"] ?? \"\" });\n return reply.code(403).send({ error: \"Forbidden\" });\n }\n\n if (client.config.detect.pathTraversal && (url.includes(\"../\") || url.includes(\"..%2F\"))) {\n client.track(EventName.PATH_TRAVERSAL, { ip, userId, meta: { url, method } });\n }\n });\n\n // ── preHandler: XSS body check ────────────────────────────────────────\n fastify.addHook(\"preHandler\", async (req: FastifyRequest, _reply: FastifyReply) => {\n const ip = client.config.getIp(req);\n const userId = client.config.getUserId(req);\n const url = req.url;\n const method = req.method.toUpperCase();\n\n if (client.config.detect.xss && [\"POST\", \"PUT\", \"PATCH\"].includes(method)) {\n try {\n const body = JSON.stringify(req.body);\n if (/<script|javascript:|on\\w+=/i.test(body)) {\n client.track(EventName.XSS_DETECTED, { ip, userId, meta: { url, method } });\n }\n } catch { /* ignore */ }\n }\n });\n\n // ── onResponse: status-code based detection ───────────────────────────\n fastify.addHook(\"onResponse\", async (req: FastifyRequest, reply: FastifyReply) => {\n const ip = client.config.getIp(req);\n const userId = client.config.getUserId(req);\n const url = req.url;\n const method = req.method.toUpperCase();\n const statusCode = reply.statusCode;\n\n if (client.config.detect.rateAbuse && statusCode === 429) {\n client.track(EventName.RATE_LIMIT, { ip, userId, meta: { url, method, statusCode } });\n }\n\n if (client.config.detect.bruteForce && statusCode === 401) {\n if (/\\/(login|signin|auth|token|session)/i.test(url)) {\n client.track(EventName.LOGIN_FAILED, { ip, userId, meta: { url, method, statusCode } });\n }\n }\n\n if (client.config.detect.scanDetection && statusCode === 404) {\n const ua = req.headers[\"user-agent\"] ?? \"\";\n if (!ua || /curl|wget|python|go-http|nuclei|sqlmap|nikto/i.test(ua)) {\n client.track(EventName.SCAN_DETECTED, { ip, userId, meta: { url, method, userAgent: ua } });\n }\n }\n\n // Auto geo-velocity on successful POST login\n if (\n client.config.detect.geoVelocity &&\n statusCode === 200 &&\n method === \"POST\" &&\n /\\/(login|signin|auth|token)/i.test(url)\n ) {\n const resolvedUserId = client.config.getUserId(req);\n if (resolvedUserId) {\n void client.trackLogin({ ip, userId: resolvedUserId, meta: { url } });\n }\n }\n });\n };\n}\n"]}
|