From 66521b5107b0ca4790e1c83185b952391a5beb2a Mon Sep 17 00:00:00 2001 From: Aurore Reynier Date: Tue, 17 Jun 2025 22:35:31 +0200 Subject: [PATCH 1/4] feat(86c4246cr): implement helmet, csp and adjust dependency necessity to have fastify and platform fastify at the same version --- package-lock.json | 71 +++++++++++++++++++++++------------------------ package.json | 3 +- src/main.ts | 31 ++++++++++++++++++++- 3 files changed, 66 insertions(+), 39 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3b9d5644..85318a69 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.1", "license": "UNLICENSED", "dependencies": { + "@fastify/helmet": "^13.0.1", "@fastify/static": "^8.1.1", "@nestjs/axios": "^4.0.0", "@nestjs/common": "^11.0.1", @@ -26,7 +27,7 @@ "class-transformer": "^0.5.1", "class-validator": "^0.14.1", "dotenv": "^16.4.7", - "fastify": "^5.2.1", + "fastify": "^5.3.3", "jsonwebtoken": "^9.0.2", "nestjs-pino": "^4.3.0", "passport": "^0.7.0", @@ -1589,6 +1590,26 @@ "integrity": "sha512-kJExsp4JCms7ipzg7SJ3y8DwmePaELHxKYtg+tZow+k0znUTf3cb+npgyqm8+ATZOdmfgfydIebPDWM172wfyA==", "license": "MIT" }, + "node_modules/@fastify/helmet": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/@fastify/helmet/-/helmet-13.0.1.tgz", + "integrity": "sha512-i+ifqazG3d0HwHL3zuZdg6B/WPc9Ee6kVfGpwGho4nxm0UaK1htss0zq+1rVhOoAorZlCgTZ3/i4S58hUGkkoA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "fastify-plugin": "^5.0.0", + "helmet": "^8.0.0" + } + }, "node_modules/@fastify/merge-json-schemas": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/@fastify/merge-json-schemas/-/merge-json-schemas-0.2.1.tgz", @@ -3626,39 +3647,6 @@ } } }, - "node_modules/@nestjs/platform-fastify/node_modules/fastify": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/fastify/-/fastify-5.3.3.tgz", - "integrity": "sha512-nCBiBCw9q6jPx+JJNVgO8JVnTXeUyrGcyTKPQikRkA/PanrFcOIo4R+ZnLeOLPZPGgzjomqfVarzE0kYx7qWiQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT", - "dependencies": { - "@fastify/ajv-compiler": "^4.0.0", - "@fastify/error": "^4.0.0", - "@fastify/fast-json-stringify-compiler": "^5.0.0", - "@fastify/proxy-addr": "^5.0.0", - "abstract-logging": "^2.0.1", - "avvio": "^9.0.0", - "fast-json-stringify": "^6.0.0", - "find-my-way": "^9.0.0", - "light-my-request": "^6.0.0", - "pino": "^9.0.0", - "process-warning": "^5.0.0", - "rfdc": "^1.3.1", - "secure-json-parse": "^4.0.0", - "semver": "^7.6.0", - "toad-cache": "^3.7.0" - } - }, "node_modules/@nestjs/schematics": { "version": "11.0.5", "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-11.0.5.tgz", @@ -8885,9 +8873,9 @@ "license": "BSD-3-Clause" }, "node_modules/fastify": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/fastify/-/fastify-5.4.0.tgz", - "integrity": "sha512-I4dVlUe+WNQAhKSyv15w+dwUh2EPiEl4X2lGYMmNSgF83WzTMAPKGdWEv5tPsCQOb+SOZwz8Vlta2vF+OeDgRw==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/fastify/-/fastify-5.3.3.tgz", + "integrity": "sha512-nCBiBCw9q6jPx+JJNVgO8JVnTXeUyrGcyTKPQikRkA/PanrFcOIo4R+ZnLeOLPZPGgzjomqfVarzE0kYx7qWiQ==", "funding": [ { "type": "github", @@ -9619,6 +9607,15 @@ "node": ">= 0.4" } }, + "node_modules/helmet": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-8.1.0.tgz", + "integrity": "sha512-jOiHyAZsmnr8LqoPGmCjYAaiuWwjAPLgY8ZX2XrmHawt99/u1y6RgrZMTeoPfpUbV96HOalYgz1qzkRbw54Pmg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/help-me": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", diff --git a/package.json b/package.json index 03b3b963..a8b33b98 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "test:e2e": "playwright test" }, "dependencies": { + "@fastify/helmet": "^13.0.1", "@fastify/static": "^8.1.1", "@nestjs/axios": "^4.0.0", "@nestjs/common": "^11.0.1", @@ -37,7 +38,7 @@ "class-transformer": "^0.5.1", "class-validator": "^0.14.1", "dotenv": "^16.4.7", - "fastify": "^5.2.1", + "fastify": "^5.3.3", "jsonwebtoken": "^9.0.2", "nestjs-pino": "^4.3.0", "passport": "^0.7.0", diff --git a/src/main.ts b/src/main.ts index 23c63c35..3d93aaa7 100644 --- a/src/main.ts +++ b/src/main.ts @@ -7,6 +7,8 @@ import { EmptyResponseInterceptor } from './common/interceptors/empty-response.i import { HttpExceptionFilter } from './common/filters/http-exception.filter'; import { ValidationPipe } from '@nestjs/common'; import { join } from 'path'; +import helmet from '@fastify/helmet'; +import { createHash } from 'crypto'; dotenv.config(); async function bootstrap() { @@ -14,6 +16,10 @@ async function bootstrap() { AppModule, new FastifyAdapter(), ); + + const nonce = createHash('sha256') + .update(Date.now().toString()) + .digest('base64'); app.useGlobalInterceptors(new EmptyResponseInterceptor()); app.useGlobalFilters(new HttpExceptionFilter()); @@ -56,11 +62,34 @@ async function bootstrap() { }); } - SwaggerModule.setup('api', app, document); + SwaggerModule.setup('api', app, document, { + customJs: [ + `'nonce-${nonce}'` + ] + }); // Nous ne définissons plus de préfixe global pour l'API // app.setGlobalPrefix('api'); + //Attention, lorsque fastify et nestjs/platform-fastify n'ont pas la même version, + //cela provoque des erreurs sur helmet + await app.register(helmet, { + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + scriptSrc: ["'self'", `'nonce-${nonce}'`], + styleSrc: ["'self'", `'nonce-${nonce}'`], + imgSrc: ["'self'", "data:", "validator.swagger.io"], + connectSrc: ["'self'", "https://discord.com/api","http://localhost:3000", "http://127.0.0.1:3000" ], + fontSrc: ["'self'"], + objectSrc: ["'none'"], + mediaSrc: ["'none'"], + frameSrc: ["'none'"], + formAction: ["'self'"], + } + }, + }); + // Configuration de la version de l'API await app.listen(3000, '0.0.0.0'); } From 8474161bec92e27aee64a674365ff65193dc7cb7 Mon Sep 17 00:00:00 2001 From: Aurore Reynier Date: Tue, 17 Jun 2025 22:51:50 +0200 Subject: [PATCH 2/4] feat(86c4246hm): implement cors --- src/main.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/main.ts b/src/main.ts index 3d93aaa7..3acb3d00 100644 --- a/src/main.ts +++ b/src/main.ts @@ -16,6 +16,26 @@ async function bootstrap() { AppModule, new FastifyAdapter(), ); + + // Configuration CORS + app.enableCors({ + origin: [ + 'http://localhost:3000', + 'https://discord.com', + 'https://discordapp.com', + 'https://cdn.discordapp.com', + 'https://discord.gg', + 'wss://gateway.discord.gg' + ], + methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'], + allowedHeaders: [ + 'Content-Type', + 'Authorization', + 'X-Super-Properties', + 'X-Discord-Locale' + ], + credentials: false, + }); const nonce = createHash('sha256') .update(Date.now().toString()) From b5594eddb8c1f316492082661631244645e2ff58 Mon Sep 17 00:00:00 2001 From: Aurore Reynier Date: Tue, 17 Jun 2025 23:00:08 +0200 Subject: [PATCH 3/4] feat(86c4246ke): add rate limit --- package-lock.json | 12 ++++++++++++ package.json | 1 + src/app.module.ts | 8 +++++++- 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 85318a69..dd29f418 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "@nestjs/platform-express": "^11.0.1", "@nestjs/platform-fastify": "^11.0.9", "@nestjs/swagger": "^11.0.5", + "@nestjs/throttler": "^6.4.0", "@nestjs/typeorm": "^11.0.0", "@types/uuid": "^10.0.0", "axios": "^1.7.9", @@ -3806,6 +3807,17 @@ } } }, + "node_modules/@nestjs/throttler": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@nestjs/throttler/-/throttler-6.4.0.tgz", + "integrity": "sha512-osL67i0PUuwU5nqSuJjtUJZMkxAnYB4VldgYUMGzvYRJDCqGRFMWbsbzm/CkUtPLRL30I8T74Xgt/OQxnYokiA==", + "license": "MIT", + "peerDependencies": { + "@nestjs/common": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0", + "@nestjs/core": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0", + "reflect-metadata": "^0.1.13 || ^0.2.0" + } + }, "node_modules/@nestjs/typeorm": { "version": "11.0.0", "resolved": "https://registry.npmjs.org/@nestjs/typeorm/-/typeorm-11.0.0.tgz", diff --git a/package.json b/package.json index a8b33b98..42ce214b 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "@nestjs/platform-express": "^11.0.1", "@nestjs/platform-fastify": "^11.0.9", "@nestjs/swagger": "^11.0.5", + "@nestjs/throttler": "^6.4.0", "@nestjs/typeorm": "^11.0.0", "@types/uuid": "^10.0.0", "axios": "^1.7.9", diff --git a/src/app.module.ts b/src/app.module.ts index f233a74a..bbe76494 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -37,6 +37,8 @@ import { AnswerTemplatesModule } from './answer-templates/answer-templates.modul // IMPORTS POUR LA SÉCURITÉ GLOBALE import { APP_GUARD } from '@nestjs/core'; import { GlobalAuthGuard } from './auth/guards/global-auth.guard'; +import { ThrottlerModule } from '@nestjs/throttler'; + /** * Module principal de l'application @@ -84,7 +86,11 @@ import { GlobalAuthGuard } from './auth/guards/global-auth.guard'; SignatureModule, PollTemplatesModule, QuestionTemplatesModule, - AnswerTemplatesModule + AnswerTemplatesModule, + ThrottlerModule.forRoot([{ + ttl: 1, // 1 seconde + limit: 50, // 50 requêtes par seconde + }]), ], controllers: [AppController], // CONFIGURATION DU GUARD GLOBAL From a1ad9375e338b596c1126487ca2d692c31f19149 Mon Sep 17 00:00:00 2001 From: Aurore Reynier Date: Sun, 29 Jun 2025 00:21:12 +0200 Subject: [PATCH 4/4] feat(86c4246cr): change csp wether it's dev or prod environment --- src/main.ts | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/src/main.ts b/src/main.ts index 3acb3d00..82b17eb4 100644 --- a/src/main.ts +++ b/src/main.ts @@ -8,7 +8,6 @@ import { HttpExceptionFilter } from './common/filters/http-exception.filter'; import { ValidationPipe } from '@nestjs/common'; import { join } from 'path'; import helmet from '@fastify/helmet'; -import { createHash } from 'crypto'; dotenv.config(); async function bootstrap() { @@ -21,7 +20,7 @@ async function bootstrap() { app.enableCors({ origin: [ 'http://localhost:3000', - 'https://discord.com', + 'https://discord.com', 'https://discordapp.com', 'https://cdn.discordapp.com', 'https://discord.gg', @@ -36,10 +35,6 @@ async function bootstrap() { ], credentials: false, }); - - const nonce = createHash('sha256') - .update(Date.now().toString()) - .digest('base64'); app.useGlobalInterceptors(new EmptyResponseInterceptor()); app.useGlobalFilters(new HttpExceptionFilter()); @@ -83,24 +78,21 @@ async function bootstrap() { } SwaggerModule.setup('api', app, document, { - customJs: [ - `'nonce-${nonce}'` - ] }); - // Nous ne définissons plus de préfixe global pour l'API - // app.setGlobalPrefix('api'); + + const isDevelopment = process.env.NODE_ENV === 'development'; //Attention, lorsque fastify et nestjs/platform-fastify n'ont pas la même version, //cela provoque des erreurs sur helmet await app.register(helmet, { - contentSecurityPolicy: { + contentSecurityPolicy: isDevelopment ? false : { directives: { defaultSrc: ["'self'"], - scriptSrc: ["'self'", `'nonce-${nonce}'`], - styleSrc: ["'self'", `'nonce-${nonce}'`], + scriptSrc: ["'self'"], + styleSrc: ["'self'"], imgSrc: ["'self'", "data:", "validator.swagger.io"], - connectSrc: ["'self'", "https://discord.com/api","http://localhost:3000", "http://127.0.0.1:3000" ], + connectSrc: ["'self'", "https://discord.com", "https://discord.com/api", "http://localhost:3000", "http://127.0.0.1:3000" ], fontSrc: ["'self'"], objectSrc: ["'none'"], mediaSrc: ["'none'"],