fix(#199): implement rate limiting on webhook endpoints

Implements comprehensive rate limiting on all webhook and coordinator endpoints
to prevent DoS attacks. Follows TDD protocol with 14 passing tests.

Implementation:
- Added @nestjs/throttler package for rate limiting
- Created ThrottlerApiKeyGuard for per-API-key rate limiting
- Created ThrottlerValkeyStorageService for distributed rate limiting via Redis
- Configured rate limits on stitcher endpoints (60 req/min)
- Configured rate limits on coordinator endpoints (100 req/min)
- Higher limits for health endpoints (300 req/min for monitoring)
- Added environment variables for rate limit configuration
- Rate limiting logs violations for security monitoring

Rate Limits:
- Stitcher webhooks: 60 requests/minute per API key
- Coordinator endpoints: 100 requests/minute per API key
- Health endpoints: 300 requests/minute (higher for monitoring)

Storage:
- Uses Valkey (Redis) for distributed rate limiting across API instances
- Falls back to in-memory storage if Redis unavailable

Testing:
- 14 comprehensive rate limiting tests (all passing)
- Tests verify: rate limit enforcement, Retry-After headers, per-API-key isolation
- TDD approach: RED (failing tests) → GREEN (implementation) → REFACTOR

Additional improvements:
- Type safety improvements in websocket gateway
- Array type notation standardization in coordinator service

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
This commit is contained in:
Jason Woltje
2026-02-02 13:07:16 -06:00
co-authored by Claude Sonnet 4.5
parent 210b3d2e8f
commit 41d56dadf0
14 changed files with 990 additions and 11 deletions
+16
View File
@@ -81,6 +81,9 @@ importers:
'@nestjs/platform-socket.io':
specifier: ^11.1.12
version: 11.1.12(@nestjs/[email protected]([email protected])([email protected])([email protected])([email protected]))(@nestjs/[email protected])([email protected])
'@nestjs/throttler':
specifier: ^6.5.0
version: 6.5.0(@nestjs/[email protected]([email protected])([email protected])([email protected])([email protected]))(@nestjs/[email protected])([email protected])
'@nestjs/websockets':
specifier: ^11.1.12
version: 11.1.12(@nestjs/[email protected]([email protected])([email protected])([email protected])([email protected]))(@nestjs/[email protected])(@nestjs/[email protected])([email protected])([email protected])
@@ -1488,6 +1491,13 @@ packages:
'@nestjs/platform-express':
optional: true
'@nestjs/[email protected]':
resolution: {integrity: sha512-9j0ZRfH0QE1qyrj9JjIRDz5gQLPqq9yVC2nHsrosDVAfI5HHw08/aUAWx9DZLSdQf4HDkmhTTEGLrRFHENvchQ==}
peerDependencies:
'@nestjs/common': ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0
'@nestjs/core': ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0
reflect-metadata: ^0.1.13 || ^0.2.0
'@nestjs/[email protected]':
resolution: {integrity: sha512-ulSOYcgosx1TqY425cRC5oXtAu1R10+OSmVfgyR9ueR25k4luekURt8dzAZxhxSCI0OsDj9WKCFLTkEuAwg0wg==}
peerDependencies:
@@ -7528,6 +7538,12 @@ snapshots:
optionalDependencies:
'@nestjs/platform-express': 11.1.12(@nestjs/[email protected]([email protected])([email protected])([email protected])([email protected]))(@nestjs/[email protected])
'@nestjs/[email protected](@nestjs/[email protected]([email protected])([email protected])([email protected])([email protected]))(@nestjs/[email protected])([email protected])':
dependencies:
'@nestjs/common': 11.1.12([email protected])([email protected])([email protected])([email protected])
'@nestjs/core': 11.1.12(@nestjs/[email protected]([email protected])([email protected])([email protected])([email protected]))(@nestjs/[email protected])(@nestjs/[email protected])([email protected])([email protected])
reflect-metadata: 0.2.2
'@nestjs/[email protected](@nestjs/[email protected]([email protected])([email protected])([email protected])([email protected]))(@nestjs/[email protected])(@nestjs/[email protected])([email protected])([email protected])':
dependencies:
'@nestjs/common': 11.1.12([email protected])([email protected])([email protected])([email protected])