diff --git a/package-lock.json b/package-lock.json index 3b9d5644..dd29f418 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", @@ -20,13 +21,14 @@ "@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", "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 +1591,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 +3648,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", @@ -3818,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", @@ -8885,9 +8885,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 +9619,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..42ce214b 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", @@ -31,13 +32,14 @@ "@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", "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/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 diff --git a/src/main.ts b/src/main.ts index 23c63c35..82b17eb4 100644 --- a/src/main.ts +++ b/src/main.ts @@ -7,6 +7,7 @@ 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'; dotenv.config(); async function bootstrap() { @@ -15,6 +16,26 @@ async function bootstrap() { 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, + }); + app.useGlobalInterceptors(new EmptyResponseInterceptor()); app.useGlobalFilters(new HttpExceptionFilter()); app.useGlobalPipes(new ValidationPipe({ whitelist: true, forbidNonWhitelisted: true })); @@ -56,11 +77,31 @@ async function bootstrap() { }); } - SwaggerModule.setup('api', app, document); + SwaggerModule.setup('api', app, document, { + }); + - // 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: isDevelopment ? false : { + directives: { + defaultSrc: ["'self'"], + scriptSrc: ["'self'"], + styleSrc: ["'self'"], + imgSrc: ["'self'", "data:", "validator.swagger.io"], + connectSrc: ["'self'", "https://discord.com", "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'); }