diff --git a/README.md b/README.md index c2d4806580..11d228de62 100644 --- a/README.md +++ b/README.md @@ -187,6 +187,7 @@ The diagram illustrates the repository's architecture, which is considered overl - **Network Error Logging** - HTTP header `NEL` - **response-time** - HTTP header `X-Response-Time` - **connect-timeout** - Request timeout +- **request-ip** - IP address retrieving - **Terminus** - Health check and graceful shutdown - **pino** - Logging - **dotenv-flow** - Environment variables loading diff --git a/api-node/package-lock.json b/api-node/package-lock.json index de1d1887f3..ebeba7abc2 100644 --- a/api-node/package-lock.json +++ b/api-node/package-lock.json @@ -56,10 +56,10 @@ "pino-http": "10.3.0", "rate-limiter-flexible": "5.0.4", "report-to": "1.1.0", + "request-ip": "3.3.0", "response-time": "2.3.3", "serve-favicon": "2.5.0", "spdy": "4.0.2", - "tiny-csrf": "1.1.4", "validator": "13.12.0", "ws": "8.18.0", "yargs": "17.7.2" @@ -78,7 +78,7 @@ "@types/cookie-parser": "1.4.8", "@types/cors": "2.8.17", "@types/dotenv-flow": "3.3.3", - "@types/express": "4.17.21", + "@types/express": "5.0.0", "@types/express-list-endpoints": "6.0.3", "@types/graphql-depth-limit": "1.1.6", "@types/graphql-upload": "8.0.12", @@ -88,6 +88,7 @@ "@types/lodash.uniq": "4.5.9", "@types/multer": "1.4.12", "@types/node": "22.10.1", + "@types/request-ip": "0.0.41", "@types/response-time": "2.3.8", "@types/serve-favicon": "2.5.7", "@types/spdy": "3.4.9", @@ -4616,13 +4617,14 @@ "dev": true }, "node_modules/@types/express": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", - "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.0.tgz", + "integrity": "sha512-DvZriSMehGHL1ZNLzi6MidnsDhUZM/x2pRdDIKdwbUNqqwHxMlRdkxtn6/EPKyqKpHqTl/4nRZsRNLpZxZRpPQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", + "@types/express-serve-static-core": "^5.0.0", "@types/qs": "*", "@types/serve-static": "*" } @@ -4637,10 +4639,11 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "4.17.37", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.37.tgz", - "integrity": "sha512-ZohaCYTgGFcOP7u6aJOhY9uIZQgZ2vxC2yWoArY+FeDXlqeH66ZVBjgvg+RLVAS/DWNq4Ap9ZXu1+SUQiiWYMg==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.2.tgz", + "integrity": "sha512-vluaspfvWEtE4vcSDlKRNer52DvOGrB2xv6diXy6UKyKW0lqZiWHGNApSyxOv+8DE5Z27IzVvE7hNkxg7EXIcg==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*", "@types/qs": "*", @@ -4891,6 +4894,15 @@ "integrity": "sha512-xrO9OoVPqFuYyR/loIHjnbvvyRZREYKLjxV4+dY6v3FQR3stQ9ZxIGkaclF7YhI9hfjpuTbu14hZEy94qKLtOA==", "dev": true }, + "node_modules/@types/request-ip": { + "version": "0.0.41", + "resolved": "https://registry.npmjs.org/@types/request-ip/-/request-ip-0.0.41.tgz", + "integrity": "sha512-Qzz0PM2nSZej4lsLzzNfADIORZhhxO7PED0fXpg4FjXiHuJ/lMyUg+YFF5q8x9HPZH3Gl6N+NOM8QZjItNgGKg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/response-time": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/@types/response-time/-/response-time-2.3.8.tgz", @@ -6358,6 +6370,20 @@ "node": ">= 0.8" } }, + "node_modules/body-parser/node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/body-parser/node_modules/raw-body": { "version": "3.0.0-beta.1", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0-beta.1.tgz", @@ -9924,6 +9950,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -9932,6 +9959,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", @@ -9947,6 +9975,7 @@ "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", "dependencies": { "ee-first": "1.1.1" }, @@ -9954,10 +9983,25 @@ "node": ">= 0.8" } }, + "node_modules/express/node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/express/node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -16555,11 +16599,13 @@ ] }, "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "devOptional": true, + "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" }, "engines": { "node": ">=0.6" @@ -17008,6 +17054,11 @@ "node": ">= 6" } }, + "node_modules/request-ip": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/request-ip/-/request-ip-3.3.0.tgz", + "integrity": "sha512-cA6Xh6e0fDBBBwH77SLJaJPBmD3nWVAcF9/XAcsrIHdjhFzFiB5aNQFytdjCGPezU3ROwrR11IddKAM08vohxA==" + }, "node_modules/request/node_modules/form-data": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", @@ -17877,13 +17928,18 @@ } }, "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -18627,21 +18683,6 @@ "node": ">=10" } }, - "node_modules/superagent/node_modules/qs": { - "version": "6.11.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", - "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", - "dev": true, - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/superagent/node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -18887,11 +18928,6 @@ "node": ">=8" } }, - "node_modules/tiny-csrf": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/tiny-csrf/-/tiny-csrf-1.1.4.tgz", - "integrity": "sha512-tyWj/ihvuPCQ8YGy55O/MnSnO4bZ5s3m+Ju3EXcBCFLz09RYLw/iHgy32wd93nEFhqHgh67MKO9669fHfk3cVg==" - }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -19686,21 +19722,6 @@ "node": ">=4" } }, - "node_modules/url/node_modules/qs": { - "version": "6.11.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", - "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", - "optional": true, - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/util": { "version": "0.12.5", "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", diff --git a/api-node/package.json b/api-node/package.json index 4088987adc..00f15e41a0 100644 --- a/api-node/package.json +++ b/api-node/package.json @@ -77,6 +77,7 @@ "pino-http": "10.3.0", "rate-limiter-flexible": "5.0.4", "report-to": "1.1.0", + "request-ip": "3.3.0", "response-time": "2.3.3", "serve-favicon": "2.5.0", "spdy": "4.0.2", @@ -98,7 +99,7 @@ "@types/cookie-parser": "1.4.8", "@types/cors": "2.8.17", "@types/dotenv-flow": "3.3.3", - "@types/express": "4.17.21", + "@types/express": "5.0.0", "@types/express-list-endpoints": "6.0.3", "@types/graphql-depth-limit": "1.1.6", "@types/graphql-upload": "8.0.12", @@ -108,6 +109,7 @@ "@types/lodash.uniq": "4.5.9", "@types/multer": "1.4.12", "@types/node": "22.10.1", + "@types/request-ip": "0.0.41", "@types/response-time": "2.3.8", "@types/serve-favicon": "2.5.7", "@types/spdy": "3.4.9", diff --git a/api-node/src/security/middlewares/authMiddleware.ts b/api-node/src/security/middlewares/authMiddleware.ts index 1e85bcbe9d..76b2bc4828 100644 --- a/api-node/src/security/middlewares/authMiddleware.ts +++ b/api-node/src/security/middlewares/authMiddleware.ts @@ -3,6 +3,8 @@ import { expressjwt as jwt } from 'express-jwt'; import config from '../../config'; const authMiddleware = (): RequestHandler => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error return jwt({ secret: config.jwtSecret, algorithms: ['HS256'], diff --git a/api-node/src/security/middlewares/rateLimitMiddleware.ts b/api-node/src/security/middlewares/rateLimitMiddleware.ts index 6f6abb43b8..f20262909d 100644 --- a/api-node/src/security/middlewares/rateLimitMiddleware.ts +++ b/api-node/src/security/middlewares/rateLimitMiddleware.ts @@ -1,6 +1,7 @@ import { NextFunction, Request, RequestHandler, Response } from 'express'; import Redis from 'ioredis'; import { BurstyRateLimiter, RateLimiterMemory, RateLimiterRedis } from 'rate-limiter-flexible'; +import { getClientIp } from 'request-ip'; const BURST_POINTS_RATE = 2.5; const BURST_DURATION_RATE = 10; @@ -54,8 +55,9 @@ const rateLimitMiddleware = ( const rateLimiter = new BurstyRateLimiter(redisRateLimiter, burstRedisRateLimiter); return (req: Request, res: Response, next: NextFunction) => { + const ip = getClientIp(req) || 'unknown'; return rateLimiter - .consume(req.ip) + .consume(ip) .then(() => { next(); })