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.
Files changed (52) hide show
  1. package/README.md +123 -6
  2. package/dist/block-manager.d.ts +134 -0
  3. package/dist/block-manager.d.ts.map +1 -0
  4. package/dist/block-manager.js +425 -0
  5. package/dist/block-manager.js.map +1 -0
  6. package/dist/cli.d.ts +141 -0
  7. package/dist/cli.d.ts.map +1 -0
  8. package/dist/cli.js +784 -0
  9. package/dist/cli.js.map +1 -0
  10. package/dist/config.d.ts +15 -0
  11. package/dist/config.d.ts.map +1 -0
  12. package/dist/config.js +148 -0
  13. package/dist/config.js.map +1 -0
  14. package/dist/detector.d.ts +56 -0
  15. package/dist/detector.d.ts.map +1 -0
  16. package/dist/detector.js +81 -0
  17. package/dist/detector.js.map +1 -0
  18. package/dist/index.d.ts +29 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +120 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/logger.d.ts +141 -0
  23. package/dist/logger.d.ts.map +1 -0
  24. package/dist/logger.js +347 -0
  25. package/dist/logger.js.map +1 -0
  26. package/dist/middleware.d.ts +68 -0
  27. package/dist/middleware.d.ts.map +1 -0
  28. package/dist/middleware.js +272 -0
  29. package/dist/middleware.js.map +1 -0
  30. package/dist/next.d.ts +73 -0
  31. package/dist/next.d.ts.map +1 -0
  32. package/dist/next.js +486 -0
  33. package/dist/next.js.map +1 -0
  34. package/dist/rate-limiter.d.ts +75 -0
  35. package/dist/rate-limiter.d.ts.map +1 -0
  36. package/dist/rate-limiter.js +153 -0
  37. package/dist/rate-limiter.js.map +1 -0
  38. package/dist/types.d.ts +199 -0
  39. package/dist/types.d.ts.map +1 -0
  40. package/dist/types.js +6 -0
  41. package/dist/types.js.map +1 -0
  42. package/next.d.ts +1 -0
  43. package/next.js +2 -0
  44. package/package.json +34 -5
  45. package/src/block-manager.js +0 -326
  46. package/src/cli.js +0 -764
  47. package/src/config.js +0 -165
  48. package/src/detector.js +0 -88
  49. package/src/index.js +0 -97
  50. package/src/logger.js +0 -280
  51. package/src/middleware.js +0 -288
  52. package/src/rate-limiter.js +0 -186
