From 45ce76061b02e35aeb5ff2eb3ac9e2a2c866d170 Mon Sep 17 00:00:00 2001 From: Jason Woltje Date: Sun, 1 Mar 2026 16:38:40 -0600 Subject: [PATCH 1/2] fix(api): helmet security headers + auth endpoint rate limiting --- apps/api/src/auth/auth.controller.ts | 2 +- apps/api/src/main.ts | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/apps/api/src/auth/auth.controller.ts b/apps/api/src/auth/auth.controller.ts index f0bd96b..b677f48 100644 --- a/apps/api/src/auth/auth.controller.ts +++ b/apps/api/src/auth/auth.controller.ts @@ -106,7 +106,7 @@ export class AuthController { // @SkipCsrf avoids double-protection conflicts. // See: https://www.better-auth.com/docs/reference/security @SkipCsrf() - @Throttle({ strict: { limit: 10, ttl: 60000 } }) + @Throttle({ default: { ttl: 60_000, limit: 5 } }) async handleAuth(@Req() req: ExpressRequest, @Res() res: ExpressResponse): Promise { // Extract client IP for logging const clientIp = this.getClientIp(req); diff --git a/apps/api/src/main.ts b/apps/api/src/main.ts index 55dafa1..d55233d 100644 --- a/apps/api/src/main.ts +++ b/apps/api/src/main.ts @@ -1,6 +1,7 @@ import { NestFactory } from "@nestjs/core"; import { RequestMethod, ValidationPipe } from "@nestjs/common"; import cookieParser from "cookie-parser"; +import helmet from "helmet"; import { AppModule } from "./app.module"; import { getTrustedOrigins } from "./auth/auth.config"; import { GlobalExceptionFilter } from "./filters/global-exception.filter"; @@ -33,6 +34,14 @@ async function bootstrap() { // Enable cookie parser for session handling app.use(cookieParser()); + // Enable helmet security headers + app.use( + helmet({ + contentSecurityPolicy: false, // Let Next.js handle CSP + crossOriginEmbedderPolicy: false, + }) + ); + // Enable global validation pipe with transformation app.useGlobalPipes( new ValidationPipe({ -- 2.49.1 From c25b77ae39fc3104547ee5e5bea05be893cdeb5a Mon Sep 17 00:00:00 2001 From: Jason Woltje Date: Sun, 1 Mar 2026 16:39:14 -0600 Subject: [PATCH 2/2] chore(api): add helmet dependency --- apps/api/package.json | 1 + pnpm-lock.yaml | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/apps/api/package.json b/apps/api/package.json index e062051..ce7a9ff 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -62,6 +62,7 @@ "discord.js": "^14.25.1", "dockerode": "^4.0.9", "gray-matter": "^4.0.3", + "helmet": "^8.1.0", "highlight.js": "^11.11.1", "ioredis": "^5.9.2", "jose": "^6.1.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 74fe90f..f382600 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -180,6 +180,9 @@ importers: gray-matter: specifier: ^4.0.3 version: 4.0.3 + helmet: + specifier: ^8.1.0 + version: 8.1.0 highlight.js: specifier: ^11.11.1 version: 11.11.1 @@ -5210,6 +5213,10 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + helmet@8.1.0: + resolution: {integrity: sha512-jOiHyAZsmnr8LqoPGmCjYAaiuWwjAPLgY8ZX2XrmHawt99/u1y6RgrZMTeoPfpUbV96HOalYgz1qzkRbw54Pmg==} + engines: {node: '>=18.0.0'} + highlight.js@11.11.1: resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==} engines: {node: '>=12.0.0'} @@ -12815,6 +12822,8 @@ snapshots: dependencies: function-bind: 1.1.2 + helmet@8.1.0: {} + highlight.js@11.11.1: {} html-encoding-sniffer@4.0.0: -- 2.49.1