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 +38 -3
- package/dist/index.d.cts +18 -1
- package/dist/index.d.mts +18 -1
- package/dist/index.d.ts +18 -1
- package/dist/index.mjs +38 -3
- package/package.json +12 -12
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(
|
|
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.
|
|
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.
|
|
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.
|
|
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(
|
|
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
|
|
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.
|
|
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.
|
|
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
|
|
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.
|
|
90
|
-
"esbuild": "0.25.
|
|
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.
|
|
94
|
-
"lint-staged": "16.1.
|
|
95
|
-
"mintlify": "4.2.
|
|
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.
|
|
100
|
-
"ts-jest": "29.4.
|
|
99
|
+
"supertest": "7.1.4",
|
|
100
|
+
"ts-jest": "29.4.1",
|
|
101
101
|
"ts-node": "10.9.2",
|
|
102
|
-
"typescript": "5.
|
|
102
|
+
"typescript": "5.9.2"
|
|
103
103
|
},
|
|
104
104
|
"prettier": "@express-rate-limit/prettier",
|
|
105
105
|
"lint-staged": {
|