package/dist/next.js ADDED
@@ -0,0 +1,486 @@
1
+ "use strict";
2
+ /**
3
+ * Next.js App Router Compatibility Adapter for Z-Shield.
4
+ *
5
+ * Provides Web-standard Request and Response security middleware for Next.js App Router
6
+ * route handlers (e.g. app/api/.../route.ts).
7
+ *
8
+ * Runtime requirement: Node.js runtime (`export const runtime = 'nodejs';`).
9
+ * State note: In-memory rate limiting and blocking state is process-local.
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.extractNextClientIp = extractNextClientIp;
13
+ exports.createNextShield = createNextShield;
14
+ exports.withNextShield = withNextShield;
15
+ const config_1 = require("./config");
16
+ const rate_limiter_1 = require("./rate-limiter");
17
+ const detector_1 = require("./detector");
18
+ const block_manager_1 = require("./block-manager");
19
+ const logger_1 = require("./logger");
20
+ const middleware_1 = require("./middleware");
21
+ /**
22
+ * Reads a header value from standard Headers or a plain object map.
23
+ */
24
+ function getHeader(request, name) {
25
+ if (!request || !request.headers) {
26
+ return null;
27
+ }
28
+ if (typeof request.headers.get === 'function') {
29
+ return request.headers.get(name);
30
+ }
31
+ const headersObj = request.headers;
32
+ const lowerName = name.toLowerCase();
33
+ let val = headersObj[name] ?? headersObj[lowerName];
34
+ if (val === undefined) {
35
+ for (const key of Object.keys(headersObj)) {
36
+ if (key.toLowerCase() === lowerName) {
37
+ val = headersObj[key];
38
+ break;
39
+ }
40
+ }
41
+ }
42
+ if (Array.isArray(val)) {
43
+ return val[0] ?? null;
44
+ }
45
+ return typeof val === 'string' ? val : null;
46
+ }
47
+ /**
48
+ * Extracts and normalizes the client IP address from a Web-standard or Next.js Request.
49
+ *
50
+ * Security Model:
51
+ * 1. When `trustProxy` is false:
52
+ * - All reverse-proxy headers (X-Forwarded-For, X-Real-IP, CF-Connecting-IP, etc.)
53
+ * and unverified platform properties are strictly ignored to prevent client IP spoofing attacks.
54
+ * - Only a verified Node.js TCP socket remote address (`socket.remoteAddress`) or an
55
+ * explicitly provided custom extractor function is accepted.
56
+ * - In environments without a direct TCP socket (e.g. serverless Web Requests without a trusted proxy),
57
+ * safe client IP identification cannot be guaranteed, and this function safely falls back to 'unknown'.
58
+ *
59
+ * 2. When `trustProxy` is true:
60
+ * - To prevent forged header injection across reverse proxies (e.g. an attacker sending CF-Connecting-IP
61
+ * to an Nginx server that does not strip it), Z-Shield strictly requires selecting an explicit trusted
62
+ * strategy via `getIp` or providing a custom extraction callback:
63
+ * - 'cloudflare' | 'cf-connecting-ip': Trusts only the CF-Connecting-IP header.
64
+ * - 'nginx' | 'x-real-ip': Trusts only the X-Real-IP header.
65
+ * - 'akamai' | 'true-client-ip': Trusts only the True-Client-IP header.
66
+ * - 'x-forwarded-for': Trusts the first valid IP in X-Forwarded-For (requires outer proxy header stripping).
67
+ * - Custom function: `(req) => string | null | undefined`
68
+ * - If `trustProxy` is true but no extraction strategy or custom function is configured,
69
+ * Z-Shield strictly refuses to guess across foreign proxy headers, and falls back safely to 'unknown'
70
+ * (or socket.remoteAddress if available).
71
+ *
72
+ * @param request - Incoming Web Request or NextLikeRequest
73
+ * @param trustProxy - Whether reverse proxy headers are trusted
74
+ * @param extractorOrStrategy - Trusted IP strategy name or custom extractor function
75
+ * @returns Normalized client IP address or 'unknown'
76
+ */
77
+ function extractNextClientIp(request, trustProxy, extractorOrStrategy) {
78
+ // 1. Custom extractor callback function takes precedence if provided by developer
79
+ if (typeof extractorOrStrategy === 'function') {
80
+ try {
81
+ const customIp = extractorOrStrategy(request);
82
+ if (typeof customIp === 'string' && customIp.trim().length > 0) {
83
+ const trimmed = customIp.trim();
84
+ if ((0, block_manager_1.isValidIp)(trimmed)) {
85
+ return (0, block_manager_1.normalizeIp)(trimmed);
86
+ }
87
+ }
88
+ }
89
+ catch {
90
+ // Gracefully fall through on extractor errors
91
+ }
92
+ return 'unknown';
93
+ }
94
+ const reqWithIp = request;
95
+ // 2. When trustProxy is false:
96
+ // Strictly ignore all reverse-proxy headers (X-Forwarded-For, X-Real-IP, etc.)
97
+ // and unverified platform headers to prevent client IP spoofing attacks.
98
+ if (!trustProxy) {
99
+ if (typeof reqWithIp.socket?.remoteAddress === 'string' && reqWithIp.socket.remoteAddress.length > 0) {
100
+ return (0, block_manager_1.normalizeIp)(reqWithIp.socket.remoteAddress);
101
+ }
102
+ // If no direct TCP socket is available (e.g. serverless Web Request without trusted proxy),
103
+ // safe client IP identification cannot be guaranteed. Fallback to 'unknown'.
104
+ return 'unknown';
105
+ }
106
+ // 3. When trustProxy is true:
107
+ // An explicit trusted IP extraction strategy must be selected.
108
+ // Z-Shield strictly refuses to guess across foreign proxy headers to prevent
109
+ // forged header injection attacks (e.g. sending CF-Connecting-IP to an Nginx server).
110
+ if (typeof extractorOrStrategy === 'string') {
111
+ const strategy = extractorOrStrategy.toLowerCase().trim();
112
+ if (strategy === 'cf-connecting-ip' || strategy === 'cloudflare') {
113
+ const cfIp = getHeader(request, 'cf-connecting-ip');
114
+ if (cfIp) {
115
+ const candidate = cfIp.trim();
116
+ if ((0, block_manager_1.isValidIp)(candidate)) {
117
+ return (0, block_manager_1.normalizeIp)(candidate);
118
+ }
119
+ }
120
+ return 'unknown';
121
+ }
122
+ if (strategy === 'x-real-ip' || strategy === 'nginx') {
123
+ const xRealIp = getHeader(request, 'x-real-ip');
124
+ if (xRealIp) {
125
+ const candidate = xRealIp.trim();
126
+ if ((0, block_manager_1.isValidIp)(candidate)) {
127
+ return (0, block_manager_1.normalizeIp)(candidate);
128
+ }
129
+ }
130
+ return 'unknown';
131
+ }
132
+ if (strategy === 'true-client-ip' || strategy === 'akamai') {
133
+ const trueIp = getHeader(request, 'true-client-ip');
134
+ if (trueIp) {
135
+ const candidate = trueIp.trim();
136
+ if ((0, block_manager_1.isValidIp)(candidate)) {
137
+ return (0, block_manager_1.normalizeIp)(candidate);
138
+ }
139
+ }
140
+ return 'unknown';
141
+ }
142
+ if (strategy === 'x-forwarded-for') {
143
+ const xff = getHeader(request, 'x-forwarded-for');
144
+ if (xff) {
145
+ const candidates = xff.split(',').map(s => s.trim()).filter(Boolean);
146
+ for (const candidate of candidates) {
147
+ if ((0, block_manager_1.isValidIp)(candidate)) {
148
+ return (0, block_manager_1.normalizeIp)(candidate);
149
+ }
150
+ }
151
+ }
152
+ return 'unknown';
153
+ }
154
+ }
155
+ // 4. Fallback if no strategy matched or no proxy header was trusted:
156
+ // If a direct TCP socket is available, return it; otherwise fail safely to 'unknown'.
157
+ if (typeof reqWithIp.socket?.remoteAddress === 'string' && reqWithIp.socket.remoteAddress.length > 0) {
158
+ return (0, block_manager_1.normalizeIp)(reqWithIp.socket.remoteAddress);
159
+ }
160
+ return 'unknown';
161
+ }
162
+ /**
163
+ * Extracts pathname from a request URL safely.
164
+ */
165
+ function extractRequestPath(request) {
166
+ if (!request.url) {
167
+ return '/';
168
+ }
169
+ try {
170
+ const parsed = new URL(request.url, 'http://localhost');
171
+ return parsed.pathname;
172
+ }
173
+ catch {
174
+ return (request.url.split('?')[0] ?? '').split('#')[0] || '/';
175
+ }
176
+ }
177
+ /**
178
+ * Merges rate-limiting headers into an existing Web Response.
179
+ */
180
+ function mergeResponseHeaders(response, headersToMerge) {
181
+ if (!response) {
182
+ return response;
183
+ }
184
+ try {
185
+ for (const [key, value] of Object.entries(headersToMerge)) {
186
+ response.headers.set(key, value);
187
+ }
188
+ return response;
189
+ }
190
+ catch {
191
+ const newHeaders = new Headers(response.headers);
192
+ for (const [key, value] of Object.entries(headersToMerge)) {
193
+ newHeaders.set(key, value);
194
+ }
195
+ const nullBodyStatuses = [101, 204, 205, 304];
196
+ const body = nullBodyStatuses.includes(response.status)
197
+ ? null
198
+ : response.body;
199
+ return new Response(body, {
200
+ status: response.status,
201
+ statusText: response.statusText,
202
+ headers: newHeaders
203
+ });
204
+ }
205
+ }
206
+ /**
207
+ * Creates standard HTTP 429 response when a request is blocked.
208
+ */
209
+ function createBlockedResponse(retryAfter, reason, extraHeaders, customBuilder, clientIp) {
210
+ if (typeof customBuilder === 'function') {
211
+ try {
212
+ return customBuilder({
213
+ reason,
214
+ retryAfter,
215
+ clientIp: clientIp || 'unknown'
216
+ });
217
+ }
218
+ catch {
219
+ // Fallback to standard response if custom builder throws
220
+ }
221
+ }
222
+ const headers = {
223
+ 'Content-Type': 'application/json',
224
+ 'Retry-After': String(retryAfter),
225
+ 'X-Z-Shield-Blocked': '1',
226
+ ...extraHeaders
227
+ };
228
+ const body = {
229
+ error: 'Too Many Requests',
230
+ message: reason,
231
+ retryAfter
232
+ };
233
+ return new Response(JSON.stringify(body), {
234
+ status: 429,
235
+ headers
236
+ });
237
+ }
238
+ /**
239
+ * Checks if the passed object is an existing ZShieldInstance.
240
+ */
241
+ function isZShieldInstance(val) {
242
+ return (typeof val === 'object' &&
243
+ val !== null &&
244
+ 'rateLimiter' in val &&
245
+ 'detector' in val &&
246
+ 'blockManager' in val &&
247
+ 'logger' in val &&
248
+ 'config' in val);
249
+ }
250
+ /**
251
+ * Factory for creating a Next.js App Router shield instance.
252
+ *
253
+ * Usage:
254
+ * import { createNextShield } from 'z-shield';
255
+ * const shield = createNextShield({ mode: 'protect', maxRequests: 50 });
256
+ *
257
+ * export const GET = shield.protect(async (request) => {
258
+ * return Response.json({ message: 'Hello from protected API' });
259
+ * });
260
+ *
261
+ * @param optionsOrInstance - Configuration options or existing ZShieldInstance
262
+ * @returns NextShieldInstance
263
+ */
264
+ function createNextShield(optionsOrInstance, adapterOptions) {
265
+ let config;
266
+ let rateLimiter;
267
+ let detector;
268
+ let blockManager;
269
+ let logger;
270
+ let customGetIp;
271
+ let customBlockResponse;
272
+ if (isZShieldInstance(optionsOrInstance)) {
273
+ config = optionsOrInstance.config;
274
+ rateLimiter = optionsOrInstance.rateLimiter;
275
+ detector = optionsOrInstance.detector;
276
+ blockManager = optionsOrInstance.blockManager;
277
+ logger = optionsOrInstance.logger;
278
+ if (adapterOptions?.getIp) {
279
+ customGetIp = adapterOptions.getIp;
280
+ }
281
+ if (adapterOptions?.customBlockResponse) {
282
+ customBlockResponse = adapterOptions.customBlockResponse;
283
+ }
284
+ }
285
+ else {
286
+ const opts = optionsOrInstance || {};
287
+ config = (0, config_1.resolveConfig)(opts);
288
+ rateLimiter = new rate_limiter_1.RateLimiter(config);
289
+ detector = new detector_1.Detector({ rateLimiter, config });
290
+ blockManager = new block_manager_1.BlockManager(config);
291
+ logger = new logger_1.Logger(config);
292
+ if ('getIp' in opts && (typeof opts.getIp === 'function' || typeof opts.getIp === 'string')) {
293
+ customGetIp = opts.getIp;
294
+ }
295
+ if ('customBlockResponse' in opts && typeof opts.customBlockResponse === 'function') {
296
+ customBlockResponse = opts.customBlockResponse;
297
+ }
298
+ }
299
+ const check = async (request) => {
300
+ const pathname = extractRequestPath(request);
301
+ const method = (request.method || 'GET').toUpperCase();
302
+ const now = Date.now();
303
+ // 1. Route exclusion check
304
+ if ((0, middleware_1.isRouteExcluded)(pathname, config.excludeRoutes)) {
305
+ return {
306
+ blocked: false,
307
+ isExcluded: true,
308
+ clientIp: '',
309
+ headers: {}
310
+ };
311
+ }
312
+ // 2. Client IP extraction
313
+ const clientIp = extractNextClientIp(request, config.trustProxy, customGetIp);
314
+ // 3. BlockManager check
315
+ const isIpBlocked = blockManager.isBlocked(clientIp, now);
316
+ if (isIpBlocked) {
317
+ const blockInfo = blockManager.getBlockInfo(clientIp, now);
318
+ const reason = blockInfo ? blockInfo.reason : 'Administrative block';
319
+ const remainingMs = blockInfo ? blockInfo.remainingMs : 0;
320
+ const retryAfterSeconds = Math.max(1, Math.ceil(remainingMs / 1000));
321
+ const action = config.mode === 'protect' ? 'blocked' : 'warned';
322
+ void logger.log({
323
+ appName: config.appName,
324
+ type: 'blocked_ip',
325
+ severity: 'warning',
326
+ ip: clientIp,
327
+ method,
328
+ route: pathname,
329
+ reason,
330
+ action
331
+ });
332
+ const headers = {
333
+ 'X-Z-Shield-Protected': '1',
334
+ 'X-Z-Shield-Mode': config.mode,
335
+ 'X-Z-Shield-Blocked': '1',
336
+ 'X-Z-Shield-Reason': reason,
337
+ 'Retry-After': String(retryAfterSeconds)
338
+ };
339
+ if (config.mode === 'protect') {
340
+ const blockedResponse = createBlockedResponse(retryAfterSeconds, 'Your IP address has been temporarily blocked due to suspicious activity.', headers, customBlockResponse, clientIp);
341
+ return {
342
+ blocked: true,
343
+ isExcluded: false,
344
+ clientIp,
345
+ headers,
346
+ response: blockedResponse
347
+ };
348
+ }
349
+ // Monitor mode: mark warning header and allow request through
350
+ headers['X-Z-Shield-Suspicious'] = '1';
351
+ return {
352
+ blocked: false,
353
+ isExcluded: false,
354
+ clientIp,
355
+ headers
356
+ };
357
+ }
358
+ // 4. Rate Limiting assessment via Detector
359
+ const detection = detector.check(clientIp, request, now);
360
+ const rateLimitHeaders = {
361
+ 'X-Z-Shield-Protected': '1',
362
+ 'X-Z-Shield-Mode': config.mode,
363
+ 'X-RateLimit-Limit': String(config.maxRequests),
364
+ 'X-RateLimit-Remaining': String(detection.remaining),
365
+ 'X-RateLimit-Reset': String(Math.ceil(detection.resetTime / 1000))
366
+ };
367
+ if (detection.isSuspicious) {
368
+ const action = config.mode === 'protect' ? 'blocked' : 'warned';
369
+ void logger.log({
370
+ appName: config.appName,
371
+ type: detection.type || 'rate_limit',
372
+ severity: detection.severity || 'warning',
373
+ ip: clientIp,
374
+ method,
375
+ route: pathname,
376
+ reason: detection.reason || 'Threshold exceeded',
377
+ action
378
+ });
379
+ rateLimitHeaders['X-Z-Shield-Suspicious'] = '1';
380
+ rateLimitHeaders['X-Z-Shield-Reason'] = detection.reason || 'Threshold exceeded';
381
+ if (config.mode === 'protect') {
382
+ if (config.autoBlock && (0, block_manager_1.isValidIp)(clientIp)) {
383
+ blockManager.block(clientIp, config.blockDurationMs, detection.reason || 'Threshold exceeded', now);
384
+ }
385
+ const retryAfterSeconds = Math.max(1, Math.ceil((detection.resetTime - now) / 1000));
386
+ rateLimitHeaders['Retry-After'] = String(retryAfterSeconds);
387
+ rateLimitHeaders['X-Z-Shield-Blocked'] = '1';
388
+ const blockedResponse = createBlockedResponse(retryAfterSeconds, 'Rate limit exceeded. Please wait before retrying.', rateLimitHeaders, customBlockResponse, clientIp);
389
+ return {
390
+ blocked: true,
391
+ isExcluded: false,
392
+ clientIp,
393
+ detection,
394
+ headers: rateLimitHeaders,
395
+ response: blockedResponse
396
+ };
397
+ }
398
+ }
399
+ // 5. Allowed / normal request
400
+ return {
401
+ blocked: false,
402
+ isExcluded: false,
403
+ clientIp,
404
+ detection,
405
+ headers: rateLimitHeaders
406
+ };
407
+ };
408
+ const protect = (handler) => {
409
+ return async (request, context) => {
410
+ const checkResult = await check(request);
411
+ if (checkResult.blocked && checkResult.response) {
412
+ return checkResult.response;
413
+ }
414
+ const response = await handler(request, context);
415
+ if (checkResult.isExcluded || Object.keys(checkResult.headers).length === 0) {
416
+ return response;
417
+ }
418
+ return mergeResponseHeaders(response, checkResult.headers);
419
+ };
420
+ };
421
+ return {
422
+ config,
423
+ rateLimiter,
424
+ detector,
425
+ blockManager,
426
+ logger,
427
+ check,
428
+ protect,
429
+ // Programmatic Block API
430
+ block: (ip, duration, reason) => {
431
+ const result = blockManager.block(ip, duration, reason);
432
+ void logger.log({
433
+ appName: config.appName,
434
+ type: 'manual_block',
435
+ severity: 'info',
436
+ ip,
437
+ method: 'INTERNAL',
438
+ route: '/admin/block',
439
+ reason: result.reason,
440
+ action: 'blocked'
441
+ });
442
+ return result;
443
+ },
444
+ unblock: (ip) => {
445
+ const result = blockManager.unblock(ip);
446
+ if (result) {
447
+ void logger.log({
448
+ appName: config.appName,
449
+ type: 'manual_unblock',
450
+ severity: 'info',
451
+ ip,
452
+ method: 'INTERNAL',
453
+ route: '/admin/unblock',
454
+ reason: 'Manual unblock requested',
455
+ action: 'unblocked'
456
+ });
457
+ }
458
+ return result;
459
+ },
460
+ isBlocked: (ip) => blockManager.isBlocked(ip),
461
+ getBlockInfo: (ip) => blockManager.getBlockInfo(ip),
462
+ listBlocks: () => blockManager.listBlocks(),
463
+ flush: () => logger.flush()
464
+ };
465
+ }
466
+ /**
467
+ * Higher-order function wrapping a Next.js App Router route handler with Z-Shield protection.
468
+ *
469
+ * Usage:
470
+ * import { withNextShield } from 'z-shield';
471
+ *
472
+ * export const GET = withNextShield(async (req) => {
473
+ * return Response.json({ ok: true });
474
+ * }, { mode: 'protect', maxRequests: 50 });
475
+ *
476
+ * @param handler - Next.js Route Handler function
477
+ * @param options - Z-Shield configuration options or existing shield instance
478
+ * @returns Wrapped route handler
479
+ */
480
+ function withNextShield(handler, options, adapterOptions) {
481
+ const shield = options && 'protect' in options
482
+ ? options
483
+ : createNextShield(options, adapterOptions);
484
+ return shield.protect(handler);
485
+ }
486
+ //# sourceMappingURL=next.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"next.js","sourceRoot":"","sources":["../src/next.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Next.js App Router Compatibility Adapter for Z-Shield.\n *\n * Provides Web-standard Request and Response security middleware for Next.js App Router\n * route handlers (e.g. app/api/.../route.ts).\n *\n * Runtime requirement: Node.js runtime (`export const runtime = 'nodejs';`).\n * State note: In-memory rate limiting and blocking state is process-local.\n */\n\nimport { resolveConfig } from './config';\nimport { RateLimiter } from './rate-limiter';\nimport { Detector } from './detector';\nimport { BlockManager, isValidIp, normalizeIp, type BlockInfo } from './block-manager';\nimport { Logger } from './logger';\nimport { isRouteExcluded } from './middleware';\nimport type {\n ZShieldConfig,\n BlockRecord,\n ZShieldInstance,\n NextLikeRequest,\n NextShieldOptions,\n NextShieldCheckResult,\n NextShieldInstance,\n NextRouteHandler,\n NextClientIpStrategy,\n NextClientIpExtractor\n} from './types';\n\n/**\n * Reads a header value from standard Headers or a plain object map.\n */\nfunction getHeader(request: Request | NextLikeRequest, name: string): string | null {\n if (!request || !request.headers) {\n return null;\n }\n if (typeof (request.headers as Headers).get === 'function') {\n return (request.headers as Headers).get(name);\n }\n const headersObj = request.headers as Record<string, string | string[] | undefined>;\n const lowerName = name.toLowerCase();\n let val = headersObj[name] ?? headersObj[lowerName];\n if (val === undefined) {\n for (const key of Object.keys(headersObj)) {\n if (key.toLowerCase() === lowerName) {\n val = headersObj[key];\n break;\n }\n }\n }\n if (Array.isArray(val)) {\n return val[0] ?? null;\n }\n return typeof val === 'string' ? val : null;\n}\n\n/**\n * Extracts and normalizes the client IP address from a Web-standard or Next.js Request.\n *\n * Security Model:\n * 1. When `trustProxy` is false:\n * - All reverse-proxy headers (X-Forwarded-For, X-Real-IP, CF-Connecting-IP, etc.)\n * and unverified platform properties are strictly ignored to prevent client IP spoofing attacks.\n * - Only a verified Node.js TCP socket remote address (`socket.remoteAddress`) or an\n * explicitly provided custom extractor function is accepted.\n * - In environments without a direct TCP socket (e.g. serverless Web Requests without a trusted proxy),\n * safe client IP identification cannot be guaranteed, and this function safely falls back to 'unknown'.\n *\n * 2. When `trustProxy` is true:\n * - To prevent forged header injection across reverse proxies (e.g. an attacker sending CF-Connecting-IP\n * to an Nginx server that does not strip it), Z-Shield strictly requires selecting an explicit trusted\n * strategy via `getIp` or providing a custom extraction callback:\n * - 'cloudflare' | 'cf-connecting-ip': Trusts only the CF-Connecting-IP header.\n * - 'nginx' | 'x-real-ip': Trusts only the X-Real-IP header.\n * - 'akamai' | 'true-client-ip': Trusts only the True-Client-IP header.\n * - 'x-forwarded-for': Trusts the first valid IP in X-Forwarded-For (requires outer proxy header stripping).\n * - Custom function: `(req) => string | null | undefined`\n * - If `trustProxy` is true but no extraction strategy or custom function is configured,\n * Z-Shield strictly refuses to guess across foreign proxy headers, and falls back safely to 'unknown'\n * (or socket.remoteAddress if available).\n *\n * @param request - Incoming Web Request or NextLikeRequest\n * @param trustProxy - Whether reverse proxy headers are trusted\n * @param extractorOrStrategy - Trusted IP strategy name or custom extractor function\n * @returns Normalized client IP address or 'unknown'\n */\nexport function extractNextClientIp(\n request: Request | NextLikeRequest,\n trustProxy: boolean,\n extractorOrStrategy?: NextClientIpExtractor | NextClientIpStrategy\n): string {\n // 1. Custom extractor callback function takes precedence if provided by developer\n if (typeof extractorOrStrategy === 'function') {\n try {\n const customIp = extractorOrStrategy(request);\n if (typeof customIp === 'string' && customIp.trim().length > 0) {\n const trimmed = customIp.trim();\n if (isValidIp(trimmed)) {\n return normalizeIp(trimmed);\n }\n }\n } catch {\n // Gracefully fall through on extractor errors\n }\n return 'unknown';\n }\n\n const reqWithIp = request as { ip?: unknown; socket?: { remoteAddress?: unknown } };\n\n // 2. When trustProxy is false:\n // Strictly ignore all reverse-proxy headers (X-Forwarded-For, X-Real-IP, etc.)\n // and unverified platform headers to prevent client IP spoofing attacks.\n if (!trustProxy) {\n if (typeof reqWithIp.socket?.remoteAddress === 'string' && reqWithIp.socket.remoteAddress.length > 0) {\n return normalizeIp(reqWithIp.socket.remoteAddress);\n }\n // If no direct TCP socket is available (e.g. serverless Web Request without trusted proxy),\n // safe client IP identification cannot be guaranteed. Fallback to 'unknown'.\n return 'unknown';\n }\n\n // 3. When trustProxy is true:\n // An explicit trusted IP extraction strategy must be selected.\n // Z-Shield strictly refuses to guess across foreign proxy headers to prevent\n // forged header injection attacks (e.g. sending CF-Connecting-IP to an Nginx server).\n if (typeof extractorOrStrategy === 'string') {\n const strategy = extractorOrStrategy.toLowerCase().trim();\n\n if (strategy === 'cf-connecting-ip' || strategy === 'cloudflare') {\n const cfIp = getHeader(request, 'cf-connecting-ip');\n if (cfIp) {\n const candidate = cfIp.trim();\n if (isValidIp(candidate)) {\n return normalizeIp(candidate);\n }\n }\n return 'unknown';\n }\n\n if (strategy === 'x-real-ip' || strategy === 'nginx') {\n const xRealIp = getHeader(request, 'x-real-ip');\n if (xRealIp) {\n const candidate = xRealIp.trim();\n if (isValidIp(candidate)) {\n return normalizeIp(candidate);\n }\n }\n return 'unknown';\n }\n\n if (strategy === 'true-client-ip' || strategy === 'akamai') {\n const trueIp = getHeader(request, 'true-client-ip');\n if (trueIp) {\n const candidate = trueIp.trim();\n if (isValidIp(candidate)) {\n return normalizeIp(candidate);\n }\n }\n return 'unknown';\n }\n\n if (strategy === 'x-forwarded-for') {\n const xff = getHeader(request, 'x-forwarded-for');\n if (xff) {\n const candidates = xff.split(',').map(s => s.trim()).filter(Boolean);\n for (const candidate of candidates) {\n if (isValidIp(candidate)) {\n return normalizeIp(candidate);\n }\n }\n }\n return 'unknown';\n }\n }\n\n // 4. Fallback if no strategy matched or no proxy header was trusted:\n // If a direct TCP socket is available, return it; otherwise fail safely to 'unknown'.\n if (typeof reqWithIp.socket?.remoteAddress === 'string' && reqWithIp.socket.remoteAddress.length > 0) {\n return normalizeIp(reqWithIp.socket.remoteAddress);\n }\n\n return 'unknown';\n}\n\n/**\n * Extracts pathname from a request URL safely.\n */\nfunction extractRequestPath(request: Request | NextLikeRequest): string {\n if (!request.url) {\n return '/';\n }\n try {\n const parsed = new URL(request.url, 'http://localhost');\n return parsed.pathname;\n } catch {\n return (request.url.split('?')[0] ?? '').split('#')[0] || '/';\n }\n}\n\n/**\n * Merges rate-limiting headers into an existing Web Response.\n */\nfunction mergeResponseHeaders(\n response: Response,\n headersToMerge: Record<string, string>\n): Response {\n if (!response) {\n return response;\n }\n\n try {\n for (const [key, value] of Object.entries(headersToMerge)) {\n response.headers.set(key, value);\n }\n return response;\n } catch {\n const newHeaders = new Headers(response.headers);\n for (const [key, value] of Object.entries(headersToMerge)) {\n newHeaders.set(key, value);\n }\n\n const nullBodyStatuses = [101, 204, 205, 304];\n const body =\n nullBodyStatuses.includes(response.status)\n ? null\n : response.body;\n\n return new Response(body, {\n status: response.status,\n statusText: response.statusText,\n headers: newHeaders\n });\n }\n}\n\n/**\n * Creates standard HTTP 429 response when a request is blocked.\n */\nfunction createBlockedResponse(\n retryAfter: number,\n reason: string,\n extraHeaders?: Record<string, string>,\n customBuilder?: (info: { reason: string; retryAfter: number; clientIp: string }) => Response,\n clientIp?: string\n): Response {\n if (typeof customBuilder === 'function') {\n try {\n return customBuilder({\n reason,\n retryAfter,\n clientIp: clientIp || 'unknown'\n });\n } catch {\n // Fallback to standard response if custom builder throws\n }\n }\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'Retry-After': String(retryAfter),\n 'X-Z-Shield-Blocked': '1',\n ...extraHeaders\n };\n\n const body = {\n error: 'Too Many Requests',\n message: reason,\n retryAfter\n };\n\n return new Response(JSON.stringify(body), {\n status: 429,\n headers\n });\n}\n\n/**\n * Checks if the passed object is an existing ZShieldInstance.\n */\nfunction isZShieldInstance(val: unknown): val is ZShieldInstance {\n return (\n typeof val === 'object' &&\n val !== null &&\n 'rateLimiter' in val &&\n 'detector' in val &&\n 'blockManager' in val &&\n 'logger' in val &&\n 'config' in val\n );\n}\n\n/**\n * Factory for creating a Next.js App Router shield instance.\n *\n * Usage:\n * import { createNextShield } from 'z-shield';\n * const shield = createNextShield({ mode: 'protect', maxRequests: 50 });\n *\n * export const GET = shield.protect(async (request) => {\n * return Response.json({ message: 'Hello from protected API' });\n * });\n *\n * @param optionsOrInstance - Configuration options or existing ZShieldInstance\n * @returns NextShieldInstance\n */\nexport function createNextShield(\n optionsOrInstance?: NextShieldOptions | ZShieldConfig | ZShieldInstance,\n adapterOptions?: NextShieldOptions\n): NextShieldInstance {\n let config: ZShieldConfig;\n let rateLimiter: RateLimiter;\n let detector: Detector;\n let blockManager: BlockManager;\n let logger: Logger;\n let customGetIp: NextClientIpExtractor | NextClientIpStrategy | undefined;\n let customBlockResponse:\n | ((info: { reason: string; retryAfter: number; clientIp: string }) => Response)\n | undefined;\n\n if (isZShieldInstance(optionsOrInstance)) {\n config = optionsOrInstance.config;\n rateLimiter = optionsOrInstance.rateLimiter;\n detector = optionsOrInstance.detector;\n blockManager = optionsOrInstance.blockManager;\n logger = optionsOrInstance.logger;\n if (adapterOptions?.getIp) {\n customGetIp = adapterOptions.getIp;\n }\n if (adapterOptions?.customBlockResponse) {\n customBlockResponse = adapterOptions.customBlockResponse;\n }\n } else {\n const opts = optionsOrInstance || {};\n config = resolveConfig(opts);\n rateLimiter = new RateLimiter(config);\n detector = new Detector({ rateLimiter, config });\n blockManager = new BlockManager(config);\n logger = new Logger(config);\n if ('getIp' in opts && (typeof opts.getIp === 'function' || typeof opts.getIp === 'string')) {\n customGetIp = opts.getIp;\n }\n if ('customBlockResponse' in opts && typeof opts.customBlockResponse === 'function') {\n customBlockResponse = opts.customBlockResponse;\n }\n }\n\n const check = async (\n request: Request | NextLikeRequest\n ): Promise<NextShieldCheckResult> => {\n const pathname = extractRequestPath(request);\n const method = (request.method || 'GET').toUpperCase();\n const now = Date.now();\n\n // 1. Route exclusion check\n if (isRouteExcluded(pathname, config.excludeRoutes)) {\n return {\n blocked: false,\n isExcluded: true,\n clientIp: '',\n headers: {}\n };\n }\n\n // 2. Client IP extraction\n const clientIp = extractNextClientIp(request, config.trustProxy, customGetIp);\n\n // 3. BlockManager check\n const isIpBlocked = blockManager.isBlocked(clientIp, now);\n if (isIpBlocked) {\n const blockInfo = blockManager.getBlockInfo(clientIp, now);\n const reason = blockInfo ? blockInfo.reason : 'Administrative block';\n const remainingMs = blockInfo ? blockInfo.remainingMs : 0;\n const retryAfterSeconds = Math.max(1, Math.ceil(remainingMs / 1000));\n const action = config.mode === 'protect' ? 'blocked' : 'warned';\n\n void logger.log({\n appName: config.appName,\n type: 'blocked_ip',\n severity: 'warning',\n ip: clientIp,\n method,\n route: pathname,\n reason,\n action\n });\n\n const headers: Record<string, string> = {\n 'X-Z-Shield-Protected': '1',\n 'X-Z-Shield-Mode': config.mode,\n 'X-Z-Shield-Blocked': '1',\n 'X-Z-Shield-Reason': reason,\n 'Retry-After': String(retryAfterSeconds)\n };\n\n if (config.mode === 'protect') {\n const blockedResponse = createBlockedResponse(\n retryAfterSeconds,\n 'Your IP address has been temporarily blocked due to suspicious activity.',\n headers,\n customBlockResponse,\n clientIp\n );\n return {\n blocked: true,\n isExcluded: false,\n clientIp,\n headers,\n response: blockedResponse\n };\n }\n\n // Monitor mode: mark warning header and allow request through\n headers['X-Z-Shield-Suspicious'] = '1';\n return {\n blocked: false,\n isExcluded: false,\n clientIp,\n headers\n };\n }\n\n // 4. Rate Limiting assessment via Detector\n const detection = detector.check(clientIp, request, now);\n const rateLimitHeaders: Record<string, string> = {\n 'X-Z-Shield-Protected': '1',\n 'X-Z-Shield-Mode': config.mode,\n 'X-RateLimit-Limit': String(config.maxRequests),\n 'X-RateLimit-Remaining': String(detection.remaining),\n 'X-RateLimit-Reset': String(Math.ceil(detection.resetTime / 1000))\n };\n\n if (detection.isSuspicious) {\n const action = config.mode === 'protect' ? 'blocked' : 'warned';\n void logger.log({\n appName: config.appName,\n type: detection.type || 'rate_limit',\n severity: detection.severity || 'warning',\n ip: clientIp,\n method,\n route: pathname,\n reason: detection.reason || 'Threshold exceeded',\n action\n });\n\n rateLimitHeaders['X-Z-Shield-Suspicious'] = '1';\n rateLimitHeaders['X-Z-Shield-Reason'] = detection.reason || 'Threshold exceeded';\n\n if (config.mode === 'protect') {\n if (config.autoBlock && isValidIp(clientIp)) {\n blockManager.block(clientIp, config.blockDurationMs, detection.reason || 'Threshold exceeded', now);\n }\n\n const retryAfterSeconds = Math.max(1, Math.ceil((detection.resetTime - now) / 1000));\n rateLimitHeaders['Retry-After'] = String(retryAfterSeconds);\n rateLimitHeaders['X-Z-Shield-Blocked'] = '1';\n\n const blockedResponse = createBlockedResponse(\n retryAfterSeconds,\n 'Rate limit exceeded. Please wait before retrying.',\n rateLimitHeaders,\n customBlockResponse,\n clientIp\n );\n\n return {\n blocked: true,\n isExcluded: false,\n clientIp,\n detection,\n headers: rateLimitHeaders,\n response: blockedResponse\n };\n }\n }\n\n // 5. Allowed / normal request\n return {\n blocked: false,\n isExcluded: false,\n clientIp,\n detection,\n headers: rateLimitHeaders\n };\n };\n\n const protect = <TContext = unknown>(\n handler: (request: Request, context?: TContext) => Promise<Response> | Response\n ): ((request: Request, context?: TContext) => Promise<Response>) => {\n return async (request: Request, context?: TContext): Promise<Response> => {\n const checkResult = await check(request);\n\n if (checkResult.blocked && checkResult.response) {\n return checkResult.response;\n }\n\n const response = await handler(request, context);\n\n if (checkResult.isExcluded || Object.keys(checkResult.headers).length === 0) {\n return response;\n }\n\n return mergeResponseHeaders(response, checkResult.headers);\n };\n };\n\n return {\n config,\n rateLimiter,\n detector,\n blockManager,\n logger,\n check,\n protect,\n\n // Programmatic Block API\n block: (ip: string, duration?: string | number, reason?: string): BlockRecord => {\n const result = blockManager.block(ip, duration, reason);\n void logger.log({\n appName: config.appName,\n type: 'manual_block',\n severity: 'info',\n ip,\n method: 'INTERNAL',\n route: '/admin/block',\n reason: result.reason,\n action: 'blocked'\n });\n return result;\n },\n\n unblock: (ip: string): boolean => {\n const result = blockManager.unblock(ip);\n if (result) {\n void logger.log({\n appName: config.appName,\n type: 'manual_unblock',\n severity: 'info',\n ip,\n method: 'INTERNAL',\n route: '/admin/unblock',\n reason: 'Manual unblock requested',\n action: 'unblocked'\n });\n }\n return result;\n },\n\n isBlocked: (ip: string): boolean => blockManager.isBlocked(ip),\n getBlockInfo: (ip: string): BlockInfo | null => blockManager.getBlockInfo(ip),\n listBlocks: (): BlockInfo[] => blockManager.listBlocks(),\n flush: (): Promise<void> => logger.flush()\n };\n}\n\n/**\n * Higher-order function wrapping a Next.js App Router route handler with Z-Shield protection.\n *\n * Usage:\n * import { withNextShield } from 'z-shield';\n *\n * export const GET = withNextShield(async (req) => {\n * return Response.json({ ok: true });\n * }, { mode: 'protect', maxRequests: 50 });\n *\n * @param handler - Next.js Route Handler function\n * @param options - Z-Shield configuration options or existing shield instance\n * @returns Wrapped route handler\n */\nexport function withNextShield<TContext = unknown>(\n handler: (request: Request, context?: TContext) => Promise<Response> | Response,\n options?: NextShieldOptions | ZShieldConfig | NextShieldInstance | ZShieldInstance,\n adapterOptions?: NextShieldOptions\n): (request: Request, context?: TContext) => Promise<Response> {\n const shield =\n options && 'protect' in options\n ? (options as NextShieldInstance)\n : createNextShield(\n options as NextShieldOptions | ZShieldConfig | ZShieldInstance | undefined,\n adapterOptions\n );\n\n return shield.protect(handler);\n}\n\nexport type {\n NextShieldOptions,\n NextShieldCheckResult,\n NextShieldInstance,\n NextRouteHandler,\n NextClientIpStrategy,\n NextClientIpExtractor,\n NextLikeRequest\n};\n\n"]}
@@ -0,0 +1,75 @@
1
+ import type { RateLimitStatus, ZShieldConfig, ZShieldOptions } from './types';
2
+ export interface RateLimiterOptions {
3
+ windowMs?: number;
4
+ maxRequests?: number;
5
+ maxTrackedIps?: number;
6
+ }
7
+ /**
8
+ * RateLimiter - Bounded In-Memory Request Counter
9
+ *
10
+ * Algorithm: Fixed Window with Per-IP Dynamic Reset Time
11
+ * - On an IP's first request, we establish a time window (now -> now + windowMs).
12
+ * - Every subsequent request within the window increments the counter.
13
+ * - When the window expires, the counter resets to 1 with a new window.
14
+ *
15
+ * Memory Protection (DoS defense):
16
+ * - JavaScript's Map preserves insertion order.
17
+ * - By deleting and re-setting accessed keys, the Map functions as an LRU (Least Recently Used) cache.
18
+ * - If the number of tracked IPs reaches `maxTrackedIps`, we evict the oldest entry.
19
+ * This ensures memory consumption is strictly bounded and cannot crash the Node.js process.
20
+ */
21
+ export declare class RateLimiter {
22
+ readonly windowMs: number;
23
+ readonly maxRequests: number;
24
+ readonly maxTrackedIps: number;
25
+ private readonly records;
26
+ /**
27
+ * @param options - Limiter options or resolved configuration
28
+ */
29
+ constructor(options?: RateLimiterOptions | ZShieldConfig | ZShieldOptions);
30
+ /**
31
+ * Records an incoming request from an IP address.
32
+ *
33
+ * @param ip - Client IP address
34
+ * @param now - Current timestamp (injectable for testing)
35
+ * @returns Current rate limit status
36
+ */
37
+ record(ip: string, now?: number): RateLimitStatus;
38
+ /**
39
+ * Inspects current status for an IP without incrementing its counter.
40
+ *
41
+ * @param ip - Client IP address
42
+ * @param now - Current timestamp
43
+ * @returns Current rate limit status
44
+ */
45
+ get(ip: string, now?: number): RateLimitStatus;
46
+ /**
47
+ * Resets counter for a specific IP.
48
+ * @param ip - Client IP address
49
+ */
50
+ reset(ip: string): void;
51
+ /**
52
+ * Clears all tracked IP records from memory.
53
+ */
54
+ clear(): void;
55
+ /**
56
+ * Number of IPs currently stored in memory.
57
+ */
58
+ get size(): number;
59
+ /**
60
+ * Scans and removes any expired counters from memory.
61
+ * Because Map maintains insertion order, oldest entries are checked first.
62
+ *
63
+ * @param now - Current timestamp
64
+ * @returns Count of removed expired entries
65
+ */
66
+ pruneExpired(now?: number): number;
67
+ /**
68
+ * Evicts the oldest entry to maintain bounded memory.
69
+ * Evicts the least recently used entry to strictly uphold memory bound.
70
+ *
71
+ * @param _now - Current timestamp
72
+ */
73
+ private _evictOldest;
74
+ }
75
+ //# sourceMappingURL=rate-limiter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../src/rate-limiter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9E,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAOD;;;;;;;;;;;;;GAaG;AACH,qBAAa,WAAW;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA4B;IAEpD;;OAEG;IACH,YAAY,OAAO,GAAE,kBAAkB,GAAG,aAAa,GAAG,cAAmB,EAM5E;IAED;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,GAAE,MAAmB,GAAG,eAAe,CA0C5D;IAED;;;;;;OAMG;IACH,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,GAAE,MAAmB,GAAG,eAAe,CAmBzD;IAED;;;OAGG;IACH,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAGtB;IAED;;OAEG;IACH,KAAK,IAAI,IAAI,CAEZ;IAED;;OAEG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED;;;;;;OAMG;IACH,YAAY,CAAC,GAAG,GAAE,MAAmB,GAAG,MAAM,CAS7C;IAED;;;;;OAKG;IACH,OAAO,CAAC,YAAY;CAQrB"}