UNPKG

@aikidosec/firewall

Version:

Zen by Aikido is an embedded Application Firewall that autonomously protects Node.js apps against common and critical attacks, provides rate limiting, detects malicious traffic (including bots), and more.

204 lines (203 loc) 6.74 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.InspectionStatistics = void 0; const percentiles_1 = require("../helpers/percentiles"); class InspectionStatistics { constructor({ maxPerfSamplesInMemory, maxCompressedStatsInMemory, }) { this.startedAt = Date.now(); this.operations = {}; this.sqlTokenizationFailures = 0; this.requests = { total: 0, aborted: 0, rateLimited: 0, attacksDetected: { total: 0, blocked: 0 }, attackWaves: { total: 0, blocked: 0, }, }; this.userAgents = { breakdown: {}, }; this.ipAddresses = { breakdown: {}, }; this.maxPerfSamplesInMemory = maxPerfSamplesInMemory; this.maxCompressedStatsInMemory = maxCompressedStatsInMemory; } hasCompressedStats() { return Object.values(this.operations).some((sinkStats) => sinkStats.compressedTimings.length > 0); } isEmpty() { return (this.requests.total === 0 && Object.keys(this.operations).length === 0 && this.requests.attacksDetected.total === 0); } reset() { this.operations = {}; this.requests = { total: 0, aborted: 0, rateLimited: 0, attacksDetected: { total: 0, blocked: 0 }, attackWaves: { total: 0, blocked: 0, }, }; this.userAgents = { breakdown: {}, }; this.ipAddresses = { breakdown: {}, }; this.startedAt = Date.now(); this.sqlTokenizationFailures = 0; } getStats() { const operations = {}; for (const operation in this.operations) { const operationStats = this.operations[operation]; operations[operation] = { kind: operationStats.kind, total: operationStats.total, attacksDetected: { total: operationStats.attacksDetected.total, blocked: operationStats.attacksDetected.blocked, }, interceptorThrewError: operationStats.interceptorThrewError, withoutContext: operationStats.withoutContext, compressedTimings: operationStats.compressedTimings, }; } return { operations: operations, startedAt: this.startedAt, sqlTokenizationFailures: this.sqlTokenizationFailures, requests: this.requests, userAgents: this.userAgents, ipAddresses: this.ipAddresses, }; } ensureOperationStats(operation, kind) { if (!this.operations[operation]) { this.operations[operation] = { withoutContext: 0, kind: kind, total: 0, durations: [], compressedTimings: [], interceptorThrewError: 0, attacksDetected: { total: 0, blocked: 0, }, }; } } compressPerfSamples(operation) { if (operation.length === 0) { return; } /* c8 ignore start */ if (!this.operations[operation]) { return; } if (this.operations[operation].durations.length === 0) { return; } /* c8 ignore stop */ const timings = this.operations[operation].durations; const averageInMS = timings.reduce((acc, curr) => acc + curr, 0) / timings.length; const [p50, p75, p90, p95, p99] = (0, percentiles_1.percentiles)([50, 75, 90, 95, 99], timings); this.operations[operation].compressedTimings.push({ averageInMS, percentiles: { "50": p50, "75": p75, "90": p90, "95": p95, "99": p99, }, compressedAt: Date.now(), }); if (this.operations[operation].compressedTimings.length > this.maxCompressedStatsInMemory) { this.operations[operation].compressedTimings.shift(); } this.operations[operation].durations = []; } interceptorThrewError(operation, kind) { if (operation.length === 0) { return; } this.ensureOperationStats(operation, kind); this.operations[operation].total += 1; this.operations[operation].interceptorThrewError += 1; } onDetectedAttack({ blocked }) { this.requests.attacksDetected.total += 1; if (blocked) { this.requests.attacksDetected.blocked += 1; } } onIPAddressMatches(matches) { matches.forEach((key) => { if (!this.ipAddresses.breakdown[key]) { this.ipAddresses.breakdown[key] = 0; } this.ipAddresses.breakdown[key] += 1; }); } onUserAgentMatches(matches) { matches.forEach((key) => { if (!this.userAgents.breakdown[key]) { this.userAgents.breakdown[key] = 0; } this.userAgents.breakdown[key] += 1; }); } onAbortedRequest() { this.requests.aborted += 1; } onRequest() { this.requests.total += 1; } onRateLimitedRequest() { this.requests.rateLimited += 1; } onAttackWaveDetected() { this.requests.attackWaves.total += 1; } onInspectedCall({ operation, kind, blocked, attackDetected, durationInMs, withoutContext, }) { if (operation.length === 0) { return; } this.ensureOperationStats(operation, kind); this.operations[operation].total += 1; if (withoutContext) { this.operations[operation].withoutContext += 1; return; } if (this.operations[operation].durations.length >= this.maxPerfSamplesInMemory) { this.compressPerfSamples(operation); } this.operations[operation].durations.push(durationInMs); if (attackDetected) { this.operations[operation].attacksDetected.total += 1; if (blocked) { this.operations[operation].attacksDetected.blocked += 1; } } } forceCompress() { for (const kind in this.operations) { this.compressPerfSamples(kind); } } onSqlTokenizationFailure() { this.sqlTokenizationFailures += 1; } } exports.InspectionStatistics = InspectionStatistics;