diff --git a/webui/package.json b/webui/package.json index 947dd5d1e..40128da0c 100644 --- a/webui/package.json +++ b/webui/package.json @@ -44,6 +44,7 @@ "clipboard-copy": "^4.0.1", "clsx": "^1.2.1", "dompurify": "^3.0.4", + "express-rate-limit": "^7.2.0", "fetch-retry": "^5.0.6", "lodash": "^4.17.21", "markdown-it": "^13.0.1", diff --git a/webui/src/default/server.ts b/webui/src/default/server.ts index c528dd31f..0976c29f9 100644 --- a/webui/src/default/server.ts +++ b/webui/src/default/server.ts @@ -10,9 +10,39 @@ import * as express from 'express'; import * as path from 'path'; +import { rateLimit } from 'express-rate-limit'; const app = express(); +const args = process.argv.slice(2); +if (args.indexOf('-ratelimit') != -1) { + const proxiesIndex = args.indexOf('-ratelimit-proxies'); + if (proxiesIndex != -1) { + app.set('trust proxy', Number(args[proxiesIndex + 1])); + } + + let windowMs = 15 * 60 * 1000; // 15 minutes + const rateLimitWindowIndex = args.indexOf('-ratelimit-window-seconds'); + if (rateLimitWindowIndex != -1) { + windowMs = Number(args[rateLimitWindowIndex + 1]) * 1000; + } + + let limit = 100; // Limit each IP to 100 requests per windowMs + const rateLimitAmountIndex = args.indexOf('-ratelimit-limit'); + if (rateLimitAmountIndex != -1) { + limit = Number(args[rateLimitAmountIndex + 1]); + } + + // Apply rate limiter to all requests + const limiter = rateLimit({ + windowMs, + limit, + standardHeaders: 'draft-7', + legacyHeaders: false + }); + app.use(limiter); +} + // Serve static resources const staticPath = path.join(__dirname, '..', '..', 'static'); app.use(express.static(staticPath)); diff --git a/webui/yarn.lock b/webui/yarn.lock index f3d7fe4ff..94839e9c6 100644 --- a/webui/yarn.lock +++ b/webui/yarn.lock @@ -2840,6 +2840,15 @@ __metadata: languageName: node linkType: hard +"express-rate-limit@npm:^7.2.0": + version: 7.2.0 + resolution: "express-rate-limit@npm:7.2.0" + peerDependencies: + express: 4 || 5 || ^5.0.0-beta.1 + checksum: 10/1cd33daeeeb3428f8990718512e8c803ca3406d1e99cbc38bcda12056a42ea51319a7bbc357bdc02d79b7f2b508c743895e8805118115cc637e180eb420e643f + languageName: node + linkType: hard + "express@npm:^4.18.2": version: 4.18.2 resolution: "express@npm:4.18.2" @@ -4648,6 +4657,7 @@ __metadata: eslint: "npm:^8.44.0" eslint-plugin-react: "npm:^7.32.2" express: "npm:^4.18.2" + express-rate-limit: "npm:^7.2.0" fetch-retry: "npm:^5.0.6" lodash: "npm:^4.17.21" markdown-it: "npm:^13.0.1"