express-rate-limit 8.0.2 → 8.1.1

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
@@ -44,7 +44,8 @@ function ipKeyGenerator(ip, ipv6Subnet = 56) {
44
44
 
45
45
  // source/memory-store.ts
46
46
  var MemoryStore = class {
47
- constructor() {
47
+ constructor(validations2) {
48
+ this.validations = validations2;
48
49
  /**
49
50
  * These two maps store usage (requests) and reset time by key (for example, IP
50
51
  * addresses or API keys).
@@ -70,6 +71,7 @@ var MemoryStore = class {
70
71
  */
71
72
  init(options) {
72
73
  this.windowMs = options.windowMs;
74
+ this.validations?.windowMs(this.windowMs);
73
75
  if (this.interval) clearInterval(this.interval);
74
76
  this.interval = setInterval(() => {
75
77
  this.clearExpired();
@@ -242,7 +244,7 @@ var setDraft6Headers = (response, info, windowMs) => {
242
244
  response.setHeader("RateLimit-Policy", `${info.limit};w=${windowSeconds}`);
243
245
  response.setHeader("RateLimit-Limit", info.limit.toString());
244
246
  response.setHeader("RateLimit-Remaining", info.remaining.toString());
245
- if (resetSeconds)
247
+ if (typeof resetSeconds === "number")
246
248
  response.setHeader("RateLimit-Reset", resetSeconds.toString());
247
249
  };
248
250
  var setDraft7Headers = (response, info, windowMs) => {
@@ -372,6 +374,21 @@ var validations = {
372
374
  );
373
375
  }
374
376
  },
377
+ /**
378
+ * 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)
379
+ *
380
+ * @param request {Request} - The Express request object.
381
+ *
382
+ * @returns {void}
383
+ */
384
+ forwardedHeader(request) {
385
+ if (request.headers.forwarded && request.ip === request.socket?.remoteAddress) {
386
+ throw new ValidationError(
387
+ "ERR_ERL_FORWARDED_HEADER",
388
+ `The 'Forwarded' header (standardized X-Forwarded-For) is set but currently being ignored. Add a custom keyGenerator to use a value from this header.`
389
+ );
390
+ }
391
+ },
375
392
  /**
376
393
  * Ensures totalHits value from store is a positive integer.
377
394
  *
@@ -585,6 +602,21 @@ var validations = {
585
602
  "Custom keyGenerator appears to use request IP without calling the ipKeyGenerator helper function for IPv6 addresses. This could allow IPv6 users to bypass limits."
586
603
  );
587
604
  }
605
+ },
606
+ /**
607
+ * Checks to see if the window duration is greater than 2^32 - 1. This is only
608
+ * called by the default MemoryStore, since it uses Node's setInterval method.
609
+ *
610
+ * See https://nodejs.org/api/timers.html#setintervalcallback-delay-args.
611
+ */
612
+ windowMs(windowMs) {
613
+ const SET_TIMEOUT_MAX = 2 ** 31 - 1;
614
+ if (typeof windowMs !== "number" || Number.isNaN(windowMs) || windowMs < 1 || windowMs > SET_TIMEOUT_MAX) {
615
+ throw new ValidationError(
616
+ "ERR_ERL_WINDOW_MS",
617
+ `Invalid windowMs value: ${windowMs}${typeof windowMs !== "number" ? ` (${typeof windowMs})` : ""}, must be a number between 1 and ${SET_TIMEOUT_MAX} when using the default MemoryStore`
618
+ );
619
+ }
588
620
  }
589
621
  };
590
622
  var getValidations = (_enabled) => {
@@ -711,6 +743,7 @@ var parseOptions = (passedOptions) => {
711
743
  validations2.ip(request.ip);
712
744
  validations2.trustProxy(request);
713
745
  validations2.xForwardedForHeader(request);
746
+ validations2.forwardedHeader(request);
714
747
  const ip = request.ip;
715
748
  let subnet = 56;
716
749
  if ((0, import_node_net3.isIPv6)(ip)) {
@@ -736,7 +769,9 @@ var parseOptions = (passedOptions) => {
736
769
  standardHeaders,
737
770
  // Note that this field is declared after the user's options are spread in,
738
771
  // so that this field doesn't get overridden with an un-promisified store!
739
- store: promisifyStore(notUndefinedOptions.store ?? new MemoryStore()),
772
+ store: promisifyStore(
773
+ notUndefinedOptions.store ?? new MemoryStore(validations2)
774
+ ),
740
775
  // Print an error to the console if a few known misconfigurations are detected.
741
776
  validations: validations2
742
777
  };
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
@@ -15,7 +15,8 @@ function ipKeyGenerator(ip, ipv6Subnet = 56) {
15
15
 
16
16
  // source/memory-store.ts
17
17
  var MemoryStore = class {
18
- constructor() {
18
+ constructor(validations2) {
19
+ this.validations = validations2;
19
20
  /**
20
21
  * These two maps store usage (requests) and reset time by key (for example, IP
21
22
  * addresses or API keys).
@@ -41,6 +42,7 @@ var MemoryStore = class {
41
42
  */
42
43
  init(options) {
43
44
  this.windowMs = options.windowMs;
45
+ this.validations?.windowMs(this.windowMs);
44
46
  if (this.interval) clearInterval(this.interval);
45
47
  this.interval = setInterval(() => {
46
48
  this.clearExpired();
@@ -213,7 +215,7 @@ var setDraft6Headers = (response, info, windowMs) => {
213
215
  response.setHeader("RateLimit-Policy", `${info.limit};w=${windowSeconds}`);
214
216
  response.setHeader("RateLimit-Limit", info.limit.toString());
215
217
  response.setHeader("RateLimit-Remaining", info.remaining.toString());
216
- if (resetSeconds)
218
+ if (typeof resetSeconds === "number")
217
219
  response.setHeader("RateLimit-Reset", resetSeconds.toString());
218
220
  };
219
221
  var setDraft7Headers = (response, info, windowMs) => {
@@ -343,6 +345,21 @@ var validations = {
343
345
  );
344
346
  }
345
347
  },
348
+ /**
349
+ * 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)
350
+ *
351
+ * @param request {Request} - The Express request object.
352
+ *
353
+ * @returns {void}
354
+ */
355
+ forwardedHeader(request) {
356
+ if (request.headers.forwarded && request.ip === request.socket?.remoteAddress) {
357
+ throw new ValidationError(
358
+ "ERR_ERL_FORWARDED_HEADER",
359
+ `The 'Forwarded' header (standardized X-Forwarded-For) is set but currently being ignored. Add a custom keyGenerator to use a value from this header.`
360
+ );
361
+ }
362
+ },
346
363
  /**
347
364
  * Ensures totalHits value from store is a positive integer.
348
365
  *
@@ -556,6 +573,21 @@ var validations = {
556
573
  "Custom keyGenerator appears to use request IP without calling the ipKeyGenerator helper function for IPv6 addresses. This could allow IPv6 users to bypass limits."
557
574
  );
558
575
  }
576
+ },
577
+ /**
578
+ * Checks to see if the window duration is greater than 2^32 - 1. This is only
579
+ * called by the default MemoryStore, since it uses Node's setInterval method.
580
+ *
581
+ * See https://nodejs.org/api/timers.html#setintervalcallback-delay-args.
582
+ */
583
+ windowMs(windowMs) {
584
+ const SET_TIMEOUT_MAX = 2 ** 31 - 1;
585
+ if (typeof windowMs !== "number" || Number.isNaN(windowMs) || windowMs < 1 || windowMs > SET_TIMEOUT_MAX) {
586
+ throw new ValidationError(
587
+ "ERR_ERL_WINDOW_MS",
588
+ `Invalid windowMs value: ${windowMs}${typeof windowMs !== "number" ? ` (${typeof windowMs})` : ""}, must be a number between 1 and ${SET_TIMEOUT_MAX} when using the default MemoryStore`
589
+ );
590
+ }
559
591
  }
560
592
  };
561
593
  var getValidations = (_enabled) => {
@@ -682,6 +714,7 @@ var parseOptions = (passedOptions) => {
682
714
  validations2.ip(request.ip);
683
715
  validations2.trustProxy(request);
684
716
  validations2.xForwardedForHeader(request);
717
+ validations2.forwardedHeader(request);
685
718
  const ip = request.ip;
686
719
  let subnet = 56;
687
720
  if (isIPv62(ip)) {
@@ -707,7 +740,9 @@ var parseOptions = (passedOptions) => {
707
740
  standardHeaders,
708
741
  // Note that this field is declared after the user's options are spread in,
709
742
  // so that this field doesn't get overridden with an un-promisified store!
710
- store: promisifyStore(notUndefinedOptions.store ?? new MemoryStore()),
743
+ store: promisifyStore(
744
+ notUndefinedOptions.store ?? new MemoryStore(validations2)
745
+ ),
711
746
  // Print an error to the console if a few known misconfigurations are detected.
712
747
  validations: validations2
713
748
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "express-rate-limit",
3
- "version": "8.0.2",
3
+ "version": "8.1.1",
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",
@@ -61,29 +61,29 @@
61
61
  "express": ">= 4.11"
62
62
  },
63
63
  "devDependencies": {
64
- "@biomejs/biome": "2.1.1",
64
+ "@biomejs/biome": "2.2.2",
65
65
  "@express-rate-limit/prettier": "1.1.1",
66
66
  "@express-rate-limit/tsconfig": "1.0.2",
67
- "@jest/globals": "30.0.4",
67
+ "@jest/globals": "30.1.2",
68
68
  "@types/express": "5.0.3",
69
69
  "@types/jest": "30.0.0",
70
- "@types/node": "24.0.14",
70
+ "@types/node": "24.3.0",
71
71
  "@types/supertest": "6.0.3",
72
72
  "del-cli": "6.0.0",
73
- "dts-bundle-generator": "8.0.1",
74
- "esbuild": "0.25.6",
73
+ "dts-bundle-generator": "8.1.2",
74
+ "esbuild": "0.25.9",
75
75
  "express": "5.1.0",
76
76
  "husky": "9.1.7",
77
- "jest": "30.0.4",
78
- "lint-staged": "16.1.2",
79
- "mintlify": "4.2.15",
77
+ "jest": "30.1.2",
78
+ "lint-staged": "16.1.6",
79
+ "mintlify": "4.2.94",
80
80
  "npm-run-all": "4.1.5",
81
81
  "prettier": "3.6.2",
82
82
  "ratelimit-header-parser": "0.1.0",
83
- "supertest": "7.1.3",
84
- "ts-jest": "29.4.0",
83
+ "supertest": "7.1.4",
84
+ "ts-jest": "29.4.1",
85
85
  "ts-node": "10.9.2",
86
- "typescript": "5.8.3"
86
+ "typescript": "5.9.2"
87
87
  },
88
88
  "prettier": "@express-rate-limit/prettier",
89
89
  "lint-staged": {