bipulsardar commited on
Commit
c8aab3a
·
1 Parent(s): 2ec5005
dist/app.js CHANGED
@@ -9,6 +9,7 @@ const path_1 = __importDefault(require("path"));
9
  const app_config_1 = require("./config/app.config");
10
  const logger_middleware_1 = require("./middleware/logger.middleware");
11
  const error_middleware_1 = require("./middleware/error.middleware");
 
12
  const api_routes_1 = require("./routes/api.routes");
13
  // Repositories
14
  const fileTaskRepository_1 = require("./repositories/fileStorage/fileTaskRepository");
@@ -29,6 +30,7 @@ const admin_controller_1 = require("./controllers/admin.controller");
29
  function createApp() {
30
  const app = (0, express_1.default)();
31
  // Middleware
 
32
  app.use(express_1.default.json());
33
  app.use(express_1.default.urlencoded({ extended: true }));
34
  app.use(logger_middleware_1.requestLogger);
 
9
  const app_config_1 = require("./config/app.config");
10
  const logger_middleware_1 = require("./middleware/logger.middleware");
11
  const error_middleware_1 = require("./middleware/error.middleware");
12
+ const rateLimit_middleware_1 = require("./middleware/rateLimit.middleware");
13
  const api_routes_1 = require("./routes/api.routes");
14
  // Repositories
15
  const fileTaskRepository_1 = require("./repositories/fileStorage/fileTaskRepository");
 
30
  function createApp() {
31
  const app = (0, express_1.default)();
32
  // Middleware
33
+ app.use(rateLimit_middleware_1.globalRateLimiter);
34
  app.use(express_1.default.json());
35
  app.use(express_1.default.urlencoded({ extended: true }));
36
  app.use(logger_middleware_1.requestLogger);
dist/middleware/rateLimit.middleware.js ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.strictActionRateLimiter = exports.globalRateLimiter = void 0;
7
+ const express_rate_limit_1 = __importDefault(require("express-rate-limit"));
8
+ /**
9
+ * General application rate limiter to suppress bot scanning volume and protect endpoints.
10
+ * Allows legitimate dashboard background polling (every ~3s = 60 req/min) while blocking aggressive scanners.
11
+ */
12
+ exports.globalRateLimiter = (0, express_rate_limit_1.default)({
13
+ windowMs: 15 * 60 * 1000, // 15 minutes
14
+ limit: 500, // Limit each IP to 500 requests per 15-minute window
15
+ standardHeaders: 'draft-7',
16
+ legacyHeaders: false,
17
+ message: {
18
+ status: 429,
19
+ error: 'Too Many Requests',
20
+ message: 'Rate limit exceeded. Please slow down your requests and try again later.',
21
+ },
22
+ });
23
+ /**
24
+ * Strict rate limiter for sensitive state-modifying endpoints (e.g., worker shutdown, task reset/generation).
25
+ */
26
+ exports.strictActionRateLimiter = (0, express_rate_limit_1.default)({
27
+ windowMs: 15 * 60 * 1000, // 15 minutes
28
+ limit: 30, // Limit each IP to 30 administrative actions per 15-minute window
29
+ standardHeaders: 'draft-7',
30
+ legacyHeaders: false,
31
+ message: {
32
+ status: 429,
33
+ error: 'Too Many Requests',
34
+ message: 'Rate limit exceeded for administrative actions. Please try again later.',
35
+ },
36
+ });
dist/routes/api.routes.js CHANGED
@@ -2,6 +2,7 @@
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
  exports.createApiRouter = createApiRouter;
4
  const express_1 = require("express");
 
5
  function createApiRouter(healthController, adminController) {
6
  const router = (0, express_1.Router)();
7
  // Health check
@@ -11,8 +12,9 @@ function createApiRouter(healthController, adminController) {
11
  router.get('/workers', adminController.getWorkers);
12
  router.get('/tasks', adminController.getTasks);
13
  router.get('/dataset/rows', adminController.getDatasetRows);
14
- router.post('/tasks/create', adminController.createTask);
15
- router.post('/tasks/reset', adminController.resetTasks);
16
- router.post('/shutdown-worker', adminController.shutdownWorker);
 
17
  return router;
18
  }
 
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
  exports.createApiRouter = createApiRouter;
4
  const express_1 = require("express");
5
+ const rateLimit_middleware_1 = require("../middleware/rateLimit.middleware");
6
  function createApiRouter(healthController, adminController) {
7
  const router = (0, express_1.Router)();
8
  // Health check
 
12
  router.get('/workers', adminController.getWorkers);
13
  router.get('/tasks', adminController.getTasks);
14
  router.get('/dataset/rows', adminController.getDatasetRows);
15
+ // Sensitive administrative POST actions protected by strict rate limit
16
+ router.post('/tasks/create', rateLimit_middleware_1.strictActionRateLimiter, adminController.createTask);
17
+ router.post('/tasks/reset', rateLimit_middleware_1.strictActionRateLimiter, adminController.resetTasks);
18
+ router.post('/shutdown-worker', rateLimit_middleware_1.strictActionRateLimiter, adminController.shutdownWorker);
19
  return router;
20
  }
package-lock.json CHANGED
@@ -11,6 +11,7 @@
11
  "dependencies": {
12
  "ejs": "^3.1.10",
13
  "express": "^4.21.2",
 
14
  "mime-types": "^2.1.35",
15
  "multer": "^1.4.5-lts.1",
16
  "ws": "^8.18.0"
@@ -719,6 +720,48 @@
719
  "url": "https://opencollective.com/express"
720
  }
721
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
722
  "node_modules/filelist": {
723
  "version": "1.0.6",
724
  "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz",
@@ -990,6 +1033,15 @@
990
  "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
991
  "license": "ISC"
992
  },
 
 
 
 
 
 
 
 
 
993
  "node_modules/ipaddr.js": {
994
  "version": "1.9.1",
995
  "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
 
11
  "dependencies": {
12
  "ejs": "^3.1.10",
13
  "express": "^4.21.2",
14
+ "express-rate-limit": "^8.6.0",
15
  "mime-types": "^2.1.35",
16
  "multer": "^1.4.5-lts.1",
17
  "ws": "^8.18.0"
 
720
  "url": "https://opencollective.com/express"
721
  }
722
  },
723
+ "node_modules/express-rate-limit": {
724
+ "version": "8.6.0",
725
+ "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.6.0.tgz",
726
+ "integrity": "sha512-XKJXDsASUOo0LLtFwW5hCcQGH0N4WQc/Rn8/Pvoia+TJFOkkFPvrtW9lZOeeNcxQJspvOIERMwiRLsVFlhHEkA==",
727
+ "license": "MIT",
728
+ "dependencies": {
729
+ "debug": "^4.4.3",
730
+ "ip-address": "^10.2.0"
731
+ },
732
+ "engines": {
733
+ "node": ">= 16"
734
+ },
735
+ "funding": {
736
+ "url": "https://github.com/sponsors/express-rate-limit"
737
+ },
738
+ "peerDependencies": {
739
+ "express": ">= 4.11"
740
+ }
741
+ },
742
+ "node_modules/express-rate-limit/node_modules/debug": {
743
+ "version": "4.4.3",
744
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
745
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
746
+ "license": "MIT",
747
+ "dependencies": {
748
+ "ms": "^2.1.3"
749
+ },
750
+ "engines": {
751
+ "node": ">=6.0"
752
+ },
753
+ "peerDependenciesMeta": {
754
+ "supports-color": {
755
+ "optional": true
756
+ }
757
+ }
758
+ },
759
+ "node_modules/express-rate-limit/node_modules/ms": {
760
+ "version": "2.1.3",
761
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
762
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
763
+ "license": "MIT"
764
+ },
765
  "node_modules/filelist": {
766
  "version": "1.0.6",
767
  "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz",
 
1033
  "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
1034
  "license": "ISC"
1035
  },
1036
+ "node_modules/ip-address": {
1037
+ "version": "10.2.0",
1038
+ "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz",
1039
+ "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==",
1040
+ "license": "MIT",
1041
+ "engines": {
1042
+ "node": ">= 12"
1043
+ }
1044
+ },
1045
  "node_modules/ipaddr.js": {
1046
  "version": "1.9.1",
1047
  "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
package.json CHANGED
@@ -20,6 +20,7 @@
20
  "dependencies": {
21
  "ejs": "^3.1.10",
22
  "express": "^4.21.2",
 
23
  "mime-types": "^2.1.35",
24
  "multer": "^1.4.5-lts.1",
25
  "ws": "^8.18.0"
 
20
  "dependencies": {
21
  "ejs": "^3.1.10",
22
  "express": "^4.21.2",
23
+ "express-rate-limit": "^8.6.0",
24
  "mime-types": "^2.1.35",
25
  "multer": "^1.4.5-lts.1",
26
  "ws": "^8.18.0"
src/app.ts CHANGED
@@ -3,6 +3,7 @@ import path from 'path';
3
  import { CONFIG } from './config/app.config';
4
  import { requestLogger } from './middleware/logger.middleware';
5
  import { errorHandler } from './middleware/error.middleware';
 
6
  import { createApiRouter } from './routes/api.routes';
7
 
8
  // Repositories
@@ -45,6 +46,7 @@ export function createApp(): AppContext {
45
  const app = express();
46
 
47
  // Middleware
 
48
  app.use(express.json());
49
  app.use(express.urlencoded({ extended: true }));
50
  app.use(requestLogger);
 
3
  import { CONFIG } from './config/app.config';
4
  import { requestLogger } from './middleware/logger.middleware';
5
  import { errorHandler } from './middleware/error.middleware';
6
+ import { globalRateLimiter } from './middleware/rateLimit.middleware';
7
  import { createApiRouter } from './routes/api.routes';
8
 
9
  // Repositories
 
46
  const app = express();
47
 
48
  // Middleware
49
+ app.use(globalRateLimiter);
50
  app.use(express.json());
51
  app.use(express.urlencoded({ extended: true }));
52
  app.use(requestLogger);
src/middleware/rateLimit.middleware.ts ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import rateLimit from 'express-rate-limit';
2
+
3
+ /**
4
+ * General application rate limiter to suppress bot scanning volume and protect endpoints.
5
+ * Allows legitimate dashboard background polling (every ~3s = 60 req/min) while blocking aggressive scanners.
6
+ */
7
+ export const globalRateLimiter = rateLimit({
8
+ windowMs: 15 * 60 * 1000, // 15 minutes
9
+ limit: 500, // Limit each IP to 500 requests per 15-minute window
10
+ standardHeaders: 'draft-7',
11
+ legacyHeaders: false,
12
+ message: {
13
+ status: 429,
14
+ error: 'Too Many Requests',
15
+ message: 'Rate limit exceeded. Please slow down your requests and try again later.',
16
+ },
17
+ });
18
+
19
+ /**
20
+ * Strict rate limiter for sensitive state-modifying endpoints (e.g., worker shutdown, task reset/generation).
21
+ */
22
+ export const strictActionRateLimiter = rateLimit({
23
+ windowMs: 15 * 60 * 1000, // 15 minutes
24
+ limit: 30, // Limit each IP to 30 administrative actions per 15-minute window
25
+ standardHeaders: 'draft-7',
26
+ legacyHeaders: false,
27
+ message: {
28
+ status: 429,
29
+ error: 'Too Many Requests',
30
+ message: 'Rate limit exceeded for administrative actions. Please try again later.',
31
+ },
32
+ });
src/routes/api.routes.ts CHANGED
@@ -1,6 +1,7 @@
1
  import { Router } from 'express';
2
  import { HealthController } from '../controllers/health.controller';
3
  import { AdminController } from '../controllers/admin.controller';
 
4
 
5
  export function createApiRouter(
6
  healthController: HealthController,
@@ -16,9 +17,12 @@ export function createApiRouter(
16
  router.get('/workers', adminController.getWorkers);
17
  router.get('/tasks', adminController.getTasks);
18
  router.get('/dataset/rows', adminController.getDatasetRows);
19
- router.post('/tasks/create', adminController.createTask);
20
- router.post('/tasks/reset', adminController.resetTasks);
21
- router.post('/shutdown-worker', adminController.shutdownWorker);
 
 
22
 
23
  return router;
24
  }
 
 
1
  import { Router } from 'express';
2
  import { HealthController } from '../controllers/health.controller';
3
  import { AdminController } from '../controllers/admin.controller';
4
+ import { strictActionRateLimiter } from '../middleware/rateLimit.middleware';
5
 
6
  export function createApiRouter(
7
  healthController: HealthController,
 
17
  router.get('/workers', adminController.getWorkers);
18
  router.get('/tasks', adminController.getTasks);
19
  router.get('/dataset/rows', adminController.getDatasetRows);
20
+
21
+ // Sensitive administrative POST actions protected by strict rate limit
22
+ router.post('/tasks/create', strictActionRateLimiter, adminController.createTask);
23
+ router.post('/tasks/reset', strictActionRateLimiter, adminController.resetTasks);
24
+ router.post('/shutdown-worker', strictActionRateLimiter, adminController.shutdownWorker);
25
 
26
  return router;
27
  }
28
+