nestjs-security-module
Version:
A plug-and-play NestJS security module with CORS, Helmet, rate limiting, audit logging, CSP, XSS sanitization, and more.
188 lines (165 loc) • 4.91 kB
text/typescript
import {
DynamicModule,
MiddlewareConsumer,
Module,
NestModule,
Provider,
} from '@nestjs/common';
import helmet from 'helmet';
import { SecurityModuleOptions } from './security.config';
import { createAuditLogMiddleware } from './middlewares/audit-log.middleware';
import { createRateLimitMiddleware } from './middlewares/rate-limit.middleware';
({})
export class SecurityModule implements NestModule {
static options: SecurityModuleOptions;
static enableCors = false;
static corsOptions: any = undefined;
static sanitizeEnabled = false;
static forRoot(options: SecurityModuleOptions): DynamicModule {
this.options = options;
this.sanitizeEnabled = !!options.sanitize;
const imports = [];
const providers: Provider[] = [];
return {
module: SecurityModule,
imports,
providers,
};
}
static register(options: SecurityModuleOptions): DynamicModule {
this.options = options;
this.sanitizeEnabled = !!options.sanitize;
// 🔧 Bunları ekle:
this.enableCors = !!options.cors;
this.corsOptions =
typeof options.cors === 'object' ? options.cors : undefined;
return {
module: SecurityModule,
};
}
configure(consumer: MiddlewareConsumer) {
const options = SecurityModule.options;
// Helmet genel
if (options.helmet !== false) {
consumer.apply(helmet()).forRoutes('*');
}
// CORS
if (options.cors) {
SecurityModule.enableCors = true;
SecurityModule.corsOptions =
typeof options.cors === 'object' ? options.cors : undefined;
}
// Rate Limiting
if (options.rateLimit) {
consumer
.apply(createRateLimitMiddleware(options.rateLimit))
.forRoutes('*');
}
// Audit Log
if (options.auditLog) {
consumer.apply(createAuditLogMiddleware()).forRoutes('*');
}
// CSP
if (options.csp) {
const cspConfig =
typeof options.csp === 'object'
? options.csp
: {
useDefaults: true,
directives: {
'default-src': ["'self'"],
'script-src': ["'self'"],
'style-src': ["'self'", "'unsafe-inline'"],
'img-src': ["'self'", 'data:'],
},
};
consumer.apply(helmet.contentSecurityPolicy(cspConfig)).forRoutes('*');
}
// X-Frame-Options
if (options.xFrameOptions) {
const frameValue =
typeof options.xFrameOptions === 'string'
? options.xFrameOptions.toLowerCase()
: 'sameorigin';
consumer
.apply(
helmet.frameguard({
action: frameValue as 'sameorigin' | 'deny',
}),
)
.forRoutes('*');
}
// Referrer-Policy
if (options.referrerPolicy) {
const policy =
typeof options.referrerPolicy === 'object'
? options.referrerPolicy
: { policy: 'no-referrer' };
consumer.apply(helmet.referrerPolicy(policy)).forRoutes('*');
}
// HSTS
if (options.hsts) {
const hstsConfig =
typeof options.hsts === 'object'
? options.hsts
: { maxAge: 60 * 60 * 24 * 180 };
consumer.apply(helmet.hsts(hstsConfig)).forRoutes('*');
}
// X-Content-Type-Options
if (options.xContentTypeOptions !== false) {
consumer.apply(helmet.noSniff()).forRoutes('*');
}
// Expect-CT
if (options.expectCt) {
const expectCtConfig =
typeof options.expectCt === 'object'
? options.expectCt
: { maxAge: 86400, enforce: true };
consumer
.apply(
(
req,
res: import('express').Response,
next: import('express').NextFunction,
) => {
res.setHeader(
'Expect-CT',
`max-age=${expectCtConfig.maxAge}${
expectCtConfig.enforce ? ', enforce' : ''
}`,
);
next();
},
)
.forRoutes('*');
}
// Permissions-Policy
if (options.permissionsPolicy) {
const policy = Object.entries(
options.permissionsPolicy as Record<string, string[]>,
)
.map(([key, val]) => `${key}=(${val.join(' ')})`)
.join(', ');
consumer
.apply(
(
req,
res: import('express').Response,
next: import('express').NextFunction,
) => {
res.setHeader('Permissions-Policy', policy);
next();
},
)
.forRoutes('*');
}
// COEP
if (options.crossOriginEmbedderPolicy !== false) {
const coep =
typeof options.crossOriginEmbedderPolicy === 'object'
? options.crossOriginEmbedderPolicy
: {};
consumer.apply(helmet.crossOriginEmbedderPolicy(coep)).forRoutes('*');
}
}
}