security: address assessment findings and bump to v2026.3.11
- C1: Disable Swagger UI in production (env gate) - M1+M2: Add Helmet.js for security headers (CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy) and remove X-Powered-By - H2: Add @nestjs/throttler rate limiting (5 req/min on login/register) - M4: Remove orgSchema from JWT payload and client-side storage; tenant middleware now resolves schema from orgId via cached DB lookup - L1: Fix Chatwoot user identification (read from auth store on ready) - Remove schemaName from frontend Organization type and UI displays Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,7 @@ import * as os from 'node:os';
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { ValidationPipe } from '@nestjs/common';
|
||||
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
|
||||
import helmet from 'helmet';
|
||||
import { AppModule } from './app.module';
|
||||
|
||||
const cluster = _cluster as any; // Cast to 'any' bypasses the missing property errors
|
||||
@@ -41,6 +42,24 @@ async function bootstrap() {
|
||||
|
||||
app.setGlobalPrefix('api');
|
||||
|
||||
// Security headers — Helmet sets CSP, X-Frame-Options, X-Content-Type-Options,
|
||||
// Referrer-Policy, Permissions-Policy, and removes X-Powered-By
|
||||
app.use(
|
||||
helmet({
|
||||
contentSecurityPolicy: {
|
||||
directives: {
|
||||
defaultSrc: ["'self'"],
|
||||
scriptSrc: ["'self'", "'unsafe-inline'", 'https://chat.hoaledgeriq.com'],
|
||||
connectSrc: ["'self'", 'https://chat.hoaledgeriq.com', 'wss://chat.hoaledgeriq.com'],
|
||||
imgSrc: ["'self'", 'data:', 'https://chat.hoaledgeriq.com'],
|
||||
styleSrc: ["'self'", "'unsafe-inline'"],
|
||||
frameSrc: ["'self'", 'https://chat.hoaledgeriq.com'],
|
||||
fontSrc: ["'self'", 'data:'],
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
// Request logging — only in development (too noisy / slow for prod)
|
||||
if (!isProduction) {
|
||||
app.use((req: any, _res: any, next: any) => {
|
||||
@@ -63,15 +82,17 @@ async function bootstrap() {
|
||||
credentials: true,
|
||||
});
|
||||
|
||||
// Swagger docs — available in all environments
|
||||
const config = new DocumentBuilder()
|
||||
.setTitle('HOA LedgerIQ API')
|
||||
.setDescription('API for the HOA LedgerIQ')
|
||||
.setVersion('2026.03.10')
|
||||
.addBearerAuth()
|
||||
.build();
|
||||
const document = SwaggerModule.createDocument(app, config);
|
||||
SwaggerModule.setup('api/docs', app, document);
|
||||
// Swagger docs — disabled in production to avoid exposing API surface
|
||||
if (!isProduction) {
|
||||
const config = new DocumentBuilder()
|
||||
.setTitle('HOA LedgerIQ API')
|
||||
.setDescription('API for the HOA LedgerIQ')
|
||||
.setVersion('2026.3.11')
|
||||
.addBearerAuth()
|
||||
.build();
|
||||
const document = SwaggerModule.createDocument(app, config);
|
||||
SwaggerModule.setup('api/docs', app, document);
|
||||
}
|
||||
|
||||
await app.listen(3000);
|
||||
console.log(`Backend worker ${process.pid} listening on port 3000`);
|
||||
|
||||
Reference in New Issue
Block a user