@noony-serverless/core 0.1.1 → 0.2.0
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/build/core/core.d.ts +16 -48
- package/build/core/core.js +2 -61
- package/build/core/handler.d.ts +37 -16
- package/build/core/handler.js +131 -42
- package/build/core/index.d.ts +0 -1
- package/build/core/index.js +0 -1
- package/build/middlewares/ConsolidatedValidationMiddleware.d.ts +126 -0
- package/build/middlewares/ConsolidatedValidationMiddleware.js +330 -0
- package/build/middlewares/ProcessingMiddleware.d.ts +138 -0
- package/build/middlewares/ProcessingMiddleware.js +425 -0
- package/build/middlewares/SecurityMiddleware.d.ts +157 -0
- package/build/middlewares/SecurityMiddleware.js +307 -0
- package/build/middlewares/authenticationMiddleware.d.ts +379 -0
- package/build/middlewares/authenticationMiddleware.js +216 -0
- package/build/middlewares/bodyParserMiddleware.d.ts +99 -0
- package/build/middlewares/bodyParserMiddleware.js +99 -0
- package/build/middlewares/bodyValidationMiddleware.d.ts +69 -3
- package/build/middlewares/bodyValidationMiddleware.js +68 -2
- package/build/middlewares/dependencyInjectionMiddleware.d.ts +238 -0
- package/build/middlewares/dependencyInjectionMiddleware.js +238 -0
- package/build/middlewares/errorHandlerMiddleware.d.ts +94 -0
- package/build/middlewares/errorHandlerMiddleware.js +105 -0
- package/build/middlewares/guards/RouteGuards.d.ts +476 -21
- package/build/middlewares/guards/RouteGuards.js +418 -21
- package/build/middlewares/guards/adapters/CustomTokenVerificationPortAdapter.d.ts +271 -0
- package/build/middlewares/guards/adapters/CustomTokenVerificationPortAdapter.js +301 -0
- package/build/middlewares/guards/cache/CacheAdapter.d.ts +369 -28
- package/build/middlewares/guards/cache/CacheAdapter.js +124 -5
- package/build/middlewares/guards/cache/MemoryCacheAdapter.d.ts +113 -4
- package/build/middlewares/guards/cache/MemoryCacheAdapter.js +113 -4
- package/build/middlewares/guards/config/GuardConfiguration.d.ts +568 -18
- package/build/middlewares/guards/config/GuardConfiguration.js +266 -10
- package/build/middlewares/guards/guards/FastAuthGuard.d.ts +5 -5
- package/build/middlewares/guards/guards/PermissionGuardFactory.d.ts +5 -13
- package/build/middlewares/guards/guards/PermissionGuardFactory.js +4 -4
- package/build/middlewares/guards/index.d.ts +43 -1
- package/build/middlewares/guards/index.js +46 -1
- package/build/middlewares/guards/resolvers/ExpressionPermissionResolver.d.ts +1 -1
- package/build/middlewares/guards/resolvers/ExpressionPermissionResolver.js +1 -1
- package/build/middlewares/guards/resolvers/PermissionResolver.d.ts +1 -1
- package/build/middlewares/guards/resolvers/PlainPermissionResolver.d.ts +1 -1
- package/build/middlewares/guards/resolvers/WildcardPermissionResolver.d.ts +1 -1
- package/build/middlewares/guards/services/FastUserContextService.d.ts +20 -33
- package/build/middlewares/guards/services/FastUserContextService.js +19 -5
- package/build/middlewares/headerVariablesMiddleware.d.ts +118 -0
- package/build/middlewares/headerVariablesMiddleware.js +118 -0
- package/build/middlewares/httpAttributesMiddleware.d.ts +235 -0
- package/build/middlewares/httpAttributesMiddleware.js +236 -1
- package/build/middlewares/index.d.ts +3 -1
- package/build/middlewares/index.js +6 -1
- package/build/middlewares/queryParametersMiddleware.d.ts +105 -0
- package/build/middlewares/queryParametersMiddleware.js +105 -0
- package/build/middlewares/rateLimitingMiddleware.d.ts +601 -9
- package/build/middlewares/rateLimitingMiddleware.js +623 -11
- package/build/middlewares/responseWrapperMiddleware.d.ts +170 -1
- package/build/middlewares/responseWrapperMiddleware.js +170 -1
- package/build/middlewares/securityAuditMiddleware.js +5 -5
- package/package.json +11 -9
- package/build/core/containerPool.d.ts +0 -44
- package/build/core/containerPool.js +0 -103
- package/build/middlewares/validationMiddleware.d.ts +0 -9
- package/build/middlewares/validationMiddleware.js +0 -40
|
@@ -86,8 +86,193 @@ declare class MemoryStore implements RateLimitStore {
|
|
|
86
86
|
destroy(): void;
|
|
87
87
|
}
|
|
88
88
|
/**
|
|
89
|
-
* Rate Limiting Middleware
|
|
90
|
-
* Implements
|
|
89
|
+
* Rate Limiting Middleware with sliding window implementation.
|
|
90
|
+
* Implements comprehensive rate limiting with dynamic limits, custom storage, and security features.
|
|
91
|
+
*
|
|
92
|
+
* ## When to Use RateLimitingMiddleware
|
|
93
|
+
*
|
|
94
|
+
* ### ✅ Recommended Use Cases:
|
|
95
|
+
* - **Business Logic Rate Limiting**: User-specific quotas, subscription-based limits
|
|
96
|
+
* - **Authentication & Security**: Login attempts, password resets, token refresh
|
|
97
|
+
* - **Content Creation**: Post creation, comment submission, profile updates
|
|
98
|
+
* - **Resource-Intensive Operations**: File uploads, data processing, complex queries
|
|
99
|
+
* - **Advanced Scenarios**: A/B testing limits, geographic restrictions, time-based rules
|
|
100
|
+
*
|
|
101
|
+
* ### ❌ Not Recommended Use Cases:
|
|
102
|
+
* - **Basic DDoS Protection**: Use WAF/CloudFlare instead
|
|
103
|
+
* - **Static Asset Protection**: Use CDN rate limiting
|
|
104
|
+
* - **Simple Volumetric Attacks**: Network-level solutions more effective
|
|
105
|
+
*
|
|
106
|
+
* ## Architecture Integration
|
|
107
|
+
*
|
|
108
|
+
* ### With WAF (Web Application Firewall):
|
|
109
|
+
* - WAF handles: IP blocking, DDoS protection, bot detection
|
|
110
|
+
* - Application handles: Business logic, user-aware limits, complex rules
|
|
111
|
+
* - Focus on complementary functionality, not duplication
|
|
112
|
+
*
|
|
113
|
+
* ### With API Gateway:
|
|
114
|
+
* - Gateway handles: Service-level limits, routing quotas, load balancing
|
|
115
|
+
* - Application handles: User context, subscription limits, feature-specific rules
|
|
116
|
+
* - Different layers for different concerns
|
|
117
|
+
*
|
|
118
|
+
* ### Direct Exposure (No WAF/Gateway):
|
|
119
|
+
* - Application must handle comprehensive protection
|
|
120
|
+
* - Implement multiple protection layers within middleware
|
|
121
|
+
* - Critical for security in simple deployments
|
|
122
|
+
*
|
|
123
|
+
* @implements {BaseMiddleware}
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* Basic API rate limiting:
|
|
127
|
+
* ```typescript
|
|
128
|
+
* import { Handler, RateLimitingMiddleware } from '@noony-serverless/core';
|
|
129
|
+
*
|
|
130
|
+
* const apiHandler = new Handler()
|
|
131
|
+
* .use(new RateLimitingMiddleware({
|
|
132
|
+
* maxRequests: 100,
|
|
133
|
+
* windowMs: 60000, // 1 minute
|
|
134
|
+
* message: 'Too many API requests'
|
|
135
|
+
* }))
|
|
136
|
+
* .handle(async (context) => {
|
|
137
|
+
* const data = await getApiData();
|
|
138
|
+
* return { success: true, data };
|
|
139
|
+
* });
|
|
140
|
+
* ```
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* Authentication endpoint with strict limits:
|
|
144
|
+
* ```typescript
|
|
145
|
+
* const loginHandler = new Handler()
|
|
146
|
+
* .use(new RateLimitingMiddleware({
|
|
147
|
+
* maxRequests: 5,
|
|
148
|
+
* windowMs: 60000, // 1 minute
|
|
149
|
+
* message: 'Too many login attempts',
|
|
150
|
+
* statusCode: 429
|
|
151
|
+
* }))
|
|
152
|
+
* .handle(async (context) => {
|
|
153
|
+
* const { email, password } = context.req.parsedBody;
|
|
154
|
+
* const token = await authenticate(email, password);
|
|
155
|
+
* return { success: true, token };
|
|
156
|
+
* });
|
|
157
|
+
* ```
|
|
158
|
+
*
|
|
159
|
+
* @example
|
|
160
|
+
* Dynamic limits based on user authentication:
|
|
161
|
+
* ```typescript
|
|
162
|
+
* const smartApiHandler = new Handler()
|
|
163
|
+
* .use(new RateLimitingMiddleware({
|
|
164
|
+
* maxRequests: 50, // Default for unauthenticated
|
|
165
|
+
* windowMs: 60000,
|
|
166
|
+
* dynamicLimits: {
|
|
167
|
+
* authenticated: {
|
|
168
|
+
* maxRequests: 1000,
|
|
169
|
+
* windowMs: 60000,
|
|
170
|
+
* matcher: (context) => !!context.user
|
|
171
|
+
* },
|
|
172
|
+
* premium: {
|
|
173
|
+
* maxRequests: 5000,
|
|
174
|
+
* windowMs: 60000,
|
|
175
|
+
* matcher: (context) => context.user?.plan === 'premium'
|
|
176
|
+
* }
|
|
177
|
+
* }
|
|
178
|
+
* }))
|
|
179
|
+
* .handle(async (context) => {
|
|
180
|
+
* return { success: true, limit: 'applied dynamically' };
|
|
181
|
+
* });
|
|
182
|
+
* ```
|
|
183
|
+
*
|
|
184
|
+
* @example
|
|
185
|
+
* Multi-layer defense (with WAF):
|
|
186
|
+
* ```typescript
|
|
187
|
+
* // WAF handles basic IP limits (10,000/min), DDoS protection
|
|
188
|
+
* // Application refines with business logic
|
|
189
|
+
* const wafAwareHandler = new Handler()
|
|
190
|
+
* .use(new RateLimitingMiddleware({
|
|
191
|
+
* maxRequests: 100, // Refined limit after WAF filtering
|
|
192
|
+
* windowMs: 60000,
|
|
193
|
+
* dynamicLimits: {
|
|
194
|
+
* premium: {
|
|
195
|
+
* maxRequests: 500,
|
|
196
|
+
* windowMs: 60000,
|
|
197
|
+
* matcher: (context) => context.user?.plan === 'premium'
|
|
198
|
+
* }
|
|
199
|
+
* },
|
|
200
|
+
* keyGenerator: (context) => `user:${context.user?.id || context.req.ip}`
|
|
201
|
+
* }))
|
|
202
|
+
* .handle(async (context) => {
|
|
203
|
+
* // Business logic with user-aware limits
|
|
204
|
+
* return await processUserRequest(context);
|
|
205
|
+
* });
|
|
206
|
+
* ```
|
|
207
|
+
*
|
|
208
|
+
* @example
|
|
209
|
+
* API Gateway integration:
|
|
210
|
+
* ```typescript
|
|
211
|
+
* // Gateway: 10,000 req/hour per service, 1,000 req/min per endpoint
|
|
212
|
+
* // Application: User-specific limits within gateway envelope
|
|
213
|
+
* const gatewayAwareHandler = new Handler()
|
|
214
|
+
* .use(new RateLimitingMiddleware({
|
|
215
|
+
* maxRequests: 100, // Per user within gateway limits
|
|
216
|
+
* windowMs: 60000,
|
|
217
|
+
* dynamicLimits: {
|
|
218
|
+
* freeTrial: {
|
|
219
|
+
* maxRequests: 10,
|
|
220
|
+
* windowMs: 60000,
|
|
221
|
+
* matcher: (context) => context.user?.trialExpired === false
|
|
222
|
+
* },
|
|
223
|
+
* enterprise: {
|
|
224
|
+
* maxRequests: 5000,
|
|
225
|
+
* windowMs: 60000,
|
|
226
|
+
* matcher: (context) => context.user?.plan === 'enterprise'
|
|
227
|
+
* }
|
|
228
|
+
* },
|
|
229
|
+
* keyGenerator: (context) => `user:${context.user?.id}`
|
|
230
|
+
* }))
|
|
231
|
+
* .handle(async (context) => {
|
|
232
|
+
* // Refined business logic limits
|
|
233
|
+
* return await handleApiRequest(context);
|
|
234
|
+
* });
|
|
235
|
+
* ```
|
|
236
|
+
*
|
|
237
|
+
* @example
|
|
238
|
+
* Comprehensive protection (no WAF/Gateway):
|
|
239
|
+
* ```typescript
|
|
240
|
+
* // Application must handle all protection layers
|
|
241
|
+
* const comprehensiveHandler = new Handler()
|
|
242
|
+
* .use(new RateLimitingMiddleware({
|
|
243
|
+
* maxRequests: 100,
|
|
244
|
+
* windowMs: 60000,
|
|
245
|
+
* dynamicLimits: {
|
|
246
|
+
* // IP-based protection (WAF-like)
|
|
247
|
+
* suspicious_ip: {
|
|
248
|
+
* maxRequests: 10,
|
|
249
|
+
* windowMs: 60000,
|
|
250
|
+
* matcher: (context) => detectSuspiciousIP(context.req.ip)
|
|
251
|
+
* },
|
|
252
|
+
* // Endpoint-specific (Gateway-like)
|
|
253
|
+
* auth_endpoint: {
|
|
254
|
+
* maxRequests: 5,
|
|
255
|
+
* windowMs: 60000,
|
|
256
|
+
* matcher: (context) => context.req.path?.includes('/auth/')
|
|
257
|
+
* },
|
|
258
|
+
* // Business logic (Application-specific)
|
|
259
|
+
* user_specific: {
|
|
260
|
+
* maxRequests: 1000,
|
|
261
|
+
* windowMs: 60000,
|
|
262
|
+
* matcher: (context) => !!context.user
|
|
263
|
+
* }
|
|
264
|
+
* },
|
|
265
|
+
* keyGenerator: (context) => {
|
|
266
|
+
* const user = context.user?.id;
|
|
267
|
+
* const ip = context.req.ip;
|
|
268
|
+
* const endpoint = context.req.path;
|
|
269
|
+
* return user ? `user:${user}` : `ip:${ip}:${endpoint}`;
|
|
270
|
+
* }
|
|
271
|
+
* }))
|
|
272
|
+
* .handle(async (context) => {
|
|
273
|
+
* return await processRequest(context);
|
|
274
|
+
* });
|
|
275
|
+
* ```
|
|
91
276
|
*/
|
|
92
277
|
export declare class RateLimitingMiddleware implements BaseMiddleware {
|
|
93
278
|
private store;
|
|
@@ -96,17 +281,223 @@ export declare class RateLimitingMiddleware implements BaseMiddleware {
|
|
|
96
281
|
before(context: Context): Promise<void>;
|
|
97
282
|
}
|
|
98
283
|
/**
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
284
|
+
* Factory function that creates a rate limiting middleware.
|
|
285
|
+
* Provides flexible rate limiting with configurable options and presets.
|
|
286
|
+
*
|
|
287
|
+
* ## Architecture Decision Matrix
|
|
288
|
+
*
|
|
289
|
+
* | Infrastructure | WAF Rate Limiting | Gateway Rate Limiting | Application Rate Limiting |
|
|
290
|
+
* |----------------|------------------|---------------------|-------------------------|
|
|
291
|
+
* | **WAF + Gateway + App** | ✅ Basic DDoS protection | ✅ Service-level limits | ✅ Business logic |
|
|
292
|
+
* | **Gateway + App** | ❌ Not available | ✅ Service + IP limits | ✅ User context + business |
|
|
293
|
+
* | **WAF + App** | ✅ Network protection | ❌ Not available | ✅ All business logic |
|
|
294
|
+
* | **App Only** | ❌ Must implement | ❌ Must implement | ✅ Everything |
|
|
295
|
+
*
|
|
296
|
+
* ## Implementation Strategy by Architecture
|
|
297
|
+
*
|
|
298
|
+
* ### Multi-Layer Defense (Recommended for Enterprise)
|
|
299
|
+
* ```typescript
|
|
300
|
+
* // WAF Layer: 10,000 req/min per IP (CloudFlare/AWS WAF)
|
|
301
|
+
* // Gateway Layer: 1,000 req/min per API key (Kong/AWS API Gateway)
|
|
302
|
+
* // Application Layer: User-specific business rules (This middleware)
|
|
303
|
+
*
|
|
304
|
+
* const enterprise = rateLimiting({
|
|
305
|
+
* maxRequests: 100, // Refined after other layers
|
|
306
|
+
* dynamicLimits: {
|
|
307
|
+
* premium: { maxRequests: 500, matcher: (ctx) => ctx.user?.plan === 'premium' }
|
|
308
|
+
* }
|
|
309
|
+
* });
|
|
310
|
+
* ```
|
|
311
|
+
*
|
|
312
|
+
* ### Gateway + Application (Good for Most Applications)
|
|
313
|
+
* ```typescript
|
|
314
|
+
* // Gateway: Service capacity protection
|
|
315
|
+
* // Application: Business logic enforcement
|
|
316
|
+
*
|
|
317
|
+
* const standard = rateLimiting({
|
|
318
|
+
* maxRequests: 200, // Higher since Gateway pre-filters
|
|
319
|
+
* dynamicLimits: {
|
|
320
|
+
* authenticated: { maxRequests: 1000, matcher: (ctx) => !!ctx.user }
|
|
321
|
+
* }
|
|
322
|
+
* });
|
|
323
|
+
* ```
|
|
324
|
+
*
|
|
325
|
+
* ### Application Only (Comprehensive Protection Required)
|
|
326
|
+
* ```typescript
|
|
327
|
+
* // Must handle all layers of protection
|
|
328
|
+
*
|
|
329
|
+
* const comprehensive = rateLimiting({
|
|
330
|
+
* maxRequests: 50, // Conservative default
|
|
331
|
+
* dynamicLimits: {
|
|
332
|
+
* // WAF-like: IP protection
|
|
333
|
+
* suspicious: { maxRequests: 5, matcher: (ctx) => detectSuspicious(ctx.req.ip) },
|
|
334
|
+
* // Gateway-like: Endpoint protection
|
|
335
|
+
* auth: { maxRequests: 10, matcher: (ctx) => ctx.req.path?.includes('/auth/') },
|
|
336
|
+
* // Business: User-specific
|
|
337
|
+
* premium: { maxRequests: 1000, matcher: (ctx) => ctx.user?.plan === 'premium' }
|
|
338
|
+
* }
|
|
339
|
+
* });
|
|
340
|
+
* ```
|
|
341
|
+
*
|
|
342
|
+
* ## Cost-Benefit Analysis
|
|
343
|
+
*
|
|
344
|
+
* | Architecture | Setup Complexity | Runtime Cost | Protection Level | Maintenance |
|
|
345
|
+
* |-------------|-----------------|-------------|-----------------|-------------|
|
|
346
|
+
* | **WAF + Gateway + App** | High | High | Maximum | Medium |
|
|
347
|
+
* | **Gateway + App** | Medium | Medium | Good | Low |
|
|
348
|
+
* | **WAF + App** | Medium | Medium | Good | Medium |
|
|
349
|
+
* | **App Only** | Low | Low | Variable | High |
|
|
350
|
+
*
|
|
351
|
+
* @param options - Rate limiting configuration options
|
|
352
|
+
* @returns BaseMiddleware instance
|
|
353
|
+
*
|
|
354
|
+
* @example
|
|
355
|
+
* Using preset configurations:
|
|
356
|
+
* ```typescript
|
|
357
|
+
* import { Handler, rateLimiting, RateLimitPresets } from '@noony-serverless/core';
|
|
358
|
+
*
|
|
359
|
+
* // Strict limits for sensitive endpoints
|
|
360
|
+
* const authHandler = new Handler()
|
|
361
|
+
* .use(rateLimiting(RateLimitPresets.AUTH))
|
|
362
|
+
* .handle(async (context) => {
|
|
363
|
+
* return await handleAuthentication(context.req.parsedBody);
|
|
364
|
+
* });
|
|
365
|
+
*
|
|
366
|
+
* // Standard API limits
|
|
367
|
+
* const apiHandler = new Handler()
|
|
368
|
+
* .use(rateLimiting(RateLimitPresets.API))
|
|
369
|
+
* .handle(async (context) => {
|
|
370
|
+
* return await handleApiRequest(context);
|
|
371
|
+
* });
|
|
372
|
+
* ```
|
|
373
|
+
*
|
|
374
|
+
* @example
|
|
375
|
+
* Custom rate limiting with skip conditions:
|
|
376
|
+
* ```typescript
|
|
377
|
+
* const conditionalHandler = new Handler()
|
|
378
|
+
* .use(rateLimiting({
|
|
379
|
+
* maxRequests: 100,
|
|
380
|
+
* windowMs: 60000,
|
|
381
|
+
* skip: (context) => {
|
|
382
|
+
* // Skip rate limiting for admin users
|
|
383
|
+
* return context.user?.role === 'admin';
|
|
384
|
+
* },
|
|
385
|
+
* keyGenerator: (context) => {
|
|
386
|
+
* // Rate limit per user instead of IP
|
|
387
|
+
* return context.user?.id || context.req.ip || 'anonymous';
|
|
388
|
+
* }
|
|
389
|
+
* }))
|
|
390
|
+
* .handle(async (context) => {
|
|
391
|
+
* return { success: true, message: 'Request processed' };
|
|
392
|
+
* });
|
|
393
|
+
* ```
|
|
394
|
+
*
|
|
395
|
+
* @example
|
|
396
|
+
* Production Redis store integration:
|
|
397
|
+
* ```typescript
|
|
398
|
+
* import Redis from 'ioredis';
|
|
399
|
+
*
|
|
400
|
+
* class RedisRateLimitStore implements RateLimitStore {
|
|
401
|
+
* constructor(private redis: Redis) {}
|
|
402
|
+
*
|
|
403
|
+
* async increment(key: string, windowMs: number) {
|
|
404
|
+
* const multi = this.redis.multi();
|
|
405
|
+
* multi.incr(key);
|
|
406
|
+
* multi.expire(key, Math.ceil(windowMs / 1000));
|
|
407
|
+
* const results = await multi.exec();
|
|
408
|
+
* return { count: results![0][1] as number, resetTime: Date.now() + windowMs };
|
|
409
|
+
* }
|
|
410
|
+
* }
|
|
411
|
+
*
|
|
412
|
+
* const productionHandler = new Handler()
|
|
413
|
+
* .use(rateLimiting({
|
|
414
|
+
* store: new RedisRateLimitStore(redisClient),
|
|
415
|
+
* maxRequests: 1000,
|
|
416
|
+
* windowMs: 60000
|
|
417
|
+
* }))
|
|
418
|
+
* .handle(async (context) => {
|
|
419
|
+
* return await handleHighVolumeAPI(context);
|
|
420
|
+
* });
|
|
421
|
+
* ```
|
|
422
|
+
*
|
|
423
|
+
* @example
|
|
424
|
+
* Multi-dimensional rate limiting:
|
|
425
|
+
* ```typescript
|
|
426
|
+
* const advancedHandler = new Handler()
|
|
427
|
+
* .use(rateLimiting({
|
|
428
|
+
* maxRequests: 100,
|
|
429
|
+
* windowMs: 60000,
|
|
430
|
+
* dynamicLimits: {
|
|
431
|
+
* // Different limits by operation type
|
|
432
|
+
* read_operations: {
|
|
433
|
+
* maxRequests: 1000,
|
|
434
|
+
* windowMs: 60000,
|
|
435
|
+
* matcher: (context) => context.req.method === 'GET'
|
|
436
|
+
* },
|
|
437
|
+
* write_operations: {
|
|
438
|
+
* maxRequests: 50,
|
|
439
|
+
* windowMs: 60000,
|
|
440
|
+
* matcher: (context) => ['POST', 'PUT', 'DELETE'].includes(context.req.method || '')
|
|
441
|
+
* },
|
|
442
|
+
* // Different limits by user tier
|
|
443
|
+
* enterprise_users: {
|
|
444
|
+
* maxRequests: 5000,
|
|
445
|
+
* windowMs: 60000,
|
|
446
|
+
* matcher: (context) => context.user?.tier === 'enterprise'
|
|
447
|
+
* }
|
|
448
|
+
* },
|
|
449
|
+
* keyGenerator: (context) => {
|
|
450
|
+
* // Multi-dimensional key: user + operation type
|
|
451
|
+
* const userId = context.user?.id || context.req.ip;
|
|
452
|
+
* const operation = context.req.method === 'GET' ? 'read' : 'write';
|
|
453
|
+
* return `${operation}:${userId}`;
|
|
454
|
+
* }
|
|
455
|
+
* }))
|
|
456
|
+
* .handle(async (context) => {
|
|
457
|
+
* return await processAdvancedRequest(context);
|
|
458
|
+
* });
|
|
459
|
+
* ```
|
|
102
460
|
*/
|
|
103
461
|
export declare const rateLimiting: (options?: RateLimitOptions) => BaseMiddleware;
|
|
104
462
|
/**
|
|
105
|
-
* Predefined rate limit configurations
|
|
463
|
+
* Predefined rate limit configurations for common use cases.
|
|
464
|
+
*
|
|
465
|
+
* These presets are designed to work well in different infrastructure scenarios:
|
|
466
|
+
* - WAF + Application: Higher limits since WAF pre-filters traffic
|
|
467
|
+
* - Gateway + Application: Moderate limits complementing gateway quotas
|
|
468
|
+
* - Application Only: Conservative limits for comprehensive protection
|
|
469
|
+
*
|
|
470
|
+
* ## Preset Selection Guide
|
|
471
|
+
*
|
|
472
|
+
* | Preset | Use Case | Infrastructure | Requests/Min |
|
|
473
|
+
* |--------|----------|---------------|-------------|
|
|
474
|
+
* | `STRICT` | Sensitive operations | Any | 5 |
|
|
475
|
+
* | `AUTH` | Authentication endpoints | Any | 10 |
|
|
476
|
+
* | `PUBLIC` | Public/unauthenticated | App Only | 50 |
|
|
477
|
+
* | `API` | Standard API endpoints | WAF/Gateway + App | 100-1000 |
|
|
478
|
+
* | `DEVELOPMENT` | Development/testing | Development | 10,000 |
|
|
479
|
+
*
|
|
480
|
+
* @example
|
|
481
|
+
* Choosing the right preset:
|
|
482
|
+
* ```typescript
|
|
483
|
+
* // High-security endpoint (password reset)
|
|
484
|
+
* .use(rateLimiting(RateLimitPresets.STRICT))
|
|
485
|
+
*
|
|
486
|
+
* // Login/registration
|
|
487
|
+
* .use(rateLimiting(RateLimitPresets.AUTH))
|
|
488
|
+
*
|
|
489
|
+
* // Public API with WAF protection
|
|
490
|
+
* .use(rateLimiting(RateLimitPresets.API))
|
|
491
|
+
*
|
|
492
|
+
* // Public API without WAF (direct exposure)
|
|
493
|
+
* .use(rateLimiting(RateLimitPresets.PUBLIC))
|
|
494
|
+
* ```
|
|
106
495
|
*/
|
|
107
496
|
export declare const RateLimitPresets: {
|
|
108
497
|
/**
|
|
109
498
|
* Very strict limits for sensitive endpoints
|
|
499
|
+
* Use for: Password resets, account changes, payment operations
|
|
500
|
+
* Infrastructure: Any (universal protection)
|
|
110
501
|
*/
|
|
111
502
|
readonly STRICT: {
|
|
112
503
|
maxRequests: number;
|
|
@@ -114,7 +505,9 @@ export declare const RateLimitPresets: {
|
|
|
114
505
|
message: string;
|
|
115
506
|
};
|
|
116
507
|
/**
|
|
117
|
-
* Standard API limits
|
|
508
|
+
* Standard API limits with dynamic scaling for authenticated users
|
|
509
|
+
* Use for: Main API endpoints, data retrieval, business operations
|
|
510
|
+
* Infrastructure: Best with WAF or Gateway (higher baseline limits)
|
|
118
511
|
*/
|
|
119
512
|
readonly API: {
|
|
120
513
|
maxRequests: number;
|
|
@@ -129,29 +522,228 @@ export declare const RateLimitPresets: {
|
|
|
129
522
|
};
|
|
130
523
|
/**
|
|
131
524
|
* Authentication endpoint limits
|
|
525
|
+
* Use for: Login, registration, token refresh, password operations
|
|
526
|
+
* Infrastructure: Any (essential security protection)
|
|
132
527
|
*/
|
|
133
528
|
readonly AUTH: {
|
|
134
529
|
maxRequests: number;
|
|
135
530
|
windowMs: number;
|
|
136
531
|
message: string;
|
|
532
|
+
keyGenerator: (context: Context) => string;
|
|
137
533
|
};
|
|
138
534
|
/**
|
|
139
|
-
* Public endpoint limits
|
|
535
|
+
* Public endpoint limits for direct application exposure
|
|
536
|
+
* Use for: Public APIs, webhooks, health checks
|
|
537
|
+
* Infrastructure: Application only (no WAF/Gateway protection)
|
|
140
538
|
*/
|
|
141
539
|
readonly PUBLIC: {
|
|
142
540
|
maxRequests: number;
|
|
143
541
|
windowMs: number;
|
|
542
|
+
dynamicLimits: {
|
|
543
|
+
suspicious: {
|
|
544
|
+
maxRequests: number;
|
|
545
|
+
windowMs: number;
|
|
546
|
+
matcher: (context: Context) => boolean;
|
|
547
|
+
};
|
|
548
|
+
};
|
|
144
549
|
};
|
|
145
550
|
/**
|
|
146
|
-
* Development mode - very permissive
|
|
551
|
+
* Development mode - very permissive limits
|
|
552
|
+
* Use for: Development, testing, debugging
|
|
553
|
+
* Infrastructure: Development environment only
|
|
147
554
|
*/
|
|
148
555
|
readonly DEVELOPMENT: {
|
|
149
556
|
maxRequests: number;
|
|
150
557
|
windowMs: number;
|
|
558
|
+
skip: (context: Context) => boolean;
|
|
559
|
+
};
|
|
560
|
+
/**
|
|
561
|
+
* Enterprise-grade configuration with multi-tier support
|
|
562
|
+
* Use for: Production SaaS applications, enterprise APIs
|
|
563
|
+
* Infrastructure: WAF + Gateway + Application (full stack protection)
|
|
564
|
+
*/
|
|
565
|
+
readonly ENTERPRISE: {
|
|
566
|
+
maxRequests: number;
|
|
567
|
+
windowMs: number;
|
|
568
|
+
dynamicLimits: {
|
|
569
|
+
free: {
|
|
570
|
+
maxRequests: number;
|
|
571
|
+
windowMs: number;
|
|
572
|
+
matcher: (context: Context) => boolean;
|
|
573
|
+
};
|
|
574
|
+
premium: {
|
|
575
|
+
maxRequests: number;
|
|
576
|
+
windowMs: number;
|
|
577
|
+
matcher: (context: Context) => boolean;
|
|
578
|
+
};
|
|
579
|
+
enterprise: {
|
|
580
|
+
maxRequests: number;
|
|
581
|
+
windowMs: number;
|
|
582
|
+
matcher: (context: Context) => boolean;
|
|
583
|
+
};
|
|
584
|
+
admin: {
|
|
585
|
+
maxRequests: number;
|
|
586
|
+
windowMs: number;
|
|
587
|
+
matcher: (context: Context) => boolean;
|
|
588
|
+
};
|
|
589
|
+
};
|
|
590
|
+
keyGenerator: (context: Context) => string;
|
|
151
591
|
};
|
|
152
592
|
};
|
|
153
593
|
/**
|
|
154
594
|
* Export memory store for testing and custom implementations
|
|
155
595
|
*/
|
|
156
596
|
export { MemoryStore };
|
|
597
|
+
/**
|
|
598
|
+
* Configuration helpers and utilities for rate limiting setup
|
|
599
|
+
*
|
|
600
|
+
* ## Best Practices for Production
|
|
601
|
+
*
|
|
602
|
+
* ### 1. Store Selection
|
|
603
|
+
* - **Development**: Use default `MemoryStore` (built-in)
|
|
604
|
+
* - **Production Single Instance**: Use `MemoryStore` with cleanup
|
|
605
|
+
* - **Production Multi-Instance**: Use Redis-based store
|
|
606
|
+
* - **Serverless**: Use external store (Redis/DynamoDB) for state persistence
|
|
607
|
+
*
|
|
608
|
+
* ### 2. Key Generation Strategy
|
|
609
|
+
* ```typescript
|
|
610
|
+
* // Bad: Too generic, easy to abuse
|
|
611
|
+
* keyGenerator: () => 'global'
|
|
612
|
+
*
|
|
613
|
+
* // Good: Multi-dimensional keys
|
|
614
|
+
* keyGenerator: (context) => {
|
|
615
|
+
* const user = context.user?.id;
|
|
616
|
+
* const endpoint = context.req.path?.split('/')[2]; // /api/users -> users
|
|
617
|
+
* const method = context.req.method;
|
|
618
|
+
* return user ? `${user}:${endpoint}:${method}` : `${context.req.ip}:${endpoint}`;
|
|
619
|
+
* }
|
|
620
|
+
* ```
|
|
621
|
+
*
|
|
622
|
+
* ### 3. Dynamic Limits Best Practices
|
|
623
|
+
* ```typescript
|
|
624
|
+
* // Order matchers from most specific to least specific
|
|
625
|
+
* dynamicLimits: {
|
|
626
|
+
* admin: { maxRequests: 10000, matcher: (ctx) => ctx.user?.role === 'admin' },
|
|
627
|
+
* enterprise: { maxRequests: 5000, matcher: (ctx) => ctx.user?.plan === 'enterprise' },
|
|
628
|
+
* premium: { maxRequests: 1000, matcher: (ctx) => ctx.user?.plan === 'premium' },
|
|
629
|
+
* authenticated: { maxRequests: 500, matcher: (ctx) => !!ctx.user },
|
|
630
|
+
* // Default fallback handled by maxRequests
|
|
631
|
+
* }
|
|
632
|
+
* ```
|
|
633
|
+
*
|
|
634
|
+
* ### 4. Error Handling and Fallback
|
|
635
|
+
* ```typescript
|
|
636
|
+
* const resilientRateLimit = rateLimiting({
|
|
637
|
+
* maxRequests: 100,
|
|
638
|
+
* windowMs: 60000,
|
|
639
|
+
*
|
|
640
|
+
* // Custom store with fallback
|
|
641
|
+
* store: new ResilientStore({
|
|
642
|
+
* primary: redisStore,
|
|
643
|
+
* fallback: new MemoryStore(),
|
|
644
|
+
* timeout: 500 // ms
|
|
645
|
+
* }),
|
|
646
|
+
*
|
|
647
|
+
* // Graceful degradation on errors
|
|
648
|
+
* onError: (error, context) => {
|
|
649
|
+
* logger.warn('Rate limiting error, allowing request', { error, ip: context.req.ip });
|
|
650
|
+
* return false; // Don't block request on store errors
|
|
651
|
+
* }
|
|
652
|
+
* });
|
|
653
|
+
* ```
|
|
654
|
+
*
|
|
655
|
+
* ### 5. Monitoring and Alerting
|
|
656
|
+
* ```typescript
|
|
657
|
+
* // Monitor rate limit effectiveness
|
|
658
|
+
* const monitoredRateLimit = rateLimiting({
|
|
659
|
+
* maxRequests: 100,
|
|
660
|
+
* windowMs: 60000,
|
|
661
|
+
*
|
|
662
|
+
* onRateLimit: (context, info) => {
|
|
663
|
+
* // Alert on high rate limit hits
|
|
664
|
+
* metrics.increment('rate_limit.exceeded', {
|
|
665
|
+
* endpoint: context.req.path,
|
|
666
|
+
* user: context.user?.id || 'anonymous'
|
|
667
|
+
* });
|
|
668
|
+
*
|
|
669
|
+
* // Log suspicious patterns
|
|
670
|
+
* if (info.current > info.limit * 2) {
|
|
671
|
+
* logger.warn('Potential abuse detected', {
|
|
672
|
+
* ip: context.req.ip,
|
|
673
|
+
* userAgent: context.req.headers?.['user-agent'],
|
|
674
|
+
* attempts: info.current
|
|
675
|
+
* });
|
|
676
|
+
* }
|
|
677
|
+
* }
|
|
678
|
+
* });
|
|
679
|
+
* ```
|
|
680
|
+
*
|
|
681
|
+
* ### 6. Testing Rate Limits
|
|
682
|
+
* ```typescript
|
|
683
|
+
* // Test helper for rate limit validation
|
|
684
|
+
* export const testRateLimit = async (
|
|
685
|
+
* handler: Handler,
|
|
686
|
+
* requests: number,
|
|
687
|
+
* shouldSucceed: number
|
|
688
|
+
* ) => {
|
|
689
|
+
* const results = await Promise.all(
|
|
690
|
+
* Array(requests).fill(0).map(() => handler.execute(mockRequest, mockResponse))
|
|
691
|
+
* );
|
|
692
|
+
*
|
|
693
|
+
* const successful = results.filter(r => r.statusCode !== 429).length;
|
|
694
|
+
* expect(successful).toBe(shouldSucceed);
|
|
695
|
+
* };
|
|
696
|
+
* ```
|
|
697
|
+
*
|
|
698
|
+
* ## Troubleshooting Common Issues
|
|
699
|
+
*
|
|
700
|
+
* ### Issue: Rate limits not working
|
|
701
|
+
* **Solution**: Check key generation and store connection
|
|
702
|
+
* ```typescript
|
|
703
|
+
* // Debug key generation
|
|
704
|
+
* keyGenerator: (context) => {
|
|
705
|
+
* const key = generateKey(context);
|
|
706
|
+
* console.log('Rate limit key:', key); // Remove in production
|
|
707
|
+
* return key;
|
|
708
|
+
* }
|
|
709
|
+
* ```
|
|
710
|
+
*
|
|
711
|
+
* ### Issue: Too many false positives
|
|
712
|
+
* **Solution**: Refine dynamic limits and key generation
|
|
713
|
+
* ```typescript
|
|
714
|
+
* // More granular limits
|
|
715
|
+
* dynamicLimits: {
|
|
716
|
+
* read: { maxRequests: 1000, matcher: (ctx) => ctx.req.method === 'GET' },
|
|
717
|
+
* write: { maxRequests: 100, matcher: (ctx) => ctx.req.method !== 'GET' }
|
|
718
|
+
* }
|
|
719
|
+
* ```
|
|
720
|
+
*
|
|
721
|
+
* ### Issue: Memory leaks in MemoryStore
|
|
722
|
+
* **Solution**: Ensure proper cleanup interval and limits
|
|
723
|
+
* ```typescript
|
|
724
|
+
* // Monitor store size
|
|
725
|
+
* setInterval(() => {
|
|
726
|
+
* const storeSize = memoryStore.size();
|
|
727
|
+
* if (storeSize > 10000) {
|
|
728
|
+
* logger.warn('Rate limit store size growing', { size: storeSize });
|
|
729
|
+
* }
|
|
730
|
+
* }, 60000);
|
|
731
|
+
* ```
|
|
732
|
+
*
|
|
733
|
+
* ### Issue: Rate limits too restrictive
|
|
734
|
+
* **Solution**: Implement gradual enforcement
|
|
735
|
+
* ```typescript
|
|
736
|
+
* const gradualLimit = rateLimiting({
|
|
737
|
+
* maxRequests: 100,
|
|
738
|
+
* windowMs: 60000,
|
|
739
|
+
*
|
|
740
|
+
* // Warn before blocking
|
|
741
|
+
* onApproachingLimit: (context, info) => {
|
|
742
|
+
* if (info.remaining < 10) {
|
|
743
|
+
* context.res.header('X-Rate-Limit-Warning', 'Approaching limit');
|
|
744
|
+
* }
|
|
745
|
+
* }
|
|
746
|
+
* });
|
|
747
|
+
* ```
|
|
748
|
+
*/
|
|
157
749
|
//# sourceMappingURL=rateLimitingMiddleware.d.ts.map
|