@pawells/nestjs-shared 1.0.0-dev.4c8c698
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/LICENSE +21 -0
- package/README.md +802 -0
- package/build/LICENSE +21 -0
- package/build/README.md +802 -0
- package/build/common/common.module.d.ts +49 -0
- package/build/common/common.module.d.ts.map +1 -0
- package/build/common/common.module.js +178 -0
- package/build/common/common.module.js.map +1 -0
- package/build/common/constants/histogram-buckets.constants.d.ts +12 -0
- package/build/common/constants/histogram-buckets.constants.d.ts.map +1 -0
- package/build/common/constants/histogram-buckets.constants.js +51 -0
- package/build/common/constants/histogram-buckets.constants.js.map +1 -0
- package/build/common/constants/http-status.constants.d.ts +27 -0
- package/build/common/constants/http-status.constants.d.ts.map +1 -0
- package/build/common/constants/http-status.constants.js +27 -0
- package/build/common/constants/http-status.constants.js.map +1 -0
- package/build/common/constants/timeout.constants.d.ts +29 -0
- package/build/common/constants/timeout.constants.d.ts.map +1 -0
- package/build/common/constants/timeout.constants.js +45 -0
- package/build/common/constants/timeout.constants.js.map +1 -0
- package/build/common/controllers/metrics.controller.d.ts +23 -0
- package/build/common/controllers/metrics.controller.d.ts.map +1 -0
- package/build/common/controllers/metrics.controller.js +66 -0
- package/build/common/controllers/metrics.controller.js.map +1 -0
- package/build/common/decorators/common-decorators.d.ts +90 -0
- package/build/common/decorators/common-decorators.d.ts.map +1 -0
- package/build/common/decorators/common-decorators.js +101 -0
- package/build/common/decorators/common-decorators.js.map +1 -0
- package/build/common/decorators/decorator-factory.d.ts +108 -0
- package/build/common/decorators/decorator-factory.d.ts.map +1 -0
- package/build/common/decorators/decorator-factory.js +104 -0
- package/build/common/decorators/decorator-factory.js.map +1 -0
- package/build/common/decorators/guard.decorators.d.ts +48 -0
- package/build/common/decorators/guard.decorators.d.ts.map +1 -0
- package/build/common/decorators/guard.decorators.js +49 -0
- package/build/common/decorators/guard.decorators.js.map +1 -0
- package/build/common/decorators/index.d.ts +10 -0
- package/build/common/decorators/index.d.ts.map +1 -0
- package/build/common/decorators/index.js +11 -0
- package/build/common/decorators/index.js.map +1 -0
- package/build/common/decorators/instrument.decorator.d.ts +128 -0
- package/build/common/decorators/instrument.decorator.d.ts.map +1 -0
- package/build/common/decorators/instrument.decorator.js +165 -0
- package/build/common/decorators/instrument.decorator.js.map +1 -0
- package/build/common/decorators/metric.decorators.d.ts +42 -0
- package/build/common/decorators/metric.decorators.d.ts.map +1 -0
- package/build/common/decorators/metric.decorators.js +85 -0
- package/build/common/decorators/metric.decorators.js.map +1 -0
- package/build/common/decorators/request-property.decorator.d.ts +65 -0
- package/build/common/decorators/request-property.decorator.d.ts.map +1 -0
- package/build/common/decorators/request-property.decorator.js +102 -0
- package/build/common/decorators/request-property.decorator.js.map +1 -0
- package/build/common/errors/base-application-error.d.ts +98 -0
- package/build/common/errors/base-application-error.d.ts.map +1 -0
- package/build/common/errors/base-application-error.js +133 -0
- package/build/common/errors/base-application-error.js.map +1 -0
- package/build/common/errors/error-factory.d.ts +93 -0
- package/build/common/errors/error-factory.d.ts.map +1 -0
- package/build/common/errors/error-factory.js +105 -0
- package/build/common/errors/error-factory.js.map +1 -0
- package/build/common/errors/index.d.ts +13 -0
- package/build/common/errors/index.d.ts.map +1 -0
- package/build/common/errors/index.js +15 -0
- package/build/common/errors/index.js.map +1 -0
- package/build/common/factories/index.d.ts +5 -0
- package/build/common/factories/index.d.ts.map +1 -0
- package/build/common/factories/index.js +3 -0
- package/build/common/factories/index.js.map +1 -0
- package/build/common/factories/module-factory.d.ts +178 -0
- package/build/common/factories/module-factory.d.ts.map +1 -0
- package/build/common/factories/module-factory.js +253 -0
- package/build/common/factories/module-factory.js.map +1 -0
- package/build/common/factories/rate-limit-config.factory.d.ts +79 -0
- package/build/common/factories/rate-limit-config.factory.d.ts.map +1 -0
- package/build/common/factories/rate-limit-config.factory.js +115 -0
- package/build/common/factories/rate-limit-config.factory.js.map +1 -0
- package/build/common/factories/security-bootstrap.factory.d.ts +77 -0
- package/build/common/factories/security-bootstrap.factory.d.ts.map +1 -0
- package/build/common/factories/security-bootstrap.factory.js +222 -0
- package/build/common/factories/security-bootstrap.factory.js.map +1 -0
- package/build/common/filters/global-exception.filter.d.ts +78 -0
- package/build/common/filters/global-exception.filter.d.ts.map +1 -0
- package/build/common/filters/global-exception.filter.js +192 -0
- package/build/common/filters/global-exception.filter.js.map +1 -0
- package/build/common/filters/http-exception.filter.d.ts +37 -0
- package/build/common/filters/http-exception.filter.d.ts.map +1 -0
- package/build/common/filters/http-exception.filter.js +91 -0
- package/build/common/filters/http-exception.filter.js.map +1 -0
- package/build/common/guards/csrf.guard.d.ts +53 -0
- package/build/common/guards/csrf.guard.d.ts.map +1 -0
- package/build/common/guards/csrf.guard.js +109 -0
- package/build/common/guards/csrf.guard.js.map +1 -0
- package/build/common/guards/metrics.guard.d.ts +42 -0
- package/build/common/guards/metrics.guard.d.ts.map +1 -0
- package/build/common/guards/metrics.guard.js +124 -0
- package/build/common/guards/metrics.guard.js.map +1 -0
- package/build/common/index.d.ts +43 -0
- package/build/common/index.d.ts.map +1 -0
- package/build/common/index.js +50 -0
- package/build/common/index.js.map +1 -0
- package/build/common/interceptors/http-client.interceptor.d.ts +11 -0
- package/build/common/interceptors/http-client.interceptor.d.ts.map +1 -0
- package/build/common/interceptors/http-client.interceptor.js +69 -0
- package/build/common/interceptors/http-client.interceptor.js.map +1 -0
- package/build/common/interceptors/http-instrumentation.interceptor.d.ts +64 -0
- package/build/common/interceptors/http-instrumentation.interceptor.d.ts.map +1 -0
- package/build/common/interceptors/http-instrumentation.interceptor.js +148 -0
- package/build/common/interceptors/http-instrumentation.interceptor.js.map +1 -0
- package/build/common/interceptors/http-metrics.interceptor.d.ts +46 -0
- package/build/common/interceptors/http-metrics.interceptor.d.ts.map +1 -0
- package/build/common/interceptors/http-metrics.interceptor.js +120 -0
- package/build/common/interceptors/http-metrics.interceptor.js.map +1 -0
- package/build/common/interceptors/logging.interceptor.d.ts +22 -0
- package/build/common/interceptors/logging.interceptor.d.ts.map +1 -0
- package/build/common/interceptors/logging.interceptor.js +67 -0
- package/build/common/interceptors/logging.interceptor.js.map +1 -0
- package/build/common/interfaces/cache-provider.interface.d.ts +54 -0
- package/build/common/interfaces/cache-provider.interface.d.ts.map +1 -0
- package/build/common/interfaces/cache-provider.interface.js +6 -0
- package/build/common/interfaces/cache-provider.interface.js.map +1 -0
- package/build/common/interfaces/index.d.ts +7 -0
- package/build/common/interfaces/index.d.ts.map +1 -0
- package/build/common/interfaces/index.js +3 -0
- package/build/common/interfaces/index.js.map +1 -0
- package/build/common/interfaces/log-context.interface.d.ts +77 -0
- package/build/common/interfaces/log-context.interface.d.ts.map +1 -0
- package/build/common/interfaces/log-context.interface.js +2 -0
- package/build/common/interfaces/log-context.interface.js.map +1 -0
- package/build/common/interfaces/log-entry.interface.d.ts +26 -0
- package/build/common/interfaces/log-entry.interface.d.ts.map +1 -0
- package/build/common/interfaces/log-entry.interface.js +33 -0
- package/build/common/interfaces/log-entry.interface.js.map +1 -0
- package/build/common/interfaces/logger.interface.d.ts +62 -0
- package/build/common/interfaces/logger.interface.d.ts.map +1 -0
- package/build/common/interfaces/logger.interface.js +2 -0
- package/build/common/interfaces/logger.interface.js.map +1 -0
- package/build/common/interfaces/metrics-exporter.interface.d.ts +275 -0
- package/build/common/interfaces/metrics-exporter.interface.d.ts.map +1 -0
- package/build/common/interfaces/metrics-exporter.interface.js +8 -0
- package/build/common/interfaces/metrics-exporter.interface.js.map +1 -0
- package/build/common/metrics/base-metrics-collector.d.ts +81 -0
- package/build/common/metrics/base-metrics-collector.d.ts.map +1 -0
- package/build/common/metrics/base-metrics-collector.js +88 -0
- package/build/common/metrics/base-metrics-collector.js.map +1 -0
- package/build/common/metrics/index.d.ts +2 -0
- package/build/common/metrics/index.d.ts.map +1 -0
- package/build/common/metrics/index.js +2 -0
- package/build/common/metrics/index.js.map +1 -0
- package/build/common/metrics.module.d.ts +50 -0
- package/build/common/metrics.module.d.ts.map +1 -0
- package/build/common/metrics.module.js +77 -0
- package/build/common/metrics.module.js.map +1 -0
- package/build/common/modules/throttler.module.d.ts +69 -0
- package/build/common/modules/throttler.module.d.ts.map +1 -0
- package/build/common/modules/throttler.module.js +117 -0
- package/build/common/modules/throttler.module.js.map +1 -0
- package/build/common/pipes/base-validation.pipe.d.ts +67 -0
- package/build/common/pipes/base-validation.pipe.d.ts.map +1 -0
- package/build/common/pipes/base-validation.pipe.js +95 -0
- package/build/common/pipes/base-validation.pipe.js.map +1 -0
- package/build/common/pipes/validation.pipe.d.ts +32 -0
- package/build/common/pipes/validation.pipe.d.ts.map +1 -0
- package/build/common/pipes/validation.pipe.js +60 -0
- package/build/common/pipes/validation.pipe.js.map +1 -0
- package/build/common/registry/instrumentation-registry.d.ts +227 -0
- package/build/common/registry/instrumentation-registry.d.ts.map +1 -0
- package/build/common/registry/instrumentation-registry.js +414 -0
- package/build/common/registry/instrumentation-registry.js.map +1 -0
- package/build/common/services/audit-logger.service.d.ts +91 -0
- package/build/common/services/audit-logger.service.d.ts.map +1 -0
- package/build/common/services/audit-logger.service.js +180 -0
- package/build/common/services/audit-logger.service.js.map +1 -0
- package/build/common/services/csrf.service.d.ts +202 -0
- package/build/common/services/csrf.service.d.ts.map +1 -0
- package/build/common/services/csrf.service.js +478 -0
- package/build/common/services/csrf.service.js.map +1 -0
- package/build/common/services/error-categorizer.service.d.ts +82 -0
- package/build/common/services/error-categorizer.service.d.ts.map +1 -0
- package/build/common/services/error-categorizer.service.js +339 -0
- package/build/common/services/error-categorizer.service.js.map +1 -0
- package/build/common/services/error-sanitizer.service.d.ts +146 -0
- package/build/common/services/error-sanitizer.service.d.ts.map +1 -0
- package/build/common/services/error-sanitizer.service.js +287 -0
- package/build/common/services/error-sanitizer.service.js.map +1 -0
- package/build/common/services/health-check.service.d.ts +86 -0
- package/build/common/services/health-check.service.d.ts.map +1 -0
- package/build/common/services/health-check.service.js +132 -0
- package/build/common/services/health-check.service.js.map +1 -0
- package/build/common/services/http-client.service.d.ts +113 -0
- package/build/common/services/http-client.service.d.ts.map +1 -0
- package/build/common/services/http-client.service.js +294 -0
- package/build/common/services/http-client.service.js.map +1 -0
- package/build/common/services/logger.service.d.ts +189 -0
- package/build/common/services/logger.service.d.ts.map +1 -0
- package/build/common/services/logger.service.js +423 -0
- package/build/common/services/logger.service.js.map +1 -0
- package/build/common/services/metrics-registry.service.d.ts +98 -0
- package/build/common/services/metrics-registry.service.d.ts.map +1 -0
- package/build/common/services/metrics-registry.service.js +262 -0
- package/build/common/services/metrics-registry.service.js.map +1 -0
- package/build/common/services/nest-logger-adapter.service.d.ts +62 -0
- package/build/common/services/nest-logger-adapter.service.d.ts.map +1 -0
- package/build/common/services/nest-logger-adapter.service.js +120 -0
- package/build/common/services/nest-logger-adapter.service.js.map +1 -0
- package/build/common/utils/error.utils.d.ts +16 -0
- package/build/common/utils/error.utils.d.ts.map +1 -0
- package/build/common/utils/error.utils.js +26 -0
- package/build/common/utils/error.utils.js.map +1 -0
- package/build/common/utils/lazy-getter.types.d.ts +190 -0
- package/build/common/utils/lazy-getter.types.d.ts.map +1 -0
- package/build/common/utils/lazy-getter.types.js +114 -0
- package/build/common/utils/lazy-getter.types.js.map +1 -0
- package/build/common/utils/module.utils.d.ts +33 -0
- package/build/common/utils/module.utils.d.ts.map +1 -0
- package/build/common/utils/module.utils.js +48 -0
- package/build/common/utils/module.utils.js.map +1 -0
- package/build/common/utils/sanitization.utils.d.ts +69 -0
- package/build/common/utils/sanitization.utils.d.ts.map +1 -0
- package/build/common/utils/sanitization.utils.js +141 -0
- package/build/common/utils/sanitization.utils.js.map +1 -0
- package/build/config/config.module.d.ts +30 -0
- package/build/config/config.module.d.ts.map +1 -0
- package/build/config/config.module.js +49 -0
- package/build/config/config.module.js.map +1 -0
- package/build/config/config.service.d.ts +74 -0
- package/build/config/config.service.d.ts.map +1 -0
- package/build/config/config.service.js +145 -0
- package/build/config/config.service.js.map +1 -0
- package/build/config/config.types.d.ts +143 -0
- package/build/config/config.types.d.ts.map +1 -0
- package/build/config/config.types.js +2 -0
- package/build/config/config.types.js.map +1 -0
- package/build/config/decorators/config.decorators.d.ts +43 -0
- package/build/config/decorators/config.decorators.d.ts.map +1 -0
- package/build/config/decorators/config.decorators.js +68 -0
- package/build/config/decorators/config.decorators.js.map +1 -0
- package/build/config/decorators/index.d.ts +2 -0
- package/build/config/decorators/index.d.ts.map +1 -0
- package/build/config/decorators/index.js +2 -0
- package/build/config/decorators/index.js.map +1 -0
- package/build/config/index.d.ts +7 -0
- package/build/config/index.d.ts.map +1 -0
- package/build/config/index.js +9 -0
- package/build/config/index.js.map +1 -0
- package/build/config/validation.utils.d.ts +136 -0
- package/build/config/validation.utils.d.ts.map +1 -0
- package/build/config/validation.utils.js +263 -0
- package/build/config/validation.utils.js.map +1 -0
- package/build/errors/index.d.ts +9 -0
- package/build/errors/index.d.ts.map +1 -0
- package/build/errors/index.js +12 -0
- package/build/errors/index.js.map +1 -0
- package/build/guards/custom-throttle.guard.d.ts +28 -0
- package/build/guards/custom-throttle.guard.d.ts.map +1 -0
- package/build/guards/custom-throttle.guard.js +52 -0
- package/build/guards/custom-throttle.guard.js.map +1 -0
- package/build/guards/index.d.ts +2 -0
- package/build/guards/index.d.ts.map +1 -0
- package/build/guards/index.js +2 -0
- package/build/guards/index.js.map +1 -0
- package/build/index.d.ts +53 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +61 -0
- package/build/index.js.map +1 -0
- package/build/logging/index.d.ts +7 -0
- package/build/logging/index.d.ts.map +1 -0
- package/build/logging/index.js +7 -0
- package/build/logging/index.js.map +1 -0
- package/build/metrics/index.d.ts +6 -0
- package/build/metrics/index.d.ts.map +1 -0
- package/build/metrics/index.js +11 -0
- package/build/metrics/index.js.map +1 -0
- package/build/package.json +72 -0
- package/build/security/index.d.ts +8 -0
- package/build/security/index.d.ts.map +1 -0
- package/build/security/index.js +11 -0
- package/build/security/index.js.map +1 -0
- package/build/test-setup.d.ts +2 -0
- package/build/test-setup.d.ts.map +1 -0
- package/build/test-setup.js +40 -0
- package/build/test-setup.js.map +1 -0
- package/build/validation/index.d.ts +6 -0
- package/build/validation/index.d.ts.map +1 -0
- package/build/validation/index.js +8 -0
- package/build/validation/index.js.map +1 -0
- package/package.json +71 -0
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security Bootstrap Factory
|
|
3
|
+
*
|
|
4
|
+
* Provides a comprehensive factory function for applying security middleware and
|
|
5
|
+
* configurations to a NestJS application. This includes compression, MongoDB injection
|
|
6
|
+
* prevention, XSS protection, Helmet.js security headers, CORS, and global validation pipes.
|
|
7
|
+
*
|
|
8
|
+
* All security features are enabled by default and can be selectively disabled via options.
|
|
9
|
+
*/
|
|
10
|
+
import { Logger, ValidationPipe } from '@nestjs/common';
|
|
11
|
+
import helmet from 'helmet';
|
|
12
|
+
import compression from 'compression';
|
|
13
|
+
import express from 'express';
|
|
14
|
+
import { sanitizeObject, sanitizeXss } from '../utils/sanitization.utils.js';
|
|
15
|
+
/**
|
|
16
|
+
* Applies comprehensive security middleware and configurations to a NestJS application
|
|
17
|
+
*
|
|
18
|
+
* Configured middleware stack (in order):
|
|
19
|
+
* 0. Body Size Limits - Enforces maximum request body size
|
|
20
|
+
* 1. Compression - Reduces response size for APIs larger than 1KB
|
|
21
|
+
* 2. MongoDB Injection Prevention - Sanitizes request body and params
|
|
22
|
+
* 3. XSS Protection - Sanitizes user input to prevent XSS attacks
|
|
23
|
+
* 4. Helmet.js - Sets security-related HTTP headers (CSP, HSTS, X-Frame-Options, etc)
|
|
24
|
+
* 5. Global Validation Pipe - Validates and transforms incoming data
|
|
25
|
+
* 6. CORS - Enables cross-origin resource sharing with configurable origins
|
|
26
|
+
*
|
|
27
|
+
* @param app The NestJS application instance
|
|
28
|
+
* @param options Configuration options for security bootstrap
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* import { NestFactory } from '@nestjs/core';
|
|
33
|
+
* import { ApplySecurityMiddleware } from '@pawells/nestjs-shared';
|
|
34
|
+
* import { AppModule } from './app.module';
|
|
35
|
+
*
|
|
36
|
+
* async function bootstrap() {
|
|
37
|
+
* const app = await NestFactory.create(AppModule);
|
|
38
|
+
*
|
|
39
|
+
* ApplySecurityMiddleware(app, {
|
|
40
|
+
* corsOrigins: ['https://example.com'],
|
|
41
|
+
* environment: 'production',
|
|
42
|
+
* });
|
|
43
|
+
*
|
|
44
|
+
* await app.listen(3000);
|
|
45
|
+
* }
|
|
46
|
+
*
|
|
47
|
+
* bootstrap();
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export function ApplySecurityMiddleware(app, options = {}) {
|
|
51
|
+
const { corsOrigins = [], environment = process.env['NODE_ENV'] ?? 'development', compressionEnabled = true, xssEnabled = true, helmetEnabled = true, mongoDbInjectionPreventionEnabled = true, corsEnabled = true, corsAllowedHeaders = ['Content-Type', 'Authorization', 'X-Requested-With', 'apollographql-client-name', 'apollographql-client-version', 'X-CSRF-Token'], cspConnectSrc = [], cspImgSrc = [], cspStyleSrc = [], cspFontSrc = [], maxBodySize = '10mb', logger = new Logger('SecurityBootstrap'), } = options;
|
|
52
|
+
// Named constants for configuration
|
|
53
|
+
const DEFAULT_COMPRESSION_LEVEL = 6;
|
|
54
|
+
const DEFAULT_COMPRESSION_THRESHOLD = 1024;
|
|
55
|
+
// Step 0: Apply request body size limits
|
|
56
|
+
app.use(express.json({ limit: maxBodySize }));
|
|
57
|
+
app.use(express.urlencoded({ extended: true, limit: maxBodySize }));
|
|
58
|
+
logger.log('Request body size limits applied');
|
|
59
|
+
// Step 1: Apply compression middleware
|
|
60
|
+
if (compressionEnabled) {
|
|
61
|
+
app.use(compression({
|
|
62
|
+
level: DEFAULT_COMPRESSION_LEVEL,
|
|
63
|
+
threshold: DEFAULT_COMPRESSION_THRESHOLD,
|
|
64
|
+
filter: (req, res) => {
|
|
65
|
+
// Don't compress responses with this request header
|
|
66
|
+
if (req.headers['x-no-compression']) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
// Use default compression filter function
|
|
70
|
+
return compression.filter(req, res);
|
|
71
|
+
},
|
|
72
|
+
}));
|
|
73
|
+
logger.log('Compression middleware enabled for API response optimization');
|
|
74
|
+
}
|
|
75
|
+
// Step 2: Apply MongoDB injection prevention middleware
|
|
76
|
+
if (mongoDbInjectionPreventionEnabled) {
|
|
77
|
+
app.use((req, _res, next) => {
|
|
78
|
+
if (req.body) {
|
|
79
|
+
req.body = sanitizeObject(req.body, 0, logger);
|
|
80
|
+
}
|
|
81
|
+
if (req.params) {
|
|
82
|
+
req.params = sanitizeObject(req.params, 0, logger);
|
|
83
|
+
}
|
|
84
|
+
if (req.cookies && typeof req.cookies === 'object') {
|
|
85
|
+
req.cookies = sanitizeObject(req.cookies);
|
|
86
|
+
}
|
|
87
|
+
next();
|
|
88
|
+
});
|
|
89
|
+
logger.log('MongoDB injection prevention middleware applied');
|
|
90
|
+
}
|
|
91
|
+
// Step 3: Apply XSS protection middleware
|
|
92
|
+
if (xssEnabled) {
|
|
93
|
+
app.use((req, _res, next) => {
|
|
94
|
+
if (req.body) {
|
|
95
|
+
req.body = sanitizeXss(req.body, logger);
|
|
96
|
+
}
|
|
97
|
+
if (req.query) {
|
|
98
|
+
req.query = sanitizeXss(req.query, logger);
|
|
99
|
+
}
|
|
100
|
+
if (req.params) {
|
|
101
|
+
req.params = sanitizeXss(req.params, logger);
|
|
102
|
+
}
|
|
103
|
+
next();
|
|
104
|
+
});
|
|
105
|
+
logger.log('XSS sanitization middleware applied');
|
|
106
|
+
}
|
|
107
|
+
// Step 4: Apply Helmet.js for security headers
|
|
108
|
+
if (helmetEnabled) {
|
|
109
|
+
app.use(helmet({
|
|
110
|
+
contentSecurityPolicy: {
|
|
111
|
+
directives: {
|
|
112
|
+
defaultSrc: [...new Set(['\'self\''])],
|
|
113
|
+
scriptSrc: [...new Set(['\'self\''])],
|
|
114
|
+
styleSrc: [...new Set(['\'self\'', ...cspStyleSrc])],
|
|
115
|
+
imgSrc: [...new Set(['\'self\'', ...cspImgSrc])],
|
|
116
|
+
connectSrc: [...new Set(['\'self\'', ...cspConnectSrc])],
|
|
117
|
+
fontSrc: [...new Set(['\'self\'', ...cspFontSrc])],
|
|
118
|
+
baseUri: [...new Set(['\'self\''])],
|
|
119
|
+
objectSrc: [...new Set(['\'none\''])],
|
|
120
|
+
mediaSrc: [...new Set(['\'self\''])],
|
|
121
|
+
frameSrc: [...new Set(['\'none\''])],
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
hsts: {
|
|
125
|
+
maxAge: 31536000, // 1 year
|
|
126
|
+
includeSubDomains: true,
|
|
127
|
+
preload: true,
|
|
128
|
+
},
|
|
129
|
+
frameguard: {
|
|
130
|
+
action: 'deny',
|
|
131
|
+
},
|
|
132
|
+
referrerPolicy: {
|
|
133
|
+
policy: 'strict-origin-when-cross-origin',
|
|
134
|
+
},
|
|
135
|
+
noSniff: true,
|
|
136
|
+
xssFilter: true,
|
|
137
|
+
dnsPrefetchControl: {
|
|
138
|
+
allow: false,
|
|
139
|
+
},
|
|
140
|
+
}));
|
|
141
|
+
logger.log('Helmet.js security headers applied');
|
|
142
|
+
}
|
|
143
|
+
// Step 5: Apply global validation pipe
|
|
144
|
+
app.useGlobalPipes(new ValidationPipe({
|
|
145
|
+
whitelist: true,
|
|
146
|
+
forbidNonWhitelisted: true,
|
|
147
|
+
transform: true,
|
|
148
|
+
}));
|
|
149
|
+
logger.log('Global ValidationPipe configured');
|
|
150
|
+
// Step 6: Enable CORS with smart origin validation
|
|
151
|
+
if (corsEnabled) {
|
|
152
|
+
// Apollo Studio origins that need access in development
|
|
153
|
+
const apolloStudioOrigins = [
|
|
154
|
+
'https://studio.apollographql.com',
|
|
155
|
+
'https://sandbox.apollo.dev',
|
|
156
|
+
];
|
|
157
|
+
/**
|
|
158
|
+
* Smart CORS origin validation algorithm.
|
|
159
|
+
*
|
|
160
|
+
* This validator implements a layered origin checking strategy that balances
|
|
161
|
+
* development convenience with production security:
|
|
162
|
+
*
|
|
163
|
+
* 1. **No-origin requests**: Allows requests without an Origin header
|
|
164
|
+
* (typical for same-origin, curl, Postman, and other non-browser clients).
|
|
165
|
+
* These requests are safe and do not require CORS validation.
|
|
166
|
+
*
|
|
167
|
+
* 2. **Development localhost origins**: In development mode, permits all localhost origins
|
|
168
|
+
* (http://localhost:*) to enable rapid iteration without configuration.
|
|
169
|
+
* This includes any port number (e.g., http://localhost:3000, http://localhost:5173).
|
|
170
|
+
*
|
|
171
|
+
* 3. **Development Apollo Studio origins**: In development mode, explicitly allows
|
|
172
|
+
* Apollo Studio (https://studio.apollographql.com) and Apollo Sandbox (https://sandbox.apollo.dev)
|
|
173
|
+
* to facilitate GraphQL IDE testing without additional setup.
|
|
174
|
+
*
|
|
175
|
+
* 4. **Configured production origins**: Allows explicitly configured origins from the
|
|
176
|
+
* corsOrigins option. This is the primary enforcement mechanism in production.
|
|
177
|
+
*
|
|
178
|
+
* 5. **Default rejection**: Any origin not matching the above is rejected with an
|
|
179
|
+
* "Not allowed by CORS" error.
|
|
180
|
+
*
|
|
181
|
+
* **Security notes:**
|
|
182
|
+
* - Development mode origins (localhost, Apollo Studio) are disabled in production
|
|
183
|
+
* - Configured origins are checked case-insensitively for robustness
|
|
184
|
+
* - Wildcard origins (e.g., *.example.com) are not supported; specify each origin explicitly
|
|
185
|
+
*/
|
|
186
|
+
app.enableCors({
|
|
187
|
+
origin: (origin, callback) => {
|
|
188
|
+
// Allow requests with no origin (same-origin, curl, Postman, etc.)
|
|
189
|
+
if (!origin) {
|
|
190
|
+
callback(null, true);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
// In development, allow all localhost origins (strict match: must be http://localhost or http://localhost:PORT)
|
|
194
|
+
if (environment === 'development' && /^http:\/\/localhost(?::\d+)?$/.test(origin)) {
|
|
195
|
+
callback(null, true);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
// In development, allow Apollo Studio origins
|
|
199
|
+
if (environment === 'development' && apolloStudioOrigins.includes(origin)) {
|
|
200
|
+
callback(null, true);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
// Check against configured allowed origins list (case-insensitive)
|
|
204
|
+
if (corsOrigins.some(allowed => allowed.toLowerCase() === origin.toLowerCase())) {
|
|
205
|
+
callback(null, true);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
// Reject unknown origins
|
|
209
|
+
callback(new Error('Not allowed by CORS'));
|
|
210
|
+
},
|
|
211
|
+
credentials: true,
|
|
212
|
+
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'],
|
|
213
|
+
allowedHeaders: corsAllowedHeaders,
|
|
214
|
+
exposedHeaders: ['X-Total-Count', 'X-Page-Number'],
|
|
215
|
+
maxAge: 3600,
|
|
216
|
+
});
|
|
217
|
+
logger.log(corsOrigins.length > 0
|
|
218
|
+
? `CORS configured for environment: ${environment}, Allowed origins: ${corsOrigins.join(', ')}`
|
|
219
|
+
: `CORS configured for environment: ${environment}, Using smart origin validation`);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
//# sourceMappingURL=security-bootstrap.factory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security-bootstrap.factory.js","sourceRoot":"","sources":["../../../src/common/factories/security-bootstrap.factory.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { ExceptionFilter, ArgumentsHost } from '@nestjs/common';
|
|
2
|
+
import { ModuleRef } from '@nestjs/core';
|
|
3
|
+
import { LazyModuleRefService } from '../utils/lazy-getter.types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Standard error response structure for all exceptions.
|
|
6
|
+
*/
|
|
7
|
+
export interface ErrorResponseBody {
|
|
8
|
+
success: false;
|
|
9
|
+
error: {
|
|
10
|
+
code: string;
|
|
11
|
+
message: string;
|
|
12
|
+
timestamp: string;
|
|
13
|
+
context?: Record<string, any>;
|
|
14
|
+
stack?: string;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Global Exception Filter.
|
|
19
|
+
* Catches all unhandled exceptions except HttpException (which is handled by HttpExceptionFilter).
|
|
20
|
+
* Standardizes error responses with consistent structure and logs all errors with categorization.
|
|
21
|
+
*
|
|
22
|
+
* Handles:
|
|
23
|
+
* - BaseApplicationError: Standardized application errors with code, status, context
|
|
24
|
+
* - Error: Generic JavaScript errors
|
|
25
|
+
* - Unknown: Unclassified exceptions
|
|
26
|
+
*
|
|
27
|
+
* Error Response Format:
|
|
28
|
+
* - success: false
|
|
29
|
+
* - error.code: Error code for programmatic handling
|
|
30
|
+
* - error.message: Human-readable error message
|
|
31
|
+
* - error.timestamp: ISO timestamp of error occurrence
|
|
32
|
+
* - error.context: Additional context (development only)
|
|
33
|
+
* - error.stack: Stack trace (development only)
|
|
34
|
+
*
|
|
35
|
+
* @remarks
|
|
36
|
+
* - Automatically redacts sensitive information via ErrorSanitizerService
|
|
37
|
+
* - Categorizes errors for logging (transient vs permanent, retryable, strategy)
|
|
38
|
+
* - Development mode includes full context and stack traces
|
|
39
|
+
* - Production mode shows generic error messages for security
|
|
40
|
+
* - Logs request details: method, URL, IP, user-agent
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* // Development response
|
|
45
|
+
* {
|
|
46
|
+
* success: false,
|
|
47
|
+
* error: {
|
|
48
|
+
* code: 'USER_NOT_FOUND',
|
|
49
|
+
* message: 'User with ID 123 not found',
|
|
50
|
+
* timestamp: '2024-01-01T12:00:00.000Z',
|
|
51
|
+
* context: { userId: '123' },
|
|
52
|
+
* stack: '...'
|
|
53
|
+
* }
|
|
54
|
+
* }
|
|
55
|
+
*
|
|
56
|
+
* // Production response
|
|
57
|
+
* {
|
|
58
|
+
* success: false,
|
|
59
|
+
* error: {
|
|
60
|
+
* code: 'USER_NOT_FOUND',
|
|
61
|
+
* message: 'User with ID 123 not found',
|
|
62
|
+
* timestamp: '2024-01-01T12:00:00.000Z'
|
|
63
|
+
* }
|
|
64
|
+
* }
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export declare class GlobalExceptionFilter implements ExceptionFilter, LazyModuleRefService {
|
|
68
|
+
private _logger;
|
|
69
|
+
private _errorSanitizer;
|
|
70
|
+
private _errorCategorizer;
|
|
71
|
+
readonly Module: ModuleRef;
|
|
72
|
+
constructor(module: ModuleRef);
|
|
73
|
+
private get Logger();
|
|
74
|
+
private get ErrorSanitizer();
|
|
75
|
+
private get ErrorCategorizer();
|
|
76
|
+
catch(exception: unknown, host: ArgumentsHost): void;
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=global-exception.filter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"global-exception.filter.d.ts","sourceRoot":"","sources":["../../../src/common/filters/global-exception.filter.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,eAAe,EAEf,aAAa,EAEb,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAMzC,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAErE;;GAEG;AACH,MAAM,WAAW,iBAAiB;IACjC,OAAO,EAAE,KAAK,CAAC;IACf,KAAK,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;CACF;AAOD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH,qBACa,qBAAsB,YAAW,eAAe,EAAE,oBAAoB;IAClF,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,eAAe,CAAoC;IAC3D,OAAO,CAAC,iBAAiB,CAAsC;IAC/D,SAAgB,MAAM,EAAE,SAAS,CAAC;gBAEtB,MAAM,EAAE,SAAS;IAI7B,OAAO,KAAK,MAAM,GAGjB;IAED,OAAO,KAAK,cAAc,GAGzB;IAED,OAAO,KAAK,gBAAgB,GAG3B;IAEM,KAAK,CAAC,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,aAAa,GAAG,IAAI;CAuG3D"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
import { Catch, HttpStatus, } from '@nestjs/common';
|
|
11
|
+
import { ModuleRef } from '@nestjs/core';
|
|
12
|
+
import { BaseApplicationError } from '../errors/base-application-error.js';
|
|
13
|
+
import { AppLogger } from '../services/logger.service.js';
|
|
14
|
+
import { ErrorSanitizerService } from '../services/error-sanitizer.service.js';
|
|
15
|
+
import { ErrorCategorizerService } from '../services/error-categorizer.service.js';
|
|
16
|
+
/**
|
|
17
|
+
* Development environments where stack traces and full error details are shown
|
|
18
|
+
*/
|
|
19
|
+
const DEV_ENVIRONMENTS = new Set(['development', 'dev', 'local', 'test']);
|
|
20
|
+
/**
|
|
21
|
+
* Global Exception Filter.
|
|
22
|
+
* Catches all unhandled exceptions except HttpException (which is handled by HttpExceptionFilter).
|
|
23
|
+
* Standardizes error responses with consistent structure and logs all errors with categorization.
|
|
24
|
+
*
|
|
25
|
+
* Handles:
|
|
26
|
+
* - BaseApplicationError: Standardized application errors with code, status, context
|
|
27
|
+
* - Error: Generic JavaScript errors
|
|
28
|
+
* - Unknown: Unclassified exceptions
|
|
29
|
+
*
|
|
30
|
+
* Error Response Format:
|
|
31
|
+
* - success: false
|
|
32
|
+
* - error.code: Error code for programmatic handling
|
|
33
|
+
* - error.message: Human-readable error message
|
|
34
|
+
* - error.timestamp: ISO timestamp of error occurrence
|
|
35
|
+
* - error.context: Additional context (development only)
|
|
36
|
+
* - error.stack: Stack trace (development only)
|
|
37
|
+
*
|
|
38
|
+
* @remarks
|
|
39
|
+
* - Automatically redacts sensitive information via ErrorSanitizerService
|
|
40
|
+
* - Categorizes errors for logging (transient vs permanent, retryable, strategy)
|
|
41
|
+
* - Development mode includes full context and stack traces
|
|
42
|
+
* - Production mode shows generic error messages for security
|
|
43
|
+
* - Logs request details: method, URL, IP, user-agent
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```typescript
|
|
47
|
+
* // Development response
|
|
48
|
+
* {
|
|
49
|
+
* success: false,
|
|
50
|
+
* error: {
|
|
51
|
+
* code: 'USER_NOT_FOUND',
|
|
52
|
+
* message: 'User with ID 123 not found',
|
|
53
|
+
* timestamp: '2024-01-01T12:00:00.000Z',
|
|
54
|
+
* context: { userId: '123' },
|
|
55
|
+
* stack: '...'
|
|
56
|
+
* }
|
|
57
|
+
* }
|
|
58
|
+
*
|
|
59
|
+
* // Production response
|
|
60
|
+
* {
|
|
61
|
+
* success: false,
|
|
62
|
+
* error: {
|
|
63
|
+
* code: 'USER_NOT_FOUND',
|
|
64
|
+
* message: 'User with ID 123 not found',
|
|
65
|
+
* timestamp: '2024-01-01T12:00:00.000Z'
|
|
66
|
+
* }
|
|
67
|
+
* }
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
let GlobalExceptionFilter = class GlobalExceptionFilter {
|
|
71
|
+
_logger;
|
|
72
|
+
_errorSanitizer;
|
|
73
|
+
_errorCategorizer;
|
|
74
|
+
Module;
|
|
75
|
+
constructor(module) {
|
|
76
|
+
this.Module = module;
|
|
77
|
+
}
|
|
78
|
+
get Logger() {
|
|
79
|
+
this._logger ??= this.Module.get(AppLogger);
|
|
80
|
+
return this._logger;
|
|
81
|
+
}
|
|
82
|
+
get ErrorSanitizer() {
|
|
83
|
+
this._errorSanitizer ??= this.Module.get(ErrorSanitizerService);
|
|
84
|
+
return this._errorSanitizer;
|
|
85
|
+
}
|
|
86
|
+
get ErrorCategorizer() {
|
|
87
|
+
this._errorCategorizer ??= this.Module.get(ErrorCategorizerService);
|
|
88
|
+
return this._errorCategorizer;
|
|
89
|
+
}
|
|
90
|
+
catch(exception, host) {
|
|
91
|
+
// Handle regular HTTP requests
|
|
92
|
+
const ctx = host.switchToHttp();
|
|
93
|
+
const response = ctx.getResponse();
|
|
94
|
+
const request = ctx.getRequest();
|
|
95
|
+
const isProduction = !DEV_ENVIRONMENTS.has(process.env['NODE_ENV'] ?? '');
|
|
96
|
+
const isDevelopment = !isProduction;
|
|
97
|
+
let status;
|
|
98
|
+
let errorResponse;
|
|
99
|
+
let errorCategory;
|
|
100
|
+
let errorTrace;
|
|
101
|
+
// Standardize error response structure for all exception types
|
|
102
|
+
if (exception instanceof BaseApplicationError) {
|
|
103
|
+
// Standardized application error
|
|
104
|
+
status = exception.statusCode;
|
|
105
|
+
errorResponse = {
|
|
106
|
+
success: false,
|
|
107
|
+
error: {
|
|
108
|
+
code: exception.code,
|
|
109
|
+
message: exception.message,
|
|
110
|
+
timestamp: exception.timestamp.toISOString(),
|
|
111
|
+
...(isDevelopment ? {
|
|
112
|
+
context: exception.context,
|
|
113
|
+
stack: exception.stack,
|
|
114
|
+
} : {}),
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
errorCategory = this.ErrorCategorizer.categorizeError(exception);
|
|
118
|
+
errorTrace = isDevelopment ? exception.stack : undefined;
|
|
119
|
+
}
|
|
120
|
+
else if (exception instanceof Error) {
|
|
121
|
+
// Generic Error
|
|
122
|
+
status = HttpStatus.INTERNAL_SERVER_ERROR;
|
|
123
|
+
errorResponse = {
|
|
124
|
+
success: false,
|
|
125
|
+
error: {
|
|
126
|
+
code: 'INTERNAL_SERVER_ERROR',
|
|
127
|
+
message: isDevelopment ? exception.message : 'An unexpected error occurred',
|
|
128
|
+
timestamp: new Date().toISOString(),
|
|
129
|
+
...(isDevelopment ? { stack: exception.stack } : {}),
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
errorCategory = this.ErrorCategorizer.categorizeError(exception);
|
|
133
|
+
errorTrace = isDevelopment ? exception.stack : undefined;
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
// Unknown exception type
|
|
137
|
+
status = HttpStatus.INTERNAL_SERVER_ERROR;
|
|
138
|
+
errorResponse = {
|
|
139
|
+
success: false,
|
|
140
|
+
error: {
|
|
141
|
+
code: 'UNKNOWN_ERROR',
|
|
142
|
+
message: isDevelopment ? String(exception) : 'An unexpected error occurred',
|
|
143
|
+
timestamp: new Date().toISOString(),
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
errorCategory = {
|
|
147
|
+
type: 'permanent',
|
|
148
|
+
retryable: false,
|
|
149
|
+
strategy: 'fail',
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
// Log the error with categorization
|
|
153
|
+
this.Logger.error('Global exception caught', errorTrace, undefined, {
|
|
154
|
+
message: errorResponse.error.message,
|
|
155
|
+
status,
|
|
156
|
+
errorType: errorCategory.type,
|
|
157
|
+
retryable: errorCategory.retryable,
|
|
158
|
+
strategy: errorCategory.strategy,
|
|
159
|
+
backoffMs: errorCategory.backoffMs,
|
|
160
|
+
url: request.url,
|
|
161
|
+
method: request.method,
|
|
162
|
+
userAgent: request.get('User-Agent'),
|
|
163
|
+
ip: request.ip,
|
|
164
|
+
});
|
|
165
|
+
// Sanitize error response for production
|
|
166
|
+
// Pass the nested error object (which has .message, .stack, .context) to the sanitizer,
|
|
167
|
+
// then reconstruct the full response envelope with the sanitized inner object.
|
|
168
|
+
const sanitizedInner = this.ErrorSanitizer.sanitizeErrorResponse({
|
|
169
|
+
message: errorResponse.error.message,
|
|
170
|
+
statusCode: status,
|
|
171
|
+
stack: errorResponse.error.stack,
|
|
172
|
+
context: errorResponse.error.context,
|
|
173
|
+
}, isDevelopment);
|
|
174
|
+
const sanitizedError = {
|
|
175
|
+
success: false,
|
|
176
|
+
error: {
|
|
177
|
+
code: errorResponse.error.code,
|
|
178
|
+
message: sanitizedInner.message,
|
|
179
|
+
timestamp: errorResponse.error.timestamp,
|
|
180
|
+
...(isDevelopment && sanitizedInner.context ? { context: sanitizedInner.context } : {}),
|
|
181
|
+
...(isDevelopment && sanitizedInner.stack ? { stack: sanitizedInner.stack } : {}),
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
response.status(status).json(sanitizedError);
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
GlobalExceptionFilter = __decorate([
|
|
188
|
+
Catch(BaseApplicationError, Error),
|
|
189
|
+
__metadata("design:paramtypes", [ModuleRef])
|
|
190
|
+
], GlobalExceptionFilter);
|
|
191
|
+
export { GlobalExceptionFilter };
|
|
192
|
+
//# sourceMappingURL=global-exception.filter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"global-exception.filter.js","sourceRoot":"","sources":["../../../src/common/filters/global-exception.filter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { ExceptionFilter, ArgumentsHost, HttpException } from '@nestjs/common';
|
|
2
|
+
import { ModuleRef } from '@nestjs/core';
|
|
3
|
+
import { LazyModuleRefService } from '../utils/lazy-getter.types.js';
|
|
4
|
+
/**
|
|
5
|
+
* HTTP Exception Filter.
|
|
6
|
+
* Handles all HTTP exceptions (400, 401, 403, 404, etc.) and formats error responses consistently.
|
|
7
|
+
* Complements GlobalExceptionFilter by handling NestJS built-in and thrown HTTP exceptions.
|
|
8
|
+
*
|
|
9
|
+
* Error Response Format:
|
|
10
|
+
* - Preserves original HTTP status code and response body
|
|
11
|
+
* - Sanitizes sensitive information before sending to client
|
|
12
|
+
* - Categorizes error for logging (transient vs permanent)
|
|
13
|
+
*
|
|
14
|
+
* @remarks
|
|
15
|
+
* - Automatically redacts sensitive data via ErrorSanitizerService
|
|
16
|
+
* - Logs error with categorization for monitoring
|
|
17
|
+
* - Preserves original exception response structure
|
|
18
|
+
* - Works in conjunction with GlobalExceptionFilter (processes first)
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* // Handled exceptions
|
|
23
|
+
* throw new BadRequestException('Invalid input'); // 400
|
|
24
|
+
* throw new UnauthorizedException('Invalid token'); // 401
|
|
25
|
+
* throw new ForbiddenException('Access denied'); // 403
|
|
26
|
+
* throw new NotFoundException('User not found'); // 404
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare class HttpExceptionFilter implements ExceptionFilter, LazyModuleRefService {
|
|
30
|
+
readonly Module: ModuleRef;
|
|
31
|
+
constructor(module: ModuleRef);
|
|
32
|
+
private get Logger();
|
|
33
|
+
private get ErrorSanitizer();
|
|
34
|
+
private get ErrorCategorizer();
|
|
35
|
+
catch(exception: HttpException, host: ArgumentsHost): void;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=http-exception.filter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-exception.filter.d.ts","sourceRoot":"","sources":["../../../src/common/filters/http-exception.filter.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,eAAe,EAEf,aAAa,EACb,aAAa,EACb,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAKzC,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAOrE;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qBACa,mBAAoB,YAAW,eAAe,EAAE,oBAAoB;IAChF,SAAgB,MAAM,EAAE,SAAS,CAAC;gBAEtB,MAAM,EAAE,SAAS;IAI7B,OAAO,KAAK,MAAM,GAEjB;IAED,OAAO,KAAK,cAAc,GAEzB;IAED,OAAO,KAAK,gBAAgB,GAE3B;IAEM,KAAK,CAAC,SAAS,EAAE,aAAa,EAAE,IAAI,EAAE,aAAa,GAAG,IAAI;CAmCjE"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
import { Catch, HttpException, } from '@nestjs/common';
|
|
11
|
+
import { ModuleRef } from '@nestjs/core';
|
|
12
|
+
import { AppLogger } from '../services/logger.service.js';
|
|
13
|
+
import { ErrorSanitizerService } from '../services/error-sanitizer.service.js';
|
|
14
|
+
import { ErrorCategorizerService } from '../services/error-categorizer.service.js';
|
|
15
|
+
/**
|
|
16
|
+
* Development environments where stack traces and full error details are shown
|
|
17
|
+
*/
|
|
18
|
+
const DEV_ENVIRONMENTS = new Set(['development', 'dev', 'local', 'test']);
|
|
19
|
+
/**
|
|
20
|
+
* HTTP Exception Filter.
|
|
21
|
+
* Handles all HTTP exceptions (400, 401, 403, 404, etc.) and formats error responses consistently.
|
|
22
|
+
* Complements GlobalExceptionFilter by handling NestJS built-in and thrown HTTP exceptions.
|
|
23
|
+
*
|
|
24
|
+
* Error Response Format:
|
|
25
|
+
* - Preserves original HTTP status code and response body
|
|
26
|
+
* - Sanitizes sensitive information before sending to client
|
|
27
|
+
* - Categorizes error for logging (transient vs permanent)
|
|
28
|
+
*
|
|
29
|
+
* @remarks
|
|
30
|
+
* - Automatically redacts sensitive data via ErrorSanitizerService
|
|
31
|
+
* - Logs error with categorization for monitoring
|
|
32
|
+
* - Preserves original exception response structure
|
|
33
|
+
* - Works in conjunction with GlobalExceptionFilter (processes first)
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```typescript
|
|
37
|
+
* // Handled exceptions
|
|
38
|
+
* throw new BadRequestException('Invalid input'); // 400
|
|
39
|
+
* throw new UnauthorizedException('Invalid token'); // 401
|
|
40
|
+
* throw new ForbiddenException('Access denied'); // 403
|
|
41
|
+
* throw new NotFoundException('User not found'); // 404
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
let HttpExceptionFilter = class HttpExceptionFilter {
|
|
45
|
+
Module;
|
|
46
|
+
constructor(module) {
|
|
47
|
+
this.Module = module;
|
|
48
|
+
}
|
|
49
|
+
get Logger() {
|
|
50
|
+
return this.Module.get(AppLogger);
|
|
51
|
+
}
|
|
52
|
+
get ErrorSanitizer() {
|
|
53
|
+
return this.Module.get(ErrorSanitizerService);
|
|
54
|
+
}
|
|
55
|
+
get ErrorCategorizer() {
|
|
56
|
+
return this.Module.get(ErrorCategorizerService);
|
|
57
|
+
}
|
|
58
|
+
catch(exception, host) {
|
|
59
|
+
// Handle regular HTTP requests
|
|
60
|
+
const ctx = host.switchToHttp();
|
|
61
|
+
const response = ctx.getResponse();
|
|
62
|
+
const status = exception.getStatus();
|
|
63
|
+
const isProduction = !DEV_ENVIRONMENTS.has(process.env['NODE_ENV'] ?? '');
|
|
64
|
+
const isDevelopment = !isProduction;
|
|
65
|
+
// Normalize response to object for sanitization
|
|
66
|
+
const exceptionResponse = exception.getResponse();
|
|
67
|
+
const errorObj = typeof exceptionResponse === 'string'
|
|
68
|
+
? { message: exceptionResponse, statusCode: status }
|
|
69
|
+
: exceptionResponse;
|
|
70
|
+
// Sanitize error response
|
|
71
|
+
const sanitizedError = this.ErrorSanitizer.sanitizeErrorResponse(errorObj, isDevelopment);
|
|
72
|
+
// Categorize and log error
|
|
73
|
+
const errorCategory = this.ErrorCategorizer.categorizeError(exception);
|
|
74
|
+
this.Logger.error('HTTP Exception caught', undefined, undefined, {
|
|
75
|
+
message: exception.message,
|
|
76
|
+
status,
|
|
77
|
+
errorType: errorCategory.type,
|
|
78
|
+
retryable: errorCategory.retryable,
|
|
79
|
+
strategy: errorCategory.strategy,
|
|
80
|
+
backoffMs: errorCategory.backoffMs,
|
|
81
|
+
stack: isDevelopment ? exception.stack : undefined,
|
|
82
|
+
});
|
|
83
|
+
response.status(status).json(sanitizedError);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
HttpExceptionFilter = __decorate([
|
|
87
|
+
Catch(HttpException),
|
|
88
|
+
__metadata("design:paramtypes", [ModuleRef])
|
|
89
|
+
], HttpExceptionFilter);
|
|
90
|
+
export { HttpExceptionFilter };
|
|
91
|
+
//# sourceMappingURL=http-exception.filter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-exception.filter.js","sourceRoot":"","sources":["../../../src/common/filters/http-exception.filter.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAEN,KAAK,EAEL,aAAa,GACb,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,OAAO,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAC1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,wCAAwC,CAAC;AAC/E,OAAO,EAAE,uBAAuB,EAAE,MAAM,0CAA0C,CAAC;AAGnF;;GAEG;AACH,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,CAAC,aAAa,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;AAE1E;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEI,IAAM,mBAAmB,GAAzB,MAAM,mBAAmB;IACf,MAAM,CAAY;IAElC,YAAY,MAAiB;QAC5B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACtB,CAAC;IAED,IAAY,MAAM;QACjB,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACnC,CAAC;IAED,IAAY,cAAc;QACzB,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;IAC/C,CAAC;IAED,IAAY,gBAAgB;QAC3B,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;IACjD,CAAC;IAEM,KAAK,CAAC,SAAwB,EAAE,IAAmB;QACzD,+BAA+B;QAC/B,MAAM,GAAG,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;QAChC,MAAM,QAAQ,GAAG,GAAG,CAAC,WAAW,EAAY,CAAC;QAC7C,MAAM,MAAM,GAAG,SAAS,CAAC,SAAS,EAAE,CAAC;QAErC,MAAM,YAAY,GAAG,CAAC,gBAAgB,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC;QAC1E,MAAM,aAAa,GAAG,CAAC,YAAY,CAAC;QAEpC,gDAAgD;QAChD,MAAM,iBAAiB,GAAG,SAAS,CAAC,WAAW,EAAE,CAAC;QAClD,MAAM,QAAQ,GAAG,OAAO,iBAAiB,KAAK,QAAQ;YACrD,CAAC,CAAC,EAAE,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,EAAE;YACpD,CAAC,CAAC,iBAAiB,CAAC;QAErB,0BAA0B;QAC1B,MAAM,cAAc,GAAG,IAAI,CAAC,cAAc,CAAC,qBAAqB,CAC/D,QAA+B,EAC/B,aAAa,CACb,CAAC;QAEF,2BAA2B;QAC3B,MAAM,aAAa,GAAG,IAAI,CAAC,gBAAgB,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC;QACvE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,uBAAuB,EAAE,SAAS,EAAE,SAAS,EAAE;YAChE,OAAO,EAAE,SAAS,CAAC,OAAO;YAC1B,MAAM;YACN,SAAS,EAAE,aAAa,CAAC,IAAI;YAC7B,SAAS,EAAE,aAAa,CAAC,SAAS;YAClC,QAAQ,EAAE,aAAa,CAAC,QAAQ;YAChC,SAAS,EAAE,aAAa,CAAC,SAAS;YAClC,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;SAClD,CAAC,CAAC;QAEH,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAC9C,CAAC;CACD,CAAA;AAtDY,mBAAmB;IAD/B,KAAK,CAAC,aAAa,CAAC;qCAIA,SAAS;GAHjB,mBAAmB,CAsD/B"}
|