diff --git a/backend/README.md b/backend/README.md index 3ec9552367bcd61a6c51276719cce4b86eddc0b7..783263b0d30ba03fc7ebd9497d02a090b1e3831a 100644 --- a/backend/README.md +++ b/backend/README.md @@ -36,3 +36,7 @@ The VM utilizes Nodemon to automatically restart the server if a crash occurs. The backend is automatically tested by a Gitlab CI server for each commit. Formatter: `yarn prettier` e2e tests: `yarn test` + +## Restrictions + +The backend will only allow 15 requests per minute from the same IP address. This is to prevent abuse of the API. diff --git a/backend/package.json b/backend/package.json index 59006f1f27eceda4fe4739add7dba63136414ccd..080f4830ff1269cbae9187c2373998e001004e8b 100644 --- a/backend/package.json +++ b/backend/package.json @@ -26,6 +26,7 @@ "typescript": "^5.0.4" }, "dependencies": { + "express-rate-limit": "^6.7.0", "firebase-admin": "^11.6.0", "node-cache": "^5.1.2", "prettier": "^2.8.4", diff --git a/backend/src/functions/console.ts b/backend/src/functions/console.ts index d6c294e9c0e37c21dcd1fca09f3efc51b1ae219f..3b1c17686b7f17605bc58cf1a478a9a1960fb223 100644 --- a/backend/src/functions/console.ts +++ b/backend/src/functions/console.ts @@ -7,9 +7,10 @@ export function log(message: string, status: string = 'info'): void { const time = new Date().toLocaleTimeString(); const file = new Error().stack?.split('at ')[2].split('\n')[0].trim(); + const cleanFile = file?.replace(/[()]/g, ''); const maxLength = 25; // Set the desired maximum length for the file section const paddedFile = - file?.split('/').pop()?.padEnd(maxLength, ' ') ?? ''.padEnd(maxLength, ' '); + cleanFile?.split('/').pop()?.padEnd(maxLength, ' ') ?? ''.padEnd(maxLength, ' '); // if status is info, log in default/black color. If warning use yellow, if danger use red const statusColor = status === 'info' ? '\x1b[0m' : status === 'warning' ? '\x1b[33m' : '\x1b[31m'; diff --git a/backend/src/index.ts b/backend/src/index.ts index 8ba97fb84085792e528589257f3014f19a60d9e6..fc3bc26bddd1ed35710d210f686db5fdc886a54e 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -12,6 +12,7 @@ import { disposeInactiveGames } from './functions/disposeInactiveGames'; import swaggerJsdoc from 'swagger-jsdoc'; import path from 'path'; import { firebaseLogger } from './middleware/firebaseLogger'; +import { rateLimiter } from './middleware/rateLimiter'; new GameHandler(); // singleton ;) @@ -22,6 +23,8 @@ const port = process.env.NODE_ENV === 'production' ? 80 : 4999; // middleware app.use(express.json()); // for parsing application/json app.use(expressLogger); // for request logging +app.set('trust proxy', 1); // required for rate limiting testing +app.use(rateLimiter); // for rate limiting // routes app.use('/lobby', lobbyRoutes); diff --git a/backend/src/middleware/rateLimiter.ts b/backend/src/middleware/rateLimiter.ts new file mode 100644 index 0000000000000000000000000000000000000000..fd7bc5d32f750e86bfcb76100880d23cc91e5dfd --- /dev/null +++ b/backend/src/middleware/rateLimiter.ts @@ -0,0 +1,9 @@ +const rateLimit = require('express-rate-limit'); + +// Configure rate limiter +export const rateLimiter = rateLimit({ + windowMs: 60 * 1000, // 1 minute in milliseconds + max: 15, // Limit each IP to 1000 requests per windowMs + statusCode: 429, // HTTP status code to send when rate limit is exceeded (Too Many Requests) + message: 'Too many requests, please try again later.', +}); diff --git a/backend/yarn.lock b/backend/yarn.lock index b2ab326facf2d95e267d9108d59c1c27293b1449..c14ca04da453b3dda2d1829c01164eb134e2489c 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -1404,6 +1404,11 @@ expect@^29.0.0: jest-message-util "^29.5.0" jest-util "^29.5.0" +express-rate-limit@^6.7.0: + version "6.7.0" + resolved "https://registry.yarnpkg.com/express-rate-limit/-/express-rate-limit-6.7.0.tgz#6aa8a1bd63dfe79702267b3af1161a93afc1d3c2" + integrity sha512-vhwIdRoqcYB/72TK3tRZI+0ttS8Ytrk24GfmsxDXK9o9IhHNO5bXRiXQSExPQ4GbaE5tvIS7j1SGrxsuWs+sGA== + express@^4.18.2: version "4.18.2" resolved "https://registry.npmjs.org/express/-/express-4.18.2.tgz"