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:
2026-03-11 15:22:58 -04:00
parent a047144922
commit 208c1dd7bc
14 changed files with 105 additions and 64 deletions

View File

@@ -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`);