express-rate-limit 8.0.1 → 8.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -39,7 +39,8 @@ function ipKeyGenerator(ip, ipv6Subnet = 56) {
39
39
 
40
40
  // source/memory-store.ts
41
41
  var MemoryStore = class {
42
- constructor() {
42
+ constructor(validations2) {
43
+ this.validations = validations2;
43
44
  /**
44
45
  * These two maps store usage (requests) and reset time by key (for example, IP
45
46
  * addresses or API keys).
@@ -65,6 +66,7 @@ var MemoryStore = class {
65
66
  */
66
67
  init(options) {
67
68
  this.windowMs = options.windowMs;
69
+ this.validations?.windowMs(this.windowMs);
68
70
  if (this.interval) clearInterval(this.interval);
69
71
  this.interval = setInterval(() => {
70
72
  this.clearExpired();
@@ -237,7 +239,7 @@ var setDraft6Headers = (response, info, windowMs) => {
237
239
  response.setHeader("RateLimit-Policy", `${info.limit};w=${windowSeconds}`);
238
240
  response.setHeader("RateLimit-Limit", info.limit.toString());
239
241
  response.setHeader("RateLimit-Remaining", info.remaining.toString());
240
- if (resetSeconds)
242
+ if (typeof resetSeconds === "number")
241
243
  response.setHeader("RateLimit-Reset", resetSeconds.toString());
242
244
  };
243
245
  var setDraft7Headers = (response, info, windowMs) => {
@@ -367,6 +369,21 @@ var validations = {
367
369
  );
368
370
  }
369
371
  },
372
+ /**
373
+ * Alert the user if the Forwarded header is set (standardized version of X-Forwarded-For - not supported by express as of version 5.1.0)
374
+ *
375
+ * @param request {Request} - The Express request object.
376
+ *
377
+ * @returns {void}
378
+ */
379
+ forwardedHeader(request) {
380
+ if (request.headers.forwarded && request.ip === request.socket?.remoteAddress) {
381
+ throw new ValidationError(
382
+ "ERR_ERL_FORWARDED_HEADER",
383
+ `The 'Forwarded' header (standardized X-Forwarded-For) is set but currently being ignored. Add a custom keyGenerator to use a value from this header.`
384
+ );
385
+ }
386
+ },
370
387
  /**
371
388
  * Ensures totalHits value from store is a positive integer.
372
389
  *
@@ -580,6 +597,21 @@ var validations = {
580
597
  "Custom keyGenerator appears to use request IP without calling the ipKeyGenerator helper function for IPv6 addresses. This could allow IPv6 users to bypass limits."
581
598
  );
582
599
  }
600
+ },
601
+ /**
602
+ * Checks to see if the window duration is greater than 2^32 - 1. This is only
603
+ * called by the default MemoryStore, since it uses Node's setInterval method.
604
+ *
605
+ * See https://nodejs.org/api/timers.html#setintervalcallback-delay-args.
606
+ */
607
+ windowMs(windowMs) {
608
+ const SET_TIMEOUT_MAX = 2 ** 31 - 1;
609
+ if (typeof windowMs !== "number" || Number.isNaN(windowMs) || windowMs < 1 || windowMs > SET_TIMEOUT_MAX) {
610
+ throw new ValidationError(
611
+ "ERR_ERL_WINDOW_MS",
612
+ `Invalid windowMs value: ${windowMs}${typeof windowMs !== "number" ? ` (${typeof windowMs})` : ""}, must be a number between 1 and ${SET_TIMEOUT_MAX} when using the default MemoryStore`
613
+ );
614
+ }
583
615
  }
584
616
  };
585
617
  var getValidations = (_enabled) => {
@@ -706,6 +738,7 @@ var parseOptions = (passedOptions) => {
706
738
  validations2.ip(request.ip);
707
739
  validations2.trustProxy(request);
708
740
  validations2.xForwardedForHeader(request);
741
+ validations2.forwardedHeader(request);
709
742
  const ip = request.ip;
710
743
  let subnet = 56;
711
744
  if ((0, import_node_net3.isIPv6)(ip)) {
@@ -731,7 +764,9 @@ var parseOptions = (passedOptions) => {
731
764
  standardHeaders,
732
765
  // Note that this field is declared after the user's options are spread in,
733
766
  // so that this field doesn't get overridden with an un-promisified store!
734
- store: promisifyStore(notUndefinedOptions.store ?? new MemoryStore()),
767
+ store: promisifyStore(
768
+ notUndefinedOptions.store ?? new MemoryStore(validations2)
769
+ ),
735
770
  // Print an error to the console if a few known misconfigurations are detected.
736
771
  validations: validations2
737
772
  };
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- // Generated by dts-bundle-generator v8.0.1
1
+ // Generated by dts-bundle-generator v8.1.2
2
2
 
3
3
  import { NextFunction, Request, RequestHandler, Response } from 'express';
4
4
 
@@ -60,6 +60,14 @@ declare const validations: {
60
60
  * @returns {void}
61
61
  */
62
62
  xForwardedForHeader(request: Request): void;
63
+ /**
64
+ * Alert the user if the Forwarded header is set (standardized version of X-Forwarded-For - not supported by express as of version 5.1.0)
65
+ *
66
+ * @param request {Request} - The Express request object.
67
+ *
68
+ * @returns {void}
69
+ */
70
+ forwardedHeader(request: Request): void;
63
71
  /**
64
72
  * Ensures totalHits value from store is a positive integer.
65
73
  *
@@ -141,6 +149,13 @@ declare const validations: {
141
149
  ipv6Subnet(ipv6Subnet?: any): void;
142
150
  ipv6SubnetOrKeyGenerator(options: Partial<Options>): void;
143
151
  keyGeneratorIpFallback(keyGenerator?: ValueDeterminingMiddleware<string>): void;
152
+ /**
153
+ * Checks to see if the window duration is greater than 2^32 - 1. This is only
154
+ * called by the default MemoryStore, since it uses Node's setInterval method.
155
+ *
156
+ * See https://nodejs.org/api/timers.html#setintervalcallback-delay-args.
157
+ */
158
+ windowMs(windowMs: number): void;
144
159
  };
145
160
  export type Validations = typeof validations;
146
161
  /**
@@ -496,6 +511,7 @@ export type Client = {
496
511
  * @public
497
512
  */
498
513
  export declare class MemoryStore implements Store {
514
+ private validations?;
499
515
  /**
500
516
  * The duration of time before which all hit counts are reset (in milliseconds).
501
517
  */
@@ -521,6 +537,7 @@ export declare class MemoryStore implements Store {
521
537
  * cannot affect other instances.
522
538
  */
523
539
  localKeys: boolean;
540
+ constructor(validations?: Validations | undefined);
524
541
  /**
525
542
  * Method that initializes the store.
526
543
  *
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- // Generated by dts-bundle-generator v8.0.1
1
+ // Generated by dts-bundle-generator v8.1.2
2
2
 
3
3
  import { NextFunction, Request, RequestHandler, Response } from 'express';
4
4
 
@@ -60,6 +60,14 @@ declare const validations: {
60
60
  * @returns {void}
61
61
  */
62
62
  xForwardedForHeader(request: Request): void;
63
+ /**
64
+ * Alert the user if the Forwarded header is set (standardized version of X-Forwarded-For - not supported by express as of version 5.1.0)
65
+ *
66
+ * @param request {Request} - The Express request object.
67
+ *
68
+ * @returns {void}
69
+ */
70
+ forwardedHeader(request: Request): void;
63
71
  /**
64
72
  * Ensures totalHits value from store is a positive integer.
65
73
  *
@@ -141,6 +149,13 @@ declare const validations: {
141
149
  ipv6Subnet(ipv6Subnet?: any): void;
142
150
  ipv6SubnetOrKeyGenerator(options: Partial<Options>): void;
143
151
  keyGeneratorIpFallback(keyGenerator?: ValueDeterminingMiddleware<string>): void;
152
+ /**
153
+ * Checks to see if the window duration is greater than 2^32 - 1. This is only
154
+ * called by the default MemoryStore, since it uses Node's setInterval method.
155
+ *
156
+ * See https://nodejs.org/api/timers.html#setintervalcallback-delay-args.
157
+ */
158
+ windowMs(windowMs: number): void;
144
159
  };
145
160
  export type Validations = typeof validations;
146
161
  /**
@@ -496,6 +511,7 @@ export type Client = {
496
511
  * @public
497
512
  */
498
513
  export declare class MemoryStore implements Store {
514
+ private validations?;
499
515
  /**
500
516
  * The duration of time before which all hit counts are reset (in milliseconds).
501
517
  */
@@ -521,6 +537,7 @@ export declare class MemoryStore implements Store {
521
537
  * cannot affect other instances.
522
538
  */
523
539
  localKeys: boolean;
540
+ constructor(validations?: Validations | undefined);
524
541
  /**
525
542
  * Method that initializes the store.
526
543
  *
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- // Generated by dts-bundle-generator v8.0.1
1
+ // Generated by dts-bundle-generator v8.1.2
2
2
 
3
3
  import { NextFunction, Request, RequestHandler, Response } from 'express';
4
4
 
@@ -60,6 +60,14 @@ declare const validations: {
60
60
  * @returns {void}
61
61
  */
62
62
  xForwardedForHeader(request: Request): void;
63
+ /**
64
+ * Alert the user if the Forwarded header is set (standardized version of X-Forwarded-For - not supported by express as of version 5.1.0)
65
+ *
66
+ * @param request {Request} - The Express request object.
67
+ *
68
+ * @returns {void}
69
+ */
70
+ forwardedHeader(request: Request): void;
63
71
  /**
64
72
  * Ensures totalHits value from store is a positive integer.
65
73
  *
@@ -141,6 +149,13 @@ declare const validations: {
141
149
  ipv6Subnet(ipv6Subnet?: any): void;
142
150
  ipv6SubnetOrKeyGenerator(options: Partial<Options>): void;
143
151
  keyGeneratorIpFallback(keyGenerator?: ValueDeterminingMiddleware<string>): void;
152
+ /**
153
+ * Checks to see if the window duration is greater than 2^32 - 1. This is only
154
+ * called by the default MemoryStore, since it uses Node's setInterval method.
155
+ *
156
+ * See https://nodejs.org/api/timers.html#setintervalcallback-delay-args.
157
+ */
158
+ windowMs(windowMs: number): void;
144
159
  };
145
160
  export type Validations = typeof validations;
146
161
  /**
@@ -496,6 +511,7 @@ export type Client = {
496
511
  * @public
497
512
  */
498
513
  export declare class MemoryStore implements Store {
514
+ private validations?;
499
515
  /**
500
516
  * The duration of time before which all hit counts are reset (in milliseconds).
501
517
  */
@@ -521,6 +537,7 @@ export declare class MemoryStore implements Store {
521
537
  * cannot affect other instances.
522
538
  */
523
539
  localKeys: boolean;
540
+ constructor(validations?: Validations | undefined);
524
541
  /**
525
542
  * Method that initializes the store.
526
543
  *
package/dist/index.mjs CHANGED
@@ -10,7 +10,8 @@ function ipKeyGenerator(ip, ipv6Subnet = 56) {
10
10
 
11
11
  // source/memory-store.ts
12
12
  var MemoryStore = class {
13
- constructor() {
13
+ constructor(validations2) {
14
+ this.validations = validations2;
14
15
  /**
15
16
  * These two maps store usage (requests) and reset time by key (for example, IP
16
17
  * addresses or API keys).
@@ -36,6 +37,7 @@ var MemoryStore = class {
36
37
  */
37
38
  init(options) {
38
39
  this.windowMs = options.windowMs;
40
+ this.validations?.windowMs(this.windowMs);
39
41
  if (this.interval) clearInterval(this.interval);
40
42
  this.interval = setInterval(() => {
41
43
  this.clearExpired();
@@ -208,7 +210,7 @@ var setDraft6Headers = (response, info, windowMs) => {
208
210
  response.setHeader("RateLimit-Policy", `${info.limit};w=${windowSeconds}`);
209
211
  response.setHeader("RateLimit-Limit", info.limit.toString());
210
212
  response.setHeader("RateLimit-Remaining", info.remaining.toString());
211
- if (resetSeconds)
213
+ if (typeof resetSeconds === "number")
212
214
  response.setHeader("RateLimit-Reset", resetSeconds.toString());
213
215
  };
214
216
  var setDraft7Headers = (response, info, windowMs) => {
@@ -338,6 +340,21 @@ var validations = {
338
340
  );
339
341
  }
340
342
  },
343
+ /**
344
+ * Alert the user if the Forwarded header is set (standardized version of X-Forwarded-For - not supported by express as of version 5.1.0)
345
+ *
346
+ * @param request {Request} - The Express request object.
347
+ *
348
+ * @returns {void}
349
+ */
350
+ forwardedHeader(request) {
351
+ if (request.headers.forwarded && request.ip === request.socket?.remoteAddress) {
352
+ throw new ValidationError(
353
+ "ERR_ERL_FORWARDED_HEADER",
354
+ `The 'Forwarded' header (standardized X-Forwarded-For) is set but currently being ignored. Add a custom keyGenerator to use a value from this header.`
355
+ );
356
+ }
357
+ },
341
358
  /**
342
359
  * Ensures totalHits value from store is a positive integer.
343
360
  *
@@ -551,6 +568,21 @@ var validations = {
551
568
  "Custom keyGenerator appears to use request IP without calling the ipKeyGenerator helper function for IPv6 addresses. This could allow IPv6 users to bypass limits."
552
569
  );
553
570
  }
571
+ },
572
+ /**
573
+ * Checks to see if the window duration is greater than 2^32 - 1. This is only
574
+ * called by the default MemoryStore, since it uses Node's setInterval method.
575
+ *
576
+ * See https://nodejs.org/api/timers.html#setintervalcallback-delay-args.
577
+ */
578
+ windowMs(windowMs) {
579
+ const SET_TIMEOUT_MAX = 2 ** 31 - 1;
580
+ if (typeof windowMs !== "number" || Number.isNaN(windowMs) || windowMs < 1 || windowMs > SET_TIMEOUT_MAX) {
581
+ throw new ValidationError(
582
+ "ERR_ERL_WINDOW_MS",
583
+ `Invalid windowMs value: ${windowMs}${typeof windowMs !== "number" ? ` (${typeof windowMs})` : ""}, must be a number between 1 and ${SET_TIMEOUT_MAX} when using the default MemoryStore`
584
+ );
585
+ }
554
586
  }
555
587
  };
556
588
  var getValidations = (_enabled) => {
@@ -677,6 +709,7 @@ var parseOptions = (passedOptions) => {
677
709
  validations2.ip(request.ip);
678
710
  validations2.trustProxy(request);
679
711
  validations2.xForwardedForHeader(request);
712
+ validations2.forwardedHeader(request);
680
713
  const ip = request.ip;
681
714
  let subnet = 56;
682
715
  if (isIPv62(ip)) {
@@ -702,7 +735,9 @@ var parseOptions = (passedOptions) => {
702
735
  standardHeaders,
703
736
  // Note that this field is declared after the user's options are spread in,
704
737
  // so that this field doesn't get overridden with an un-promisified store!
705
- store: promisifyStore(notUndefinedOptions.store ?? new MemoryStore()),
738
+ store: promisifyStore(
739
+ notUndefinedOptions.store ?? new MemoryStore(validations2)
740
+ ),
706
741
  // Print an error to the console if a few known misconfigurations are detected.
707
742
  validations: validations2
708
743
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "express-rate-limit",
3
- "version": "8.0.1",
3
+ "version": "8.1.0",
4
4
  "description": "Basic IP rate-limiting middleware for Express. Use to limit repeated requests to public APIs and/or endpoints such as password reset.",
5
5
  "author": {
6
6
  "name": "Nathan Friedly",
@@ -77,29 +77,29 @@
77
77
  "express": ">= 4.11"
78
78
  },
79
79
  "devDependencies": {
80
- "@biomejs/biome": "2.1.1",
80
+ "@biomejs/biome": "2.2.2",
81
81
  "@express-rate-limit/prettier": "1.1.1",
82
82
  "@express-rate-limit/tsconfig": "1.0.2",
83
- "@jest/globals": "30.0.4",
83
+ "@jest/globals": "30.1.2",
84
84
  "@types/express": "5.0.3",
85
85
  "@types/jest": "30.0.0",
86
- "@types/node": "24.0.14",
86
+ "@types/node": "24.3.0",
87
87
  "@types/supertest": "6.0.3",
88
88
  "del-cli": "6.0.0",
89
- "dts-bundle-generator": "8.0.1",
90
- "esbuild": "0.25.6",
89
+ "dts-bundle-generator": "8.1.2",
90
+ "esbuild": "0.25.9",
91
91
  "express": "5.1.0",
92
92
  "husky": "9.1.7",
93
- "jest": "30.0.4",
94
- "lint-staged": "16.1.2",
95
- "mintlify": "4.2.15",
93
+ "jest": "30.1.2",
94
+ "lint-staged": "16.1.6",
95
+ "mintlify": "4.2.94",
96
96
  "npm-run-all": "4.1.5",
97
97
  "prettier": "3.6.2",
98
98
  "ratelimit-header-parser": "0.1.0",
99
- "supertest": "7.1.3",
100
- "ts-jest": "29.4.0",
99
+ "supertest": "7.1.4",
100
+ "ts-jest": "29.4.1",
101
101
  "ts-node": "10.9.2",
102
- "typescript": "5.8.3"
102
+ "typescript": "5.9.2"
103
103
  },
104
104
  "prettier": "@express-rate-limit/prettier",
105
105
  "lint-staged": {