z-shield 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +123 -6
- package/dist/block-manager.d.ts +134 -0
- package/dist/block-manager.d.ts.map +1 -0
- package/dist/block-manager.js +425 -0
- package/dist/block-manager.js.map +1 -0
- package/dist/cli.d.ts +141 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +784 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +15 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +148 -0
- package/dist/config.js.map +1 -0
- package/dist/detector.d.ts +56 -0
- package/dist/detector.d.ts.map +1 -0
- package/dist/detector.js +81 -0
- package/dist/detector.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +120 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +141 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +347 -0
- package/dist/logger.js.map +1 -0
- package/dist/middleware.d.ts +68 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +272 -0
- package/dist/middleware.js.map +1 -0
- package/dist/next.d.ts +73 -0
- package/dist/next.d.ts.map +1 -0
- package/dist/next.js +486 -0
- package/dist/next.js.map +1 -0
- package/dist/rate-limiter.d.ts +75 -0
- package/dist/rate-limiter.d.ts.map +1 -0
- package/dist/rate-limiter.js +153 -0
- package/dist/rate-limiter.js.map +1 -0
- package/dist/types.d.ts +199 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/next.d.ts +1 -0
- package/next.js +2 -0
- package/package.json +34 -5
- package/src/block-manager.js +0 -326
- package/src/cli.js +0 -764
- package/src/config.js +0 -165
- package/src/detector.js +0 -88
- package/src/index.js +0 -97
- package/src/logger.js +0 -280
- package/src/middleware.js +0 -288
- package/src/rate-limiter.js +0 -186
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.BlockManager = void 0;
|
|
7
|
+
exports.canonicalizeIpv6 = canonicalizeIpv6;
|
|
8
|
+
exports.normalizeIp = normalizeIp;
|
|
9
|
+
exports.isValidIp = isValidIp;
|
|
10
|
+
exports.parseDuration = parseDuration;
|
|
11
|
+
const node_net_1 = __importDefault(require("node:net"));
|
|
12
|
+
/**
|
|
13
|
+
* Canonicalizes an IPv6 address string according to RFC 5952.
|
|
14
|
+
* - Parses 16-bit hextets
|
|
15
|
+
* - Handles embedded IPv4 in IPv6 (including IPv4-mapped IPv6)
|
|
16
|
+
* - Normalizes loopback ('::1' and '0:0:0:0:0:0:0:1') to '127.0.0.1'
|
|
17
|
+
* - Normalizes IPv4-mapped addresses to standard IPv4
|
|
18
|
+
* - Compresses the longest run of 2 or more zeros with '::' (first run on tie)
|
|
19
|
+
* - Lowers all hexadecimal characters and removes leading zeros in each field
|
|
20
|
+
*
|
|
21
|
+
* @param ip - Valid IPv6 address string
|
|
22
|
+
* @returns Canonicalized representation
|
|
23
|
+
*/
|
|
24
|
+
function canonicalizeIpv6(ip) {
|
|
25
|
+
let str = ip.toLowerCase().trim();
|
|
26
|
+
const zoneIndex = str.indexOf('%');
|
|
27
|
+
if (zoneIndex !== -1) {
|
|
28
|
+
str = str.substring(0, zoneIndex);
|
|
29
|
+
}
|
|
30
|
+
// Handle embedded IPv4 (e.g. ::ffff:192.0.2.1)
|
|
31
|
+
let embeddedIpv4 = null;
|
|
32
|
+
const lastColon = str.lastIndexOf(':');
|
|
33
|
+
if (lastColon !== -1) {
|
|
34
|
+
const candidateIpv4 = str.substring(lastColon + 1);
|
|
35
|
+
if (candidateIpv4.includes('.')) {
|
|
36
|
+
if (node_net_1.default.isIP(candidateIpv4) === 4) {
|
|
37
|
+
embeddedIpv4 = candidateIpv4.split('.').map(Number);
|
|
38
|
+
str = str.substring(0, lastColon) + ':0:0';
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
return ip.toLowerCase().trim();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const parts = str.split('::');
|
|
46
|
+
if (parts.length > 2) {
|
|
47
|
+
return ip.toLowerCase().trim();
|
|
48
|
+
}
|
|
49
|
+
const head = parts[0] ? parts[0].split(':').filter(Boolean) : [];
|
|
50
|
+
const tail = parts.length === 2 && parts[1] ? parts[1].split(':').filter(Boolean) : [];
|
|
51
|
+
const totalHextets = head.length + tail.length;
|
|
52
|
+
if (parts.length === 1 && totalHextets !== 8) {
|
|
53
|
+
return ip.toLowerCase().trim();
|
|
54
|
+
}
|
|
55
|
+
if (parts.length === 2 && totalHextets >= 8) {
|
|
56
|
+
return ip.toLowerCase().trim();
|
|
57
|
+
}
|
|
58
|
+
const missingZeros = 8 - totalHextets;
|
|
59
|
+
const hextets = [];
|
|
60
|
+
for (const h of head) {
|
|
61
|
+
const val = parseInt(h, 16);
|
|
62
|
+
if (isNaN(val) || val < 0 || val > 0xffff) {
|
|
63
|
+
return ip.toLowerCase().trim();
|
|
64
|
+
}
|
|
65
|
+
hextets.push(val);
|
|
66
|
+
}
|
|
67
|
+
for (let i = 0; i < missingZeros; i++) {
|
|
68
|
+
hextets.push(0);
|
|
69
|
+
}
|
|
70
|
+
for (const t of tail) {
|
|
71
|
+
const val = parseInt(t, 16);
|
|
72
|
+
if (isNaN(val) || val < 0 || val > 0xffff) {
|
|
73
|
+
return ip.toLowerCase().trim();
|
|
74
|
+
}
|
|
75
|
+
hextets.push(val);
|
|
76
|
+
}
|
|
77
|
+
if (hextets.length !== 8) {
|
|
78
|
+
return ip.toLowerCase().trim();
|
|
79
|
+
}
|
|
80
|
+
if (embeddedIpv4 && embeddedIpv4.length === 4) {
|
|
81
|
+
hextets[6] = ((embeddedIpv4[0] ?? 0) << 8) | (embeddedIpv4[1] ?? 0);
|
|
82
|
+
hextets[7] = ((embeddedIpv4[2] ?? 0) << 8) | (embeddedIpv4[3] ?? 0);
|
|
83
|
+
}
|
|
84
|
+
// IPv4-mapped IPv6: ::ffff:w.x.y.z or 0:0:0:0:0:ffff:w.x.y.z
|
|
85
|
+
const isIpv4Mapped = hextets[0] === 0 &&
|
|
86
|
+
hextets[1] === 0 &&
|
|
87
|
+
hextets[2] === 0 &&
|
|
88
|
+
hextets[3] === 0 &&
|
|
89
|
+
hextets[4] === 0 &&
|
|
90
|
+
hextets[5] === 0xffff;
|
|
91
|
+
if (isIpv4Mapped) {
|
|
92
|
+
const h6 = hextets[6] ?? 0;
|
|
93
|
+
const h7 = hextets[7] ?? 0;
|
|
94
|
+
const b0 = (h6 >> 8) & 0xff;
|
|
95
|
+
const b1 = h6 & 0xff;
|
|
96
|
+
const b2 = (h7 >> 8) & 0xff;
|
|
97
|
+
const b3 = h7 & 0xff;
|
|
98
|
+
return `${b0}.${b1}.${b2}.${b3}`;
|
|
99
|
+
}
|
|
100
|
+
// IPv6 Loopback: ::1 or 0:0:0:0:0:0:0:1 -> 127.0.0.1
|
|
101
|
+
const isLoopback = hextets.slice(0, 7).every((h) => h === 0) && hextets[7] === 1;
|
|
102
|
+
if (isLoopback) {
|
|
103
|
+
return '127.0.0.1';
|
|
104
|
+
}
|
|
105
|
+
// Find longest contiguous run of zeros (length >= 2) under RFC 5952
|
|
106
|
+
let bestStart = -1;
|
|
107
|
+
let bestLen = 0;
|
|
108
|
+
let curStart = -1;
|
|
109
|
+
let curLen = 0;
|
|
110
|
+
for (let i = 0; i < 8; i++) {
|
|
111
|
+
if (hextets[i] === 0) {
|
|
112
|
+
if (curStart === -1) {
|
|
113
|
+
curStart = i;
|
|
114
|
+
curLen = 1;
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
curLen++;
|
|
118
|
+
}
|
|
119
|
+
if (curLen > bestLen) {
|
|
120
|
+
bestStart = curStart;
|
|
121
|
+
bestLen = curLen;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
curStart = -1;
|
|
126
|
+
curLen = 0;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// If no zero sequence of 2 or more hextets exists, join all 8 hextets
|
|
130
|
+
if (bestLen < 2) {
|
|
131
|
+
return hextets.map((h) => h.toString(16)).join(':');
|
|
132
|
+
}
|
|
133
|
+
const headStr = hextets.slice(0, bestStart).map((h) => h.toString(16)).join(':');
|
|
134
|
+
const tailStr = hextets.slice(bestStart + bestLen).map((h) => h.toString(16)).join(':');
|
|
135
|
+
return `${headStr}::${tailStr}`;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Normalizes an IP address for consistent comparison across IPv4 and IPv6.
|
|
139
|
+
* - Trims whitespace
|
|
140
|
+
* - Canonicalizes IPv6 addresses according to RFC 5952 (zero compression, lowercase)
|
|
141
|
+
* - Unmaps all IPv4-mapped IPv6 variations (e.g. '::ffff:192.168.1.1' -> '192.168.1.1')
|
|
142
|
+
* - Normalizes all IPv6 loopback variants ('::1', '0:0:0:0:0:0:0:1') to '127.0.0.1'
|
|
143
|
+
* - Preserves invalid strings without converting them into fake valid IPs
|
|
144
|
+
*
|
|
145
|
+
* @param ip - Raw client IP candidate
|
|
146
|
+
* @returns Normalized IP or 'unknown'
|
|
147
|
+
*/
|
|
148
|
+
function normalizeIp(ip) {
|
|
149
|
+
if (typeof ip !== 'string') {
|
|
150
|
+
return 'unknown';
|
|
151
|
+
}
|
|
152
|
+
const trimmed = ip.trim();
|
|
153
|
+
if (trimmed.length === 0) {
|
|
154
|
+
return 'unknown';
|
|
155
|
+
}
|
|
156
|
+
// Standard valid IPv4
|
|
157
|
+
const ipType = node_net_1.default.isIP(trimmed);
|
|
158
|
+
if (ipType === 4) {
|
|
159
|
+
return trimmed;
|
|
160
|
+
}
|
|
161
|
+
// Standard valid IPv6 (including loopback and IPv4-mapped)
|
|
162
|
+
if (ipType === 6) {
|
|
163
|
+
return canonicalizeIpv6(trimmed);
|
|
164
|
+
}
|
|
165
|
+
// Handle possible ::ffff:192.0.2.1 where net.isIP might return 0
|
|
166
|
+
const lower = trimmed.toLowerCase();
|
|
167
|
+
if (lower.startsWith('::ffff:')) {
|
|
168
|
+
const candidateIpv4 = trimmed.substring(7);
|
|
169
|
+
if (node_net_1.default.isIP(candidateIpv4) === 4) {
|
|
170
|
+
return candidateIpv4;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return trimmed;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Validates whether an IP address is a valid IPv4 or IPv6 string.
|
|
177
|
+
* Uses Node.js built-in net.isIP (returns 4 for IPv4, 6 for IPv6, 0 for invalid).
|
|
178
|
+
*
|
|
179
|
+
* @param ip - IP candidate string
|
|
180
|
+
* @returns True if valid IPv4 or IPv6
|
|
181
|
+
*/
|
|
182
|
+
function isValidIp(ip) {
|
|
183
|
+
if (typeof ip !== 'string' || ip.trim().length === 0) {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
const norm = normalizeIp(ip);
|
|
187
|
+
return node_net_1.default.isIP(norm) !== 0;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Parses block duration into milliseconds.
|
|
191
|
+
* Supports numbers (milliseconds) or human-readable strings ('30s', '10m', '1h', '2d').
|
|
192
|
+
*
|
|
193
|
+
* @param duration - Numeric ms or formatted string
|
|
194
|
+
* @returns Duration in milliseconds
|
|
195
|
+
*/
|
|
196
|
+
function parseDuration(duration) {
|
|
197
|
+
if (typeof duration === 'number') {
|
|
198
|
+
const ms = Math.floor(duration);
|
|
199
|
+
if (!Number.isFinite(duration) || duration <= 0 || ms <= 0) {
|
|
200
|
+
throw new TypeError('[Z-Shield] Block duration must be a positive number in milliseconds.');
|
|
201
|
+
}
|
|
202
|
+
return ms;
|
|
203
|
+
}
|
|
204
|
+
if (typeof duration === 'string') {
|
|
205
|
+
const trimmed = duration.trim();
|
|
206
|
+
const match = trimmed.match(/^(-?\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/i);
|
|
207
|
+
if (!match) {
|
|
208
|
+
throw new Error(`[Z-Shield] Invalid duration format: "${duration}". Use format like "10m", "1h", "30s", or milliseconds.`);
|
|
209
|
+
}
|
|
210
|
+
const rawVal = match[1];
|
|
211
|
+
if (rawVal === undefined) {
|
|
212
|
+
throw new Error('[Z-Shield] Block duration must be greater than zero.');
|
|
213
|
+
}
|
|
214
|
+
const val = parseFloat(rawVal);
|
|
215
|
+
if (!Number.isFinite(val) || val <= 0) {
|
|
216
|
+
throw new Error('[Z-Shield] Block duration must be greater than zero.');
|
|
217
|
+
}
|
|
218
|
+
const unit = (match[2] || 'ms').toLowerCase();
|
|
219
|
+
let ms;
|
|
220
|
+
switch (unit) {
|
|
221
|
+
case 'ms':
|
|
222
|
+
ms = Math.floor(val);
|
|
223
|
+
break;
|
|
224
|
+
case 's':
|
|
225
|
+
ms = Math.floor(val * 1000);
|
|
226
|
+
break;
|
|
227
|
+
case 'm':
|
|
228
|
+
ms = Math.floor(val * 60 * 1000);
|
|
229
|
+
break;
|
|
230
|
+
case 'h':
|
|
231
|
+
ms = Math.floor(val * 60 * 60 * 1000);
|
|
232
|
+
break;
|
|
233
|
+
case 'd':
|
|
234
|
+
ms = Math.floor(val * 24 * 60 * 60 * 1000);
|
|
235
|
+
break;
|
|
236
|
+
default:
|
|
237
|
+
ms = Math.floor(val);
|
|
238
|
+
break;
|
|
239
|
+
}
|
|
240
|
+
if (ms <= 0) {
|
|
241
|
+
throw new Error('[Z-Shield] Block duration must be greater than zero.');
|
|
242
|
+
}
|
|
243
|
+
return ms;
|
|
244
|
+
}
|
|
245
|
+
throw new TypeError('[Z-Shield] Block duration must be a number (milliseconds) or a duration string like "10m".');
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* BlockManager - In-Memory Temporary IP Block Management
|
|
249
|
+
*
|
|
250
|
+
* Characteristics:
|
|
251
|
+
* - Process-Local: Blocks are held in RAM. They do NOT survive server restarts.
|
|
252
|
+
* - Automatic Expiry: Block entries are checked against their expiration timestamp.
|
|
253
|
+
* - Bounded Memory: Uses LRU eviction when `maxBlockedIps` is reached to prevent memory exhaustion.
|
|
254
|
+
* - No Background Timers: Uses passive/lazy expiration on access to keep Node.js event loop idle.
|
|
255
|
+
*/
|
|
256
|
+
class BlockManager {
|
|
257
|
+
defaultDurationMs;
|
|
258
|
+
maxBlockedIps;
|
|
259
|
+
blocks;
|
|
260
|
+
/**
|
|
261
|
+
* @param options - Block manager options or resolved config
|
|
262
|
+
*/
|
|
263
|
+
constructor(options = {}) {
|
|
264
|
+
this.defaultDurationMs = options.blockDurationMs || 15 * 60 * 1000;
|
|
265
|
+
this.maxBlockedIps = options.maxBlockedIps || 5000;
|
|
266
|
+
this.blocks = new Map();
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Temporarily blocks an IP address.
|
|
270
|
+
*
|
|
271
|
+
* @param ip - Valid IPv4 or IPv6 address
|
|
272
|
+
* @param duration - Duration in ms or string ('10m')
|
|
273
|
+
* @param reason - Reason for block
|
|
274
|
+
* @param now - Current timestamp (injectable for testing)
|
|
275
|
+
* @returns Created block record
|
|
276
|
+
*/
|
|
277
|
+
block(ip, duration = this.defaultDurationMs, reason = 'Administrative block', now = Date.now()) {
|
|
278
|
+
if (!isValidIp(ip)) {
|
|
279
|
+
throw new TypeError(`[Z-Shield] Cannot block invalid IP address: "${ip}".`);
|
|
280
|
+
}
|
|
281
|
+
const durationMs = parseDuration(duration);
|
|
282
|
+
const cleanIp = normalizeIp(ip);
|
|
283
|
+
// Bound memory: if at capacity, evict an old entry before inserting
|
|
284
|
+
if (this.blocks.size >= this.maxBlockedIps && !this.blocks.has(cleanIp)) {
|
|
285
|
+
this._evictOldest(now);
|
|
286
|
+
}
|
|
287
|
+
const entry = {
|
|
288
|
+
blockedAt: now,
|
|
289
|
+
expiresAt: now + durationMs,
|
|
290
|
+
reason: String(reason || 'Administrative block')
|
|
291
|
+
};
|
|
292
|
+
// Refresh LRU order: delete if exists, then set
|
|
293
|
+
this.blocks.delete(cleanIp);
|
|
294
|
+
this.blocks.set(cleanIp, entry);
|
|
295
|
+
return {
|
|
296
|
+
ip: cleanIp,
|
|
297
|
+
blockedAt: entry.blockedAt,
|
|
298
|
+
expiresAt: entry.expiresAt,
|
|
299
|
+
durationMs,
|
|
300
|
+
reason: entry.reason
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Manually unblocks an IP address.
|
|
305
|
+
*
|
|
306
|
+
* @param ip - Client IP
|
|
307
|
+
* @returns True if IP was unblocked, false if it was not blocked
|
|
308
|
+
*/
|
|
309
|
+
unblock(ip) {
|
|
310
|
+
if (!isValidIp(ip)) {
|
|
311
|
+
throw new TypeError(`[Z-Shield] Cannot unblock invalid IP address: "${ip}".`);
|
|
312
|
+
}
|
|
313
|
+
return this.blocks.delete(normalizeIp(ip));
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Checks whether an IP address is currently blocked.
|
|
317
|
+
* Expired blocks are lazily pruned.
|
|
318
|
+
*
|
|
319
|
+
* @param ip - Client IP
|
|
320
|
+
* @param now - Current timestamp
|
|
321
|
+
* @returns True if actively blocked
|
|
322
|
+
*/
|
|
323
|
+
isBlocked(ip, now = Date.now()) {
|
|
324
|
+
if (!ip || typeof ip !== 'string') {
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
const cleanIp = normalizeIp(ip);
|
|
328
|
+
const entry = this.blocks.get(cleanIp);
|
|
329
|
+
if (!entry) {
|
|
330
|
+
return false;
|
|
331
|
+
}
|
|
332
|
+
// Lazy expiration check
|
|
333
|
+
if (now >= entry.expiresAt) {
|
|
334
|
+
this.blocks.delete(cleanIp);
|
|
335
|
+
return false;
|
|
336
|
+
}
|
|
337
|
+
return true;
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Retrieves active block details for an IP, or null if not blocked/expired.
|
|
341
|
+
*
|
|
342
|
+
* @param ip - Client IP
|
|
343
|
+
* @param now - Current timestamp
|
|
344
|
+
* @returns Block information or null
|
|
345
|
+
*/
|
|
346
|
+
getBlockInfo(ip, now = Date.now()) {
|
|
347
|
+
if (!this.isBlocked(ip, now)) {
|
|
348
|
+
return null;
|
|
349
|
+
}
|
|
350
|
+
const cleanIp = normalizeIp(ip);
|
|
351
|
+
const entry = this.blocks.get(cleanIp);
|
|
352
|
+
if (!entry) {
|
|
353
|
+
return null;
|
|
354
|
+
}
|
|
355
|
+
return {
|
|
356
|
+
ip: cleanIp,
|
|
357
|
+
blockedAt: entry.blockedAt,
|
|
358
|
+
expiresAt: entry.expiresAt,
|
|
359
|
+
remainingMs: Math.max(0, entry.expiresAt - now),
|
|
360
|
+
reason: entry.reason
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Returns a list of all currently active blocked IPs.
|
|
365
|
+
*
|
|
366
|
+
* @param now - Current timestamp
|
|
367
|
+
* @returns Array of active block records
|
|
368
|
+
*/
|
|
369
|
+
listBlocks(now = Date.now()) {
|
|
370
|
+
this.pruneExpired(now);
|
|
371
|
+
const result = [];
|
|
372
|
+
for (const [ip, entry] of this.blocks.entries()) {
|
|
373
|
+
result.push({
|
|
374
|
+
ip,
|
|
375
|
+
blockedAt: entry.blockedAt,
|
|
376
|
+
expiresAt: entry.expiresAt,
|
|
377
|
+
remainingMs: Math.max(0, entry.expiresAt - now),
|
|
378
|
+
reason: entry.reason
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
return result;
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Scans and purges any expired blocks from memory.
|
|
385
|
+
*
|
|
386
|
+
* @param now - Current timestamp
|
|
387
|
+
* @returns Number of purged blocks
|
|
388
|
+
*/
|
|
389
|
+
pruneExpired(now = Date.now()) {
|
|
390
|
+
let pruned = 0;
|
|
391
|
+
for (const [ip, entry] of this.blocks.entries()) {
|
|
392
|
+
if (now >= entry.expiresAt) {
|
|
393
|
+
this.blocks.delete(ip);
|
|
394
|
+
pruned++;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
return pruned;
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* Clears all blocks from memory.
|
|
401
|
+
*/
|
|
402
|
+
clear() {
|
|
403
|
+
this.blocks.clear();
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Number of IPs currently stored in memory.
|
|
407
|
+
*/
|
|
408
|
+
get size() {
|
|
409
|
+
return this.blocks.size;
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Evicts the oldest entry to strictly uphold maxBlockedIps limit.
|
|
413
|
+
*
|
|
414
|
+
* @param _now - Current timestamp
|
|
415
|
+
*/
|
|
416
|
+
_evictOldest(_now) {
|
|
417
|
+
const iterator = this.blocks.keys();
|
|
418
|
+
const firstKey = iterator.next().value;
|
|
419
|
+
if (firstKey !== undefined) {
|
|
420
|
+
this.blocks.delete(firstKey);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
exports.BlockManager = BlockManager;
|
|
425
|
+
//# sourceMappingURL=block-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"block-manager.js","sourceRoot":"","sources":["../src/block-manager.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,wDAA2B;AAsB3B;;;;;;;;;;;GAWG;AACH,0BAAiC,EAAU;IACzC,IAAI,GAAG,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;IAClC,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACnC,IAAI,SAAS,KAAK,CAAC,CAAC,EAAE,CAAC;QACrB,GAAG,GAAG,GAAG,CAAC,SAAS,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;IACpC,CAAC;IAED,+CAA+C;IAC/C,IAAI,YAAY,GAAoB,IAAI,CAAC;IACzC,MAAM,SAAS,GAAG,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACvC,IAAI,SAAS,KAAK,CAAC,CAAC,EAAE,CAAC;QACrB,MAAM,aAAa,GAAG,GAAG,CAAC,SAAS,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC;QACnD,IAAI,aAAa,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YAChC,IAAI,kBAAG,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;gBAClC,YAAY,GAAG,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;gBACpD,GAAG,GAAG,GAAG,CAAC,SAAS,CAAC,CAAC,EAAE,SAAS,CAAC,GAAG,MAAM,CAAC;YAC7C,CAAC;iBAAM,CAAC;gBACN,OAAO,EAAE,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;YACjC,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC9B,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrB,OAAO,EAAE,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;IACjC,CAAC;IAED,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACjE,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAEvF,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;IAC/C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,YAAY,KAAK,CAAC,EAAE,CAAC;QAC7C,OAAO,EAAE,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;IACjC,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,YAAY,IAAI,CAAC,EAAE,CAAC;QAC5C,OAAO,EAAE,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;IACjC,CAAC;IAED,MAAM,YAAY,GAAG,CAAC,GAAG,YAAY,CAAC;IACtC,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QACrB,MAAM,GAAG,GAAG,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC5B,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,GAAG,GAAG,MAAM,EAAE,CAAC;YAC1C,OAAO,EAAE,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;QACjC,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACpB,CAAC;IACD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,YAAY,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QACrB,MAAM,GAAG,GAAG,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC5B,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,GAAG,GAAG,MAAM,EAAE,CAAC;YAC1C,OAAO,EAAE,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;QACjC,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACpB,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,EAAE,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;IACjC,CAAC;IAED,IAAI,YAAY,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9C,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACpE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IACtE,CAAC;IAED,6DAA6D;IAC7D,MAAM,YAAY,GAChB,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;QAChB,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;QAChB,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;QAChB,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;QAChB,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;QAChB,OAAO,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC;IACxB,IAAI,YAAY,EAAE,CAAC;QACjB,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC3B,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC3B,MAAM,EAAE,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC;QAC5B,MAAM,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;QACrB,MAAM,EAAE,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC;QAC5B,MAAM,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;QACrB,OAAO,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC;IACnC,CAAC;IAED,qDAAqD;IACrD,MAAM,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IACjF,IAAI,UAAU,EAAE,CAAC;QACf,OAAO,WAAW,CAAC;IACrB,CAAC;IAED,oEAAoE;IACpE,IAAI,SAAS,GAAG,CAAC,CAAC,CAAC;IACnB,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,IAAI,QAAQ,GAAG,CAAC,CAAC,CAAC;IAClB,IAAI,MAAM,GAAG,CAAC,CAAC;IAEf,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3B,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;YACrB,IAAI,QAAQ,KAAK,CAAC,CAAC,EAAE,CAAC;gBACpB,QAAQ,GAAG,CAAC,CAAC;gBACb,MAAM,GAAG,CAAC,CAAC;YACb,CAAC;iBAAM,CAAC;gBACN,MAAM,EAAE,CAAC;YACX,CAAC;YACD,IAAI,MAAM,GAAG,OAAO,EAAE,CAAC;gBACrB,SAAS,GAAG,QAAQ,CAAC;gBACrB,OAAO,GAAG,MAAM,CAAC;YACnB,CAAC;QACH,CAAC;aAAM,CAAC;YACN,QAAQ,GAAG,CAAC,CAAC,CAAC;YACd,MAAM,GAAG,CAAC,CAAC;QACb,CAAC;IACH,CAAC;IAED,sEAAsE;IACtE,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;QAChB,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACtD,CAAC;IAED,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACjF,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,SAAS,GAAG,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACxF,OAAO,GAAG,OAAO,KAAK,OAAO,EAAE,CAAC;AAClC,CAAC;AAED;;;;;;;;;;GAUG;AACH,qBAA4B,EAAW;IACrC,IAAI,OAAO,EAAE,KAAK,QAAQ,EAAE,CAAC;QAC3B,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,OAAO,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC;IAC1B,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,sBAAsB;IACtB,MAAM,MAAM,GAAG,kBAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACjC,IAAI,MAAM,KAAK,CAAC,EAAE,CAAC;QACjB,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,2DAA2D;IAC3D,IAAI,MAAM,KAAK,CAAC,EAAE,CAAC;QACjB,OAAO,gBAAgB,CAAC,OAAO,CAAC,CAAC;IACnC,CAAC;IAED,iEAAiE;IACjE,MAAM,KAAK,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IACpC,IAAI,KAAK,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAChC,MAAM,aAAa,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;QAC3C,IAAI,kBAAG,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;YAClC,OAAO,aAAa,CAAC;QACvB,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;;GAMG;AACH,mBAA0B,EAAW;IACnC,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,IAAI,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;IAC7B,OAAO,kBAAG,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAC9B,CAAC;AAED;;;;;;GAMG;AACH,uBAA8B,QAAyB;IACrD,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACjC,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAChC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,QAAQ,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;YAC3D,MAAM,IAAI,SAAS,CAAC,sEAAsE,CAAC,CAAC;QAC9F,CAAC;QACD,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACjC,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC;QAChC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,sCAAsC,CAAC,CAAC;QACpE,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,KAAK,CAAC,wCAAwC,QAAQ,yDAAyD,CAAC,CAAC;QAC7H,CAAC;QACD,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACxB,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;QAC1E,CAAC;QACD,MAAM,GAAG,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;QAC/B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC;YACtC,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;QAC1E,CAAC;QACD,MAAM,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;QAC9C,IAAI,EAAU,CAAC;QACf,QAAQ,IAAI,EAAE,CAAC;YACb,KAAK,IAAI;gBAAE,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gBAAC,MAAM;YACvC,KAAK,GAAG;gBAAE,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;gBAAC,MAAM;YAC7C,KAAK,GAAG;gBAAE,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;gBAAC,MAAM;YAClD,KAAK,GAAG;gBAAE,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;gBAAC,MAAM;YACvD,KAAK,GAAG;gBAAE,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;gBAAC,MAAM;YAC5D;gBAAS,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gBAAC,MAAM;QACvC,CAAC;QACD,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;QAC1E,CAAC;QACD,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,IAAI,SAAS,CAAC,4FAA4F,CAAC,CAAC;AACpH,CAAC;AAED;;;;;;;;GAQG;AACH;IACW,iBAAiB,CAAS;IAC1B,aAAa,CAAS;IACd,MAAM,CAAkC;IAEzD;;OAEG;IACH,YAAY,OAAO,GAAyD,EAAE;QAC5E,IAAI,CAAC,iBAAiB,GAAG,OAAO,CAAC,eAAe,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;QACnE,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,IAAI,CAAC;QAEnD,IAAI,CAAC,MAAM,GAAG,IAAI,GAAG,EAA8B,CAAC;IACtD,CAAC;IAED;;;;;;;;OAQG;IACH,KAAK,CACH,EAAU,EACV,QAAQ,GAAoB,IAAI,CAAC,iBAAiB,EAClD,MAAM,GAAW,sBAAsB,EACvC,GAAG,GAAW,IAAI,CAAC,GAAG,EAAE;QAExB,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,CAAC;YACnB,MAAM,IAAI,SAAS,CAAC,gDAAgD,EAAE,IAAI,CAAC,CAAC;QAC9E,CAAC;QAED,MAAM,UAAU,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;QAC3C,MAAM,OAAO,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;QAEhC,oEAAoE;QACpE,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,IAAI,CAAC,aAAa,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YACxE,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;QACzB,CAAC;QAED,MAAM,KAAK,GAAuB;YAChC,SAAS,EAAE,GAAG;YACd,SAAS,EAAE,GAAG,GAAG,UAAU;YAC3B,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,sBAAsB,CAAC;SACjD,CAAC;QAEF,gDAAgD;QAChD,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC5B,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAEhC,OAAO;YACL,EAAE,EAAE,OAAO;YACX,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,UAAU;YACV,MAAM,EAAE,KAAK,CAAC,MAAM;SACrB,CAAC;IACJ,CAAC;IAED;;;;;OAKG;IACH,OAAO,CAAC,EAAU;QAChB,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,CAAC;YACnB,MAAM,IAAI,SAAS,CAAC,kDAAkD,EAAE,IAAI,CAAC,CAAC;QAChF,CAAC;QACD,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC;IAC7C,CAAC;IAED;;;;;;;OAOG;IACH,SAAS,CAAC,EAA6B,EAAE,GAAG,GAAW,IAAI,CAAC,GAAG,EAAE;QAC/D,IAAI,CAAC,EAAE,IAAI,OAAO,EAAE,KAAK,QAAQ,EAAE,CAAC;YAClC,OAAO,KAAK,CAAC;QACf,CAAC;QAED,MAAM,OAAO,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;QAChC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAEvC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,KAAK,CAAC;QACf,CAAC;QAED,wBAAwB;QACxB,IAAI,GAAG,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;YAC3B,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAC5B,OAAO,KAAK,CAAC;QACf,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;;OAMG;IACH,YAAY,CAAC,EAAU,EAAE,GAAG,GAAW,IAAI,CAAC,GAAG,EAAE;QAC/C,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC;YAC7B,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,OAAO,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;QAChC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACvC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO;YACL,EAAE,EAAE,OAAO;YACX,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,WAAW,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,SAAS,GAAG,GAAG,CAAC;YAC/C,MAAM,EAAE,KAAK,CAAC,MAAM;SACrB,CAAC;IACJ,CAAC;IAED;;;;;OAKG;IACH,UAAU,CAAC,GAAG,GAAW,IAAI,CAAC,GAAG,EAAE;QACjC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;QACvB,MAAM,MAAM,GAAgB,EAAE,CAAC;QAC/B,KAAK,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC;YAChD,MAAM,CAAC,IAAI,CAAC;gBACV,EAAE;gBACF,SAAS,EAAE,KAAK,CAAC,SAAS;gBAC1B,SAAS,EAAE,KAAK,CAAC,SAAS;gBAC1B,WAAW,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,SAAS,GAAG,GAAG,CAAC;gBAC/C,MAAM,EAAE,KAAK,CAAC,MAAM;aACrB,CAAC,CAAC;QACL,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;;;;OAKG;IACH,YAAY,CAAC,GAAG,GAAW,IAAI,CAAC,GAAG,EAAE;QACnC,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,KAAK,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC;YAChD,IAAI,GAAG,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;gBAC3B,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBACvB,MAAM,EAAE,CAAC;YACX,CAAC;QACH,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;OAEG;IACH,KAAK;QACH,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;IACtB,CAAC;IAED;;OAEG;IACH,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;IAC1B,CAAC;IAED;;;;OAIG;IACK,YAAY,CAAC,IAAY;QAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QACpC,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC;QAEvC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;CACF","sourcesContent":["import net from 'node:net';\nimport type { BlockRecord, ZShieldConfig, ZShieldOptions } from './types';\n\nexport interface BlockManagerOptions {\n blockDurationMs?: number;\n maxBlockedIps?: number;\n}\n\nexport interface BlockInfo {\n ip: string;\n blockedAt: number;\n expiresAt: number;\n remainingMs: number;\n reason: string;\n}\n\ninterface InternalBlockEntry {\n blockedAt: number;\n expiresAt: number;\n reason: string;\n}\n\n/**\n * Canonicalizes an IPv6 address string according to RFC 5952.\n * - Parses 16-bit hextets\n * - Handles embedded IPv4 in IPv6 (including IPv4-mapped IPv6)\n * - Normalizes loopback ('::1' and '0:0:0:0:0:0:0:1') to '127.0.0.1'\n * - Normalizes IPv4-mapped addresses to standard IPv4\n * - Compresses the longest run of 2 or more zeros with '::' (first run on tie)\n * - Lowers all hexadecimal characters and removes leading zeros in each field\n *\n * @param ip - Valid IPv6 address string\n * @returns Canonicalized representation\n */\nexport function canonicalizeIpv6(ip: string): string {\n let str = ip.toLowerCase().trim();\n const zoneIndex = str.indexOf('%');\n if (zoneIndex !== -1) {\n str = str.substring(0, zoneIndex);\n }\n\n // Handle embedded IPv4 (e.g. ::ffff:192.0.2.1)\n let embeddedIpv4: number[] | null = null;\n const lastColon = str.lastIndexOf(':');\n if (lastColon !== -1) {\n const candidateIpv4 = str.substring(lastColon + 1);\n if (candidateIpv4.includes('.')) {\n if (net.isIP(candidateIpv4) === 4) {\n embeddedIpv4 = candidateIpv4.split('.').map(Number);\n str = str.substring(0, lastColon) + ':0:0';\n } else {\n return ip.toLowerCase().trim();\n }\n }\n }\n\n const parts = str.split('::');\n if (parts.length > 2) {\n return ip.toLowerCase().trim();\n }\n\n const head = parts[0] ? parts[0].split(':').filter(Boolean) : [];\n const tail = parts.length === 2 && parts[1] ? parts[1].split(':').filter(Boolean) : [];\n\n const totalHextets = head.length + tail.length;\n if (parts.length === 1 && totalHextets !== 8) {\n return ip.toLowerCase().trim();\n }\n if (parts.length === 2 && totalHextets >= 8) {\n return ip.toLowerCase().trim();\n }\n\n const missingZeros = 8 - totalHextets;\n const hextets: number[] = [];\n\n for (const h of head) {\n const val = parseInt(h, 16);\n if (isNaN(val) || val < 0 || val > 0xffff) {\n return ip.toLowerCase().trim();\n }\n hextets.push(val);\n }\n for (let i = 0; i < missingZeros; i++) {\n hextets.push(0);\n }\n for (const t of tail) {\n const val = parseInt(t, 16);\n if (isNaN(val) || val < 0 || val > 0xffff) {\n return ip.toLowerCase().trim();\n }\n hextets.push(val);\n }\n\n if (hextets.length !== 8) {\n return ip.toLowerCase().trim();\n }\n\n if (embeddedIpv4 && embeddedIpv4.length === 4) {\n hextets[6] = ((embeddedIpv4[0] ?? 0) << 8) | (embeddedIpv4[1] ?? 0);\n hextets[7] = ((embeddedIpv4[2] ?? 0) << 8) | (embeddedIpv4[3] ?? 0);\n }\n\n // IPv4-mapped IPv6: ::ffff:w.x.y.z or 0:0:0:0:0:ffff:w.x.y.z\n const isIpv4Mapped =\n hextets[0] === 0 &&\n hextets[1] === 0 &&\n hextets[2] === 0 &&\n hextets[3] === 0 &&\n hextets[4] === 0 &&\n hextets[5] === 0xffff;\n if (isIpv4Mapped) {\n const h6 = hextets[6] ?? 0;\n const h7 = hextets[7] ?? 0;\n const b0 = (h6 >> 8) & 0xff;\n const b1 = h6 & 0xff;\n const b2 = (h7 >> 8) & 0xff;\n const b3 = h7 & 0xff;\n return `${b0}.${b1}.${b2}.${b3}`;\n }\n\n // IPv6 Loopback: ::1 or 0:0:0:0:0:0:0:1 -> 127.0.0.1\n const isLoopback = hextets.slice(0, 7).every((h) => h === 0) && hextets[7] === 1;\n if (isLoopback) {\n return '127.0.0.1';\n }\n\n // Find longest contiguous run of zeros (length >= 2) under RFC 5952\n let bestStart = -1;\n let bestLen = 0;\n let curStart = -1;\n let curLen = 0;\n\n for (let i = 0; i < 8; i++) {\n if (hextets[i] === 0) {\n if (curStart === -1) {\n curStart = i;\n curLen = 1;\n } else {\n curLen++;\n }\n if (curLen > bestLen) {\n bestStart = curStart;\n bestLen = curLen;\n }\n } else {\n curStart = -1;\n curLen = 0;\n }\n }\n\n // If no zero sequence of 2 or more hextets exists, join all 8 hextets\n if (bestLen < 2) {\n return hextets.map((h) => h.toString(16)).join(':');\n }\n\n const headStr = hextets.slice(0, bestStart).map((h) => h.toString(16)).join(':');\n const tailStr = hextets.slice(bestStart + bestLen).map((h) => h.toString(16)).join(':');\n return `${headStr}::${tailStr}`;\n}\n\n/**\n * Normalizes an IP address for consistent comparison across IPv4 and IPv6.\n * - Trims whitespace\n * - Canonicalizes IPv6 addresses according to RFC 5952 (zero compression, lowercase)\n * - Unmaps all IPv4-mapped IPv6 variations (e.g. '::ffff:192.168.1.1' -> '192.168.1.1')\n * - Normalizes all IPv6 loopback variants ('::1', '0:0:0:0:0:0:0:1') to '127.0.0.1'\n * - Preserves invalid strings without converting them into fake valid IPs\n *\n * @param ip - Raw client IP candidate\n * @returns Normalized IP or 'unknown'\n */\nexport function normalizeIp(ip: unknown): string {\n if (typeof ip !== 'string') {\n return 'unknown';\n }\n const trimmed = ip.trim();\n if (trimmed.length === 0) {\n return 'unknown';\n }\n\n // Standard valid IPv4\n const ipType = net.isIP(trimmed);\n if (ipType === 4) {\n return trimmed;\n }\n\n // Standard valid IPv6 (including loopback and IPv4-mapped)\n if (ipType === 6) {\n return canonicalizeIpv6(trimmed);\n }\n\n // Handle possible ::ffff:192.0.2.1 where net.isIP might return 0\n const lower = trimmed.toLowerCase();\n if (lower.startsWith('::ffff:')) {\n const candidateIpv4 = trimmed.substring(7);\n if (net.isIP(candidateIpv4) === 4) {\n return candidateIpv4;\n }\n }\n\n return trimmed;\n}\n\n/**\n * Validates whether an IP address is a valid IPv4 or IPv6 string.\n * Uses Node.js built-in net.isIP (returns 4 for IPv4, 6 for IPv6, 0 for invalid).\n *\n * @param ip - IP candidate string\n * @returns True if valid IPv4 or IPv6\n */\nexport function isValidIp(ip: unknown): boolean {\n if (typeof ip !== 'string' || ip.trim().length === 0) {\n return false;\n }\n const norm = normalizeIp(ip);\n return net.isIP(norm) !== 0;\n}\n\n/**\n * Parses block duration into milliseconds.\n * Supports numbers (milliseconds) or human-readable strings ('30s', '10m', '1h', '2d').\n *\n * @param duration - Numeric ms or formatted string\n * @returns Duration in milliseconds\n */\nexport function parseDuration(duration: number | string): number {\n if (typeof duration === 'number') {\n const ms = Math.floor(duration);\n if (!Number.isFinite(duration) || duration <= 0 || ms <= 0) {\n throw new TypeError('[Z-Shield] Block duration must be a positive number in milliseconds.');\n }\n return ms;\n }\n\n if (typeof duration === 'string') {\n const trimmed = duration.trim();\n const match = trimmed.match(/^(-?\\d+(?:\\.\\d+)?)\\s*(ms|s|m|h|d)?$/i);\n if (!match) {\n throw new Error(`[Z-Shield] Invalid duration format: \"${duration}\". Use format like \"10m\", \"1h\", \"30s\", or milliseconds.`);\n }\n const rawVal = match[1];\n if (rawVal === undefined) {\n throw new Error('[Z-Shield] Block duration must be greater than zero.');\n }\n const val = parseFloat(rawVal);\n if (!Number.isFinite(val) || val <= 0) {\n throw new Error('[Z-Shield] Block duration must be greater than zero.');\n }\n const unit = (match[2] || 'ms').toLowerCase();\n let ms: number;\n switch (unit) {\n case 'ms': ms = Math.floor(val); break;\n case 's': ms = Math.floor(val * 1000); break;\n case 'm': ms = Math.floor(val * 60 * 1000); break;\n case 'h': ms = Math.floor(val * 60 * 60 * 1000); break;\n case 'd': ms = Math.floor(val * 24 * 60 * 60 * 1000); break;\n default: ms = Math.floor(val); break;\n }\n if (ms <= 0) {\n throw new Error('[Z-Shield] Block duration must be greater than zero.');\n }\n return ms;\n }\n\n throw new TypeError('[Z-Shield] Block duration must be a number (milliseconds) or a duration string like \"10m\".');\n}\n\n/**\n * BlockManager - In-Memory Temporary IP Block Management\n *\n * Characteristics:\n * - Process-Local: Blocks are held in RAM. They do NOT survive server restarts.\n * - Automatic Expiry: Block entries are checked against their expiration timestamp.\n * - Bounded Memory: Uses LRU eviction when `maxBlockedIps` is reached to prevent memory exhaustion.\n * - No Background Timers: Uses passive/lazy expiration on access to keep Node.js event loop idle.\n */\nexport class BlockManager {\n readonly defaultDurationMs: number;\n readonly maxBlockedIps: number;\n private readonly blocks: Map<string, InternalBlockEntry>;\n\n /**\n * @param options - Block manager options or resolved config\n */\n constructor(options: BlockManagerOptions | ZShieldConfig | ZShieldOptions = {}) {\n this.defaultDurationMs = options.blockDurationMs || 15 * 60 * 1000;\n this.maxBlockedIps = options.maxBlockedIps || 5000;\n\n this.blocks = new Map<string, InternalBlockEntry>();\n }\n\n /**\n * Temporarily blocks an IP address.\n *\n * @param ip - Valid IPv4 or IPv6 address\n * @param duration - Duration in ms or string ('10m')\n * @param reason - Reason for block\n * @param now - Current timestamp (injectable for testing)\n * @returns Created block record\n */\n block(\n ip: string,\n duration: number | string = this.defaultDurationMs,\n reason: string = 'Administrative block',\n now: number = Date.now()\n ): BlockRecord {\n if (!isValidIp(ip)) {\n throw new TypeError(`[Z-Shield] Cannot block invalid IP address: \"${ip}\".`);\n }\n\n const durationMs = parseDuration(duration);\n const cleanIp = normalizeIp(ip);\n\n // Bound memory: if at capacity, evict an old entry before inserting\n if (this.blocks.size >= this.maxBlockedIps && !this.blocks.has(cleanIp)) {\n this._evictOldest(now);\n }\n\n const entry: InternalBlockEntry = {\n blockedAt: now,\n expiresAt: now + durationMs,\n reason: String(reason || 'Administrative block')\n };\n\n // Refresh LRU order: delete if exists, then set\n this.blocks.delete(cleanIp);\n this.blocks.set(cleanIp, entry);\n\n return {\n ip: cleanIp,\n blockedAt: entry.blockedAt,\n expiresAt: entry.expiresAt,\n durationMs,\n reason: entry.reason\n };\n }\n\n /**\n * Manually unblocks an IP address.\n *\n * @param ip - Client IP\n * @returns True if IP was unblocked, false if it was not blocked\n */\n unblock(ip: string): boolean {\n if (!isValidIp(ip)) {\n throw new TypeError(`[Z-Shield] Cannot unblock invalid IP address: \"${ip}\".`);\n }\n return this.blocks.delete(normalizeIp(ip));\n }\n\n /**\n * Checks whether an IP address is currently blocked.\n * Expired blocks are lazily pruned.\n *\n * @param ip - Client IP\n * @param now - Current timestamp\n * @returns True if actively blocked\n */\n isBlocked(ip: string | undefined | null, now: number = Date.now()): boolean {\n if (!ip || typeof ip !== 'string') {\n return false;\n }\n\n const cleanIp = normalizeIp(ip);\n const entry = this.blocks.get(cleanIp);\n\n if (!entry) {\n return false;\n }\n\n // Lazy expiration check\n if (now >= entry.expiresAt) {\n this.blocks.delete(cleanIp);\n return false;\n }\n\n return true;\n }\n\n /**\n * Retrieves active block details for an IP, or null if not blocked/expired.\n *\n * @param ip - Client IP\n * @param now - Current timestamp\n * @returns Block information or null\n */\n getBlockInfo(ip: string, now: number = Date.now()): BlockInfo | null {\n if (!this.isBlocked(ip, now)) {\n return null;\n }\n\n const cleanIp = normalizeIp(ip);\n const entry = this.blocks.get(cleanIp);\n if (!entry) {\n return null;\n }\n\n return {\n ip: cleanIp,\n blockedAt: entry.blockedAt,\n expiresAt: entry.expiresAt,\n remainingMs: Math.max(0, entry.expiresAt - now),\n reason: entry.reason\n };\n }\n\n /**\n * Returns a list of all currently active blocked IPs.\n *\n * @param now - Current timestamp\n * @returns Array of active block records\n */\n listBlocks(now: number = Date.now()): BlockInfo[] {\n this.pruneExpired(now);\n const result: BlockInfo[] = [];\n for (const [ip, entry] of this.blocks.entries()) {\n result.push({\n ip,\n blockedAt: entry.blockedAt,\n expiresAt: entry.expiresAt,\n remainingMs: Math.max(0, entry.expiresAt - now),\n reason: entry.reason\n });\n }\n return result;\n }\n\n /**\n * Scans and purges any expired blocks from memory.\n *\n * @param now - Current timestamp\n * @returns Number of purged blocks\n */\n pruneExpired(now: number = Date.now()): number {\n let pruned = 0;\n for (const [ip, entry] of this.blocks.entries()) {\n if (now >= entry.expiresAt) {\n this.blocks.delete(ip);\n pruned++;\n }\n }\n return pruned;\n }\n\n /**\n * Clears all blocks from memory.\n */\n clear(): void {\n this.blocks.clear();\n }\n\n /**\n * Number of IPs currently stored in memory.\n */\n get size(): number {\n return this.blocks.size;\n }\n\n /**\n * Evicts the oldest entry to strictly uphold maxBlockedIps limit.\n *\n * @param _now - Current timestamp\n */\n private _evictOldest(_now: number): void {\n const iterator = this.blocks.keys();\n const firstKey = iterator.next().value;\n\n if (firstKey !== undefined) {\n this.blocks.delete(firstKey);\n }\n }\n}\n"]}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
export interface ParsedCliArgs {
|
|
3
|
+
command: string;
|
|
4
|
+
args: string[];
|
|
5
|
+
options: Record<string, string | boolean>;
|
|
6
|
+
}
|
|
7
|
+
export type ColorFormatter = (str: string | number | undefined | null) => string;
|
|
8
|
+
export interface ColorMap {
|
|
9
|
+
bold: ColorFormatter;
|
|
10
|
+
dim: ColorFormatter;
|
|
11
|
+
italic: ColorFormatter;
|
|
12
|
+
underline: ColorFormatter;
|
|
13
|
+
inverse: ColorFormatter;
|
|
14
|
+
red: ColorFormatter;
|
|
15
|
+
green: ColorFormatter;
|
|
16
|
+
yellow: ColorFormatter;
|
|
17
|
+
blue: ColorFormatter;
|
|
18
|
+
magenta: ColorFormatter;
|
|
19
|
+
cyan: ColorFormatter;
|
|
20
|
+
white: ColorFormatter;
|
|
21
|
+
gray: ColorFormatter;
|
|
22
|
+
enabled: boolean;
|
|
23
|
+
hasColors: boolean;
|
|
24
|
+
}
|
|
25
|
+
export interface CliOutput {
|
|
26
|
+
log: (...args: unknown[]) => void;
|
|
27
|
+
error: (...args: unknown[]) => void;
|
|
28
|
+
isTTY?: boolean;
|
|
29
|
+
}
|
|
30
|
+
export interface LogReadResult {
|
|
31
|
+
events: Record<string, unknown>[];
|
|
32
|
+
totalLines: number;
|
|
33
|
+
malformedCount: number;
|
|
34
|
+
fileExists: boolean;
|
|
35
|
+
}
|
|
36
|
+
export interface StatusResult {
|
|
37
|
+
fileExists: boolean;
|
|
38
|
+
totalEvents: number;
|
|
39
|
+
malformedCount?: number;
|
|
40
|
+
typeCounts?: Record<string, number>;
|
|
41
|
+
actionCounts?: Record<string, number>;
|
|
42
|
+
topIps?: Array<{
|
|
43
|
+
ip: string;
|
|
44
|
+
count: number;
|
|
45
|
+
}>;
|
|
46
|
+
topRoutes?: Array<{
|
|
47
|
+
route: string;
|
|
48
|
+
count: number;
|
|
49
|
+
}>;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Parses raw command-line arguments into a structured command object.
|
|
53
|
+
*
|
|
54
|
+
* @param argv - Array of CLI arguments (typically process.argv.slice(2))
|
|
55
|
+
* @returns Structured CLI command, positional args, and parsed options
|
|
56
|
+
*/
|
|
57
|
+
export declare function parseCliArgs(argv?: string[]): ParsedCliArgs;
|
|
58
|
+
/**
|
|
59
|
+
* Resolves the target JSON Lines log file path based on CLI options or defaults.
|
|
60
|
+
*
|
|
61
|
+
* @param options - Parsed CLI options
|
|
62
|
+
* @returns Absolute path to events.jsonl
|
|
63
|
+
*/
|
|
64
|
+
export declare function resolveLogPath(options?: Record<string, unknown>): string;
|
|
65
|
+
/**
|
|
66
|
+
* Creates an ANSI color styling helper object.
|
|
67
|
+
* When enabled is false, all styling methods act as plain passthroughs.
|
|
68
|
+
*
|
|
69
|
+
* @param enabled - Whether color styling is active
|
|
70
|
+
* @returns Color styling map
|
|
71
|
+
*/
|
|
72
|
+
export declare function createColors(enabled?: boolean): ColorMap;
|
|
73
|
+
/**
|
|
74
|
+
* Determines whether terminal colors should be enabled based on
|
|
75
|
+
* explicit options, environment variables, and TTY capabilities.
|
|
76
|
+
*
|
|
77
|
+
* Respects the standard NO_COLOR specification (https://no-color.org).
|
|
78
|
+
*
|
|
79
|
+
* @param options - Parsed CLI options
|
|
80
|
+
* @param output - Output destination (console or mock stream)
|
|
81
|
+
* @returns True if colors should be enabled
|
|
82
|
+
*/
|
|
83
|
+
export declare function resolveColorSupport(options?: Record<string, unknown>, output?: CliOutput | Console | null): boolean;
|
|
84
|
+
/**
|
|
85
|
+
* Formats a single security event record into a clean, sanitized terminal line.
|
|
86
|
+
* Strips all ANSI escapes and control characters from untrusted log fields before formatting.
|
|
87
|
+
*
|
|
88
|
+
* @param event - Security event object
|
|
89
|
+
* @param options - Options object or boolean for color
|
|
90
|
+
* @returns Sanitized output line
|
|
91
|
+
*/
|
|
92
|
+
export declare function formatLogEvent(event: Record<string, unknown> | null | undefined, options?: Record<string, unknown> | boolean): string;
|
|
93
|
+
/**
|
|
94
|
+
* Reads and parses the last N records from the JSON Lines file.
|
|
95
|
+
* Handles missing files and skips malformed records gracefully.
|
|
96
|
+
*
|
|
97
|
+
* @param logFilePath - Path to log file
|
|
98
|
+
* @param limit - Max number of events to return
|
|
99
|
+
* @returns Extracted events and counts
|
|
100
|
+
*/
|
|
101
|
+
export declare function readLogs(logFilePath: string, limit?: number): LogReadResult;
|
|
102
|
+
/**
|
|
103
|
+
* Sanitizes untrusted IP address values from logs for safe status aggregation and output.
|
|
104
|
+
* Strips ANSI escapes, truncates at newlines/control characters, and extracts valid IP addresses.
|
|
105
|
+
*
|
|
106
|
+
* @param rawIp - Untrusted IP candidate from logs
|
|
107
|
+
* @returns Normalized IP address or 'unknown'
|
|
108
|
+
*/
|
|
109
|
+
export declare function sanitizeIp(rawIp: unknown): string;
|
|
110
|
+
/**
|
|
111
|
+
* Sanitizes untrusted event type strings from logs.
|
|
112
|
+
*
|
|
113
|
+
* @param rawType - Untrusted event type string
|
|
114
|
+
* @param maxLength - Maximum permitted length
|
|
115
|
+
* @returns Clean event type string
|
|
116
|
+
*/
|
|
117
|
+
export declare function sanitizeType(rawType: unknown, maxLength?: number): string;
|
|
118
|
+
/**
|
|
119
|
+
* Sanitizes untrusted action strings from logs.
|
|
120
|
+
*
|
|
121
|
+
* @param rawAction - Untrusted action string
|
|
122
|
+
* @param maxLength - Maximum permitted length
|
|
123
|
+
* @returns Clean action string
|
|
124
|
+
*/
|
|
125
|
+
export declare function sanitizeAction(rawAction: unknown, maxLength?: number): string;
|
|
126
|
+
/**
|
|
127
|
+
* Computes security summary statistics from the log file.
|
|
128
|
+
*
|
|
129
|
+
* @param logFilePath - Path to log file
|
|
130
|
+
* @returns Summary metrics
|
|
131
|
+
*/
|
|
132
|
+
export declare function calculateStatus(logFilePath: string): StatusResult;
|
|
133
|
+
/**
|
|
134
|
+
* Main command runner. Returns an exit code (0 for success, 1 for error).
|
|
135
|
+
*
|
|
136
|
+
* @param argv - Arguments array
|
|
137
|
+
* @param output - Output destination (console or mock object)
|
|
138
|
+
* @returns Exit code
|
|
139
|
+
*/
|
|
140
|
+
export declare function runCommand(argv?: string[], output?: CliOutput | Console): Promise<number>;
|
|
141
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|