@anomira/node-sdk 0.1.6 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/buffer.ts","../src/geo-velocity.ts","../src/sensitive.ts","../src/types.ts","../src/client.ts","../src/middleware/express.ts","../src/middleware/fastify.ts"],"names":["AsyncLocalStorage","createExpressMiddleware","createFastifyPlugin"],"mappings":"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","file":"index.cjs","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(\"[SentinelAPI]\", ...args);\n }\n\n private warn(...args: unknown[]): void {\n console.warn(\"[SentinelAPI]\", ...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 SentinelAPI 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 SentinelAPI 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 SentinelAPI 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 keys and certificates ───────────────────────────────────\n {\n type: \"private_key\",\n label: \"Private Key\",\n regex: /-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/,\n },\n {\n type: \"certificate\",\n label: \"Certificate / Public Key\",\n regex: /-----BEGIN CERTIFICATE-----/,\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 starting with 0)\n // Require a non-digit boundary on both sides to reduce false positives\n type: \"bvn\",\n label: \"BVN / NIN\",\n regex: /(?<!\\d)[1-9]\\d{10}(?!\\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 SentinelConfig {\n /** SDK API key — get this from the SentinelAPI dashboard */\n apiKey: string;\n\n /** Your app ID from the SentinelAPI 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 SentinelAPI 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 SentinelAPI 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// ─── 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\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","import { AsyncLocalStorage } from \"node:async_hooks\";\nimport { EventBuffer } from \"./buffer.js\";\nimport { checkGeoVelocity } from \"./geo-velocity.js\";\nimport { scanForLeaks } from \"./sensitive.js\";\nimport type { SentinelConfig, SdkEvent, FirewallRule } from \"./types.js\";\nimport { EventName } from \"./types.js\";\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 * SentinelClient — the core SDK object.\n *\n * Instantiate once and reuse across your application:\n *\n * ```ts\n * import { SentinelAPI } from \"@anomira/node-sdk\";\n *\n * export const sentinel = new SentinelAPI({\n * apiKey: process.env.SENTINEL_API_KEY!,\n * appId: process.env.SENTINEL_APP_ID!,\n * });\n * ```\n */\nexport class SentinelClient {\n readonly config: Required<Omit<SentinelConfig, \"getUserId\" | \"getIp\" | \"detect\">> & {\n getUserId: NonNullable<SentinelConfig[\"getUserId\"]>;\n getIp: NonNullable<SentinelConfig[\"getIp\"]>;\n detect: Required<NonNullable<SentinelConfig[\"detect\"]>>;\n captureConsole: boolean;\n service: string;\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 /** True when credentials are missing — all operations become no-ops. */\n private disabled = false;\n /** In-process cache of blocked IPs — refreshed every 60 s from the ingest server. */\n private blockedIpCache: 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: SentinelConfig) {\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 };\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 };\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 // 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(\"[SentinelAPI]\")) 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.1.0`,\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(`[SentinelAPI] blocklist refreshed — ${this.blockedIpCache.size} blocked IPs`);\n }\n } catch {\n // Network error: keep the existing cache, never throw\n }\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(`[SentinelAPI] 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(`[SentinelAPI] [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(`[SentinelAPI] ❌ Wrong ingest URL — got redirect to ${res.headers.get(\"location\")}. Check SENTINEL_INGEST_URL.`);\n return;\n }\n if (res.ok) {\n this._origLog(`[SentinelAPI] ✅ Connected (appId: ${this.config.appId.slice(0, 8)}…)`);\n return;\n }\n if (res.status === 401) {\n this._origWarn(\"[SentinelAPI] ❌ Invalid API key — check your SENTINEL_API_KEY\");\n return;\n }\n if (res.status === 403) {\n this._origWarn(\"[SentinelAPI] ❌ App not found or appId mismatch — check your SENTINEL_APP_ID\");\n return;\n }\n this._origWarn(`[SentinelAPI] ⚠️ Ingest returned HTTP ${res.status} — check your configuration`);\n } catch {\n this._origWarn(\"[SentinelAPI] ⚠️ 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 /** Returns true if the IP is in the current blocked list. Synchronous — no network call. */\n isBlocked(ip: string): boolean {\n if (this.disabled) return false;\n return this.blockedIpCache.has(ip);\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 ip: string;\n userId?: string;\n meta?: Record<string, unknown>;\n },\n ): void {\n if (this.disabled) return;\n const event: SdkEvent = {\n name: eventName,\n ts: Date.now(),\n ip: data.ip,\n userId: data.userId,\n meta: data.meta,\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 // Record the successful login\n this.track(EventName.LOGIN_SUCCESS, { ...data, 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, data.ip, tsMs, this.config.geoLookupUrl || undefined);\n if (!result) return;\n\n this.track(EventName.GEO_VELOCITY, {\n ip: data.ip,\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,\n userId: data.userId,\n meta: { phone: data.phone, ...data.meta },\n });\n }\n\n /**\n * Send a structured log entry to the SentinelAPI 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 `[SentinelAPI] ⚠️ 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(`[SentinelAPI] log:${level} ${message}`);\n }\n if (this.logBuffer.length >= 50) void this.#flushLogs();\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 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\nfunction defaultGetUserId(req: unknown): string | undefined {\n const r = req as Record<string, unknown>;\n const user = r[\"user\"] as Record<string, unknown> | undefined;\n if (!user) return undefined;\n return (\n (user[\"id\"] as string | undefined) ??\n (user[\"userId\"] as string | undefined) ??\n (user[\"sub\"] as string | undefined) ??\n (user[\"_id\"] as string | undefined)\n );\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 // X-Forwarded-For can be a comma-separated list; take the first (client) IP\n const fwd = r[\"headers\"] as Record<string, string | string[] | undefined> | undefined;\n const xff = fwd?.[\"x-forwarded-for\"];\n if (xff) {\n const first = Array.isArray(xff) ? xff[0] : xff.split(\",\")[0];\n if (first) return normalizeIp(first.trim());\n }\n const xri = fwd?.[\"x-real-ip\"];\n if (typeof xri === \"string\") return normalizeIp(xri.trim());\n\n const socket = r[\"socket\"] as { remoteAddress?: string } | undefined;\n return normalizeIp(socket?.remoteAddress ?? \"0.0.0.0\");\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 createExpressMiddleware(client: SentinelClient) {\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 // ── Block check — synchronous, zero latency ──\n if (client.isBlocked(ip)) {\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 const headers = req[\"headers\"] as Record<string, string | undefined> | undefined;\n const ua = headers?.[\"user-agent\"] ?? \"\";\n\n // ── Firewall rule evaluation ──\n const fwMatch = client.matchFirewallRule({ url, body: req[\"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 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 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 // Always emit an HTTP access log entry so the Events dashboard has data\n client.track(EventName.REQUEST, {\n ip, userId,\n meta: { method, endpoint: url, status, latencyMs, userAgent: ua, bytes },\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) {\n if (/\\/(login|signin|auth|token|session)/i.test(url)) {\n client.track(EventName.LOGIN_FAILED, { ip, userId, 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, 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: SentinelClient) {\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 // ── Block check — synchronous, zero latency ──\n if (client.isBlocked(ip)) {\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 const userId = client.config.getUserId(req);\n const status = (reply[\"statusCode\"] as number | undefined) ?? 0;\n const headers = (req as Record<string, unknown>)[\"headers\"] as Record<string, string> | undefined;\n const ua = headers?.[\"user-agent\"] ?? \"\";\n const latencyMs = (reply as Record<string, unknown>)[\"elapsedTime\"] as number | undefined ?? 0;\n\n // Always emit HTTP access log entry for the Events dashboard\n client.track(EventName.REQUEST, {\n ip, userId,\n meta: { method, endpoint: url, status, latencyMs: Math.round(latencyMs), userAgent: ua, bytes: 0 },\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 { SentinelClient } 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 { SentinelAPI } from \"@sentinelapi/node-sdk\";\n *\n * const app = express();\n * const sentinel = new SentinelAPI({ 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: SentinelClient) {\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 // ── 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 { SentinelClient } 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 { SentinelAPI } from \"@sentinelapi/node-sdk\";\n *\n * const app = Fastify();\n * const sentinel = new SentinelAPI({ apiKey: \"...\", appId: \"...\" });\n *\n * await app.register(sentinel.fastify());\n * ```\n */\nexport function createFastifyPlugin(client: SentinelClient): FastifyPluginAsync {\n return async function sentinelPlugin(fastify) {\n\n // ── onRequest: path traversal check ──────────────────────────────────\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 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/client.ts","../src/middleware/express.ts","../src/middleware/fastify.ts"],"names":["AsyncLocalStorage","createExpressMiddleware","createFastifyPlugin"],"mappings":"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","file":"index.cjs","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// ─── 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","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\";\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\">> & {\n getUserId: NonNullable<AnomiraConfig[\"getUserId\"]>;\n getIp: NonNullable<AnomiraConfig[\"getIp\"]>;\n detect: Required<NonNullable<AnomiraConfig[\"detect\"]>>;\n captureConsole: boolean;\n service: string;\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 /** True when credentials are missing — all operations become no-ops. */\n private disabled = false;\n /** In-process cache of blocked IPs — refreshed every 60 s from the ingest server. */\n private blockedIpCache: 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 };\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 };\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 // 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.1.0`,\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 #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 /** Returns true if the IP is in the current blocked list. Synchronous — no network call. */\n isBlocked(ip: string): boolean {\n if (this.disabled) return false;\n return this.blockedIpCache.has(ip);\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 ip: string;\n userId?: string;\n meta?: Record<string, unknown>;\n },\n ): void {\n if (this.disabled) return;\n const event: SdkEvent = {\n name: eventName,\n ts: Date.now(),\n ip: data.ip,\n userId: data.userId,\n meta: data.meta,\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 // Record the successful login\n this.track(EventName.LOGIN_SUCCESS, { ...data, 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, data.ip, tsMs, this.config.geoLookupUrl || undefined);\n if (!result) return;\n\n this.track(EventName.GEO_VELOCITY, {\n ip: data.ip,\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,\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 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\nfunction defaultGetUserId(req: unknown): string | undefined {\n const r = req as Record<string, unknown>;\n const user = r[\"user\"] as Record<string, unknown> | undefined;\n if (!user) return undefined;\n return (\n (user[\"id\"] as string | undefined) ??\n (user[\"userId\"] as string | undefined) ??\n (user[\"sub\"] as string | undefined) ??\n (user[\"_id\"] as string | undefined)\n );\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 // X-Forwarded-For can be a comma-separated list; take the first (client) IP\n const fwd = r[\"headers\"] as Record<string, string | string[] | undefined> | undefined;\n const xff = fwd?.[\"x-forwarded-for\"];\n if (xff) {\n const first = Array.isArray(xff) ? xff[0] : xff.split(\",\")[0];\n if (first) return normalizeIp(first.trim());\n }\n const xri = fwd?.[\"x-real-ip\"];\n if (typeof xri === \"string\") return normalizeIp(xri.trim());\n\n const socket = r[\"socket\"] as { remoteAddress?: string } | undefined;\n return normalizeIp(socket?.remoteAddress ?? \"0.0.0.0\");\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 createExpressMiddleware(client: AnomiraClient) {\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 // ── Block check — synchronous, zero latency ──\n if (client.isBlocked(ip)) {\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 const headers = req[\"headers\"] as Record<string, string | undefined> | undefined;\n const ua = headers?.[\"user-agent\"] ?? \"\";\n\n // ── Firewall rule evaluation ──\n const fwMatch = client.matchFirewallRule({ url, body: req[\"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 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 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 // Always emit an HTTP access log entry so the Events dashboard has data\n client.track(EventName.REQUEST, {\n ip, userId,\n meta: { method, endpoint: url, status, latencyMs, userAgent: ua, bytes },\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) {\n if (/\\/(login|signin|auth|token|session)/i.test(url)) {\n client.track(EventName.LOGIN_FAILED, { ip, userId, 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, 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 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 // ── Block check — synchronous, zero latency ──\n if (client.isBlocked(ip)) {\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 const userId = client.config.getUserId(req);\n const status = (reply[\"statusCode\"] as number | undefined) ?? 0;\n const headers = (req as Record<string, unknown>)[\"headers\"] as Record<string, string> | undefined;\n const ua = headers?.[\"user-agent\"] ?? \"\";\n const latencyMs = (reply as Record<string, unknown>)[\"elapsedTime\"] as number | undefined ?? 0;\n\n // Always emit HTTP access log entry for the Events dashboard\n client.track(EventName.REQUEST, {\n ip, userId,\n meta: { method, endpoint: url, status, latencyMs: Math.round(latencyMs), userAgent: ua, bytes: 0 },\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 // ── 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: path traversal check ──────────────────────────────────\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 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"]